"""Verify lesson examples and save actual evidence; never modify CSV inputs."""

import hashlib
import importlib.util
import json
import os
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path

LAB = Path(__file__).resolve().parent
ROOT = LAB.parents[2]
ORIGINAL = LAB.parent / 'CC-01_asset_naming'


def load(name):
    spec = importlib.util.spec_from_file_location(name, LAB / 'debug' / (name + '.py'))
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module.extension_allowed


def hashes():
    paths = [ORIGINAL / 'asset_check.py', *sorted((ORIGINAL / 'fixtures').glob('*.csv'))]
    return {str(path.relative_to(ROOT)): hashlib.sha256(path.read_bytes()).hexdigest() for path in paths}


def main():
    before = hashes()
    buggy, fixed = load('extension_buggy'), load('extension_fixed')
    known = [
        ('Texture', 'T_Rock.png', True), ('Texture', 'T_Rock.PNG', False),
        ('Texture', 'T_HDR.exr', True), ('Texture', 'T_HDR.EXR', False),
        ('StaticMesh', 'SM_Rock.fbx', True), ('StaticMesh', 'SM_Rock.FBX', False),
        ('Texture', 'T_Rock.png.bak', False), ('Texture', 'T_Rock', False),
        ('Material', 'M_Rock.mat', False),
    ]
    regression = [{'asset_type': kind, 'name': name, 'expected': expected,
                   'buggy': buggy(kind, name), 'fixed': fixed(kind, name)}
                  for kind, name, expected in known]
    buggy_failures = [item['name'] for item in regression if item['buggy'] != item['expected']]
    fixed_passed = all(item['fixed'] == item['expected'] for item in regression)
    mutation_detected = buggy_failures == ['T_Rock.PNG', 'T_HDR.EXR', 'SM_Rock.FBX']

    cli = []
    # Oracles are written from the published lesson rules, not derived from the checker.
    fixtures = [('mixed', 1, 6, 3), ('valid', 0, 3, 0), ('edges', 1, 6, 4),
                ('uppercase', 1, 1, 1), ('empty', 2, None, None),
                ('bad_header', 2, None, None), ('does_not_exist', 2, None, None)]
    for name, expected_exit, rows, invalid in fixtures:
        command = [sys.executable, 'asset_check.py', f'fixtures/assets_{name}.csv', '--json']
        run = subprocess.run(command, cwd=ORIGINAL, text=True, encoding='utf-8', capture_output=True, timeout=10)
        passed = run.returncode == expected_exit
        if rows is not None:
            try:
                report = json.loads(run.stdout)
                passed &= (report['rows'] == rows and report['invalid'] == invalid
                           and report['valid'] == rows - invalid
                           and len(report['problems']) == invalid and not run.stderr)
            except (ValueError, KeyError, TypeError):
                passed = False
        else:
            passed &= not run.stdout and run.stderr.startswith('INPUT_ERROR:')
        cli.append({'case': name, 'command': command, 'expected_exit': expected_exit,
                    'actual_exit': run.returncode, 'stdout': run.stdout, 'stderr': run.stderr,
                    'passed': bool(passed)})

    settings = json.loads((LAB / '.claude' / 'settings.json').read_text(encoding='utf-8'))
    mcp = json.loads((LAB / '.mcp.example.json').read_text(encoding='utf-8'))
    configured = settings['hooks']['PreToolUse'][0]['hooks'][0]['command']
    events = [
        ('normal_relative', 'Write', 'docs/report.md', LAB, 0, True),
        ('normal_absolute', 'Edit', str(LAB / 'docs' / '资产规则.md'), LAB, 0, True),
        ('protected_relative', 'Write', 'protected/规则快照.md', LAB, 2, True),
        ('protected_absolute', 'Edit', str(LAB / 'protected' / '规则快照.md'), LAB, 2, True),
        ('traversal', 'Write', 'docs/../protected/new.md', LAB, 2, True),
        ('outside_project', 'Write', str(ROOT / 'outside-lab.md'), LAB, 2, True),
        ('similar_prefix', 'Write', 'protected-neighbor/report.md', LAB, 0, True),
        ('nested_cwd', 'Write', '../protected/规则快照.md', LAB / 'docs', 2, True),
        ('missing_path', 'Edit', '', LAB, 2, True),
        ('malformed_json', 'Write', None, LAB, 2, True),
        ('missing_root', 'Write', 'docs/report.md', LAB, 2, False),
        ('unmatched_read_tool', 'Read', 'protected/规则快照.md', LAB, 0, True),
    ]
    hooks = []
    for name, tool, target, cwd, expected_exit, has_root in events:
        env = os.environ.copy()
        env['PYTHONIOENCODING'] = 'utf-8'
        env['CLAUDE_PROJECT_DIR'] = str(LAB)
        if not has_root:
            env.pop('CLAUDE_PROJECT_DIR', None)
        payload = {'hook_event_name': 'PreToolUse', 'tool_name': tool,
                   'tool_input': {'file_path': target}, 'cwd': str(cwd)}
        stdin = '{invalid' if name == 'malformed_json' else json.dumps(payload, ensure_ascii=False)
        # Execute the literal configured command, from another directory, to check cwd independence.
        run = subprocess.run(configured, shell=True, cwd=ROOT, env=env, input=stdin,
                             text=True, encoding='utf-8', capture_output=True, timeout=10)
        passed = run.returncode == expected_exit
        if expected_exit == 2:
            passed &= run.stderr.startswith('WRITE_DENIED:')
        hooks.append({'case': name, 'command': configured, 'process_cwd': str(ROOT),
                      'event_cwd': str(cwd), 'expected_exit': expected_exit,
                      'actual_exit': run.returncode, 'stdout': run.stdout, 'stderr': run.stderr,
                      'passed': bool(passed)})

    after = hashes()
    unchanged = before == after
    passed = fixed_passed and mutation_detected and unchanged and all(item['passed'] for item in cli + hooks)
    result = {'checked_at': datetime.now(timezone.utc).isoformat(), 'python': sys.version,
              'scope': 'Python examples and configured command only; no Claude model, MCP or engine session',
              'regression': regression, 'buggy_failures': buggy_failures, 'fixed_passed': fixed_passed,
              'mutation_detected': mutation_detected, 'cli': cli, 'hooks': hooks,
              'config_json_parsed': bool(settings and mcp), 'original_inputs_unchanged': unchanged,
              'input_hashes_before': before, 'input_hashes_after': after, 'passed': passed}
    output = ROOT / '检查记录' / 'CC-02-04_示例检查.json'
    output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
    print(json.dumps({'passed': passed, 'fixed_cases': len(regression), 'buggy_failures': len(buggy_failures),
                      'cli_cases': len(cli), 'hook_cases': len(hooks), 'unchanged': unchanged,
                      'evidence': str(output.relative_to(ROOT))}, ensure_ascii=False))
    return 0 if passed else 1


if __name__ == '__main__':
    sys.exit(main())
