#!/usr/bin/env python3
"""Проверка ручного эталона и границ перестановки, отдельно от генератора."""
import copy
import hashlib
import itertools
import json
import subprocess
import sys
import tempfile
from pathlib import Path
from example import run

ROOT = Path(__file__).resolve().parent
fixture = json.loads((ROOT / 'input.json').read_text())
expected = json.loads((ROOT / 'expected.json').read_text())
checks = []

def check(name, value, scope):
    checks.append({'name': name, 'status': 'pass' if value else 'fail', 'scope': scope})

def rejected(name, altered):
    try:
        run(altered)
    except ValueError:
        check(name, True, 'Отрицательный контроль: вход отклонён')
    else:
        check(name, False, 'Неверный вход был принят')

base = run(fixture)
check('six_distinct_fictional_skus', len(fixture['catalog']) == len({x['sku'] for x in fixture['catalog']}) == 6, 'Фиксированный учебный каталог')
for stage in ['retrieval', 'eligible', 'reranked', 'final']:
    check('manual_expected_' + stage, base[stage] == expected[stage], 'Независимо записанный ручной эталон')
check('filter_removes_F_not_X', 'F' in base['retrieval'] and 'F' not in base['eligible'] and 'X' not in base['retrieval'], 'Различаются две причины отсутствия')
permutations_ok = []
for free in itertools.permutations(['A', 'B', 'C']):
    data = copy.deepcopy(fixture); data['teaching_order'] = list(free) + ['P']
    out = run(data)
    # Не используем метод insert генератора: второй слот зарезервирован вручную.
    manual = [free[0], 'P', free[1], free[2]]
    permutations_ok.append(out['final'] == manual and set(out['reranked']) == {'A', 'B', 'C', 'P'} and not set(out['final']) & {'F', 'X'})
check('all_six_free_permutations', len(permutations_ok) == 6 and all(permutations_ok), 'Все 3! перестановок трёх свободных SKU, P остаётся вторым, F/X не появляются')
hide = copy.deepcopy(fixture); hide['rules']['hide'] = ['C']; hidden = run(hide)
check('hide_C_manual_result', hidden['final'] == expected['hide_C_final'], 'Отдельное правило hide после учебной перестановки')
check('hide_does_not_change_reranker_input', hidden['eligible'] == base['eligible'] and hidden['reranked'] == base['reranked'], 'Этапы скрытия и перестановки не смешаны')
for sku in ['F', 'X']:
    bad = copy.deepcopy(fixture); bad['teaching_order'][-1] = sku; rejected('reranker_cannot_inject_' + sku, bad)
bad = copy.deepcopy(fixture); bad['teaching_order'][-1] = 'A'; rejected('duplicate_order_rejected', bad)
bad = copy.deepcopy(fixture); bad['teaching_order'].pop(); rejected('missing_order_item_rejected', bad)
bad = copy.deepcopy(fixture); bad['rules']['pin']['sku'] = 'X'; rejected('pin_outside_candidates_rejected_in_this_model', bad)
bad = copy.deepcopy(fixture); bad['rules']['hide'] = ['P']; rejected('pin_hide_conflict_rejected_not_vendor_claim', bad)
bad = copy.deepcopy(fixture); bad['rules']['pin']['position_1_based'] = 8; rejected('pin_position_rejected', bad)
original = copy.deepcopy(fixture); run(fixture)
check('fixture_not_mutated', fixture == original, 'Независимый повтор с тем же входом')
with tempfile.TemporaryDirectory() as temp:
    temp = Path(temp)
    # Переносим только вход и генератор, но не expected.json.
    for filename in ['input.json', 'example.py']:
        (temp / filename).write_bytes((ROOT / filename).read_bytes())
    proc = subprocess.run([sys.executable, str(temp / 'example.py'), '--out', str(temp / 'out')], capture_output=True, text=True)
    repeated = json.loads((temp / 'out/result.json').read_text()) if proc.returncode == 0 else None
    check('portable_cli_without_expected_file', repeated == base, 'Реальный запуск в отдельной папке без эталона')
report = {'scope': 'Учебная детерминированная модель; не тест Algolia/Elastic/ML, не допуск публикации', 'checks': checks, 'passed': sum(x['status'] == 'pass' for x in checks), 'total': len(checks), 'source_sha256': {name: hashlib.sha256((ROOT / name).read_bytes()).hexdigest() for name in ['input.json', 'expected.json', 'example.py', 'verify.py']}}
(ROOT / 'verification.json').write_text(json.dumps(report, ensure_ascii=False, indent=2) + '\n')
print(json.dumps({'passed': report['passed'], 'total': report['total']}, ensure_ascii=False))
raise SystemExit(0 if report['passed'] == report['total'] else 1)
