"""Run the unchanged upstream tiny-model tests in a fresh, isolated checkout copy.

Requires an already prepared Linux/Python 3.9 environment. This script does not
provision a server, install dependencies, change model parameters, or spend money.
"""
import argparse
import datetime
import hashlib
import importlib.metadata
import json
import pathlib
import platform
import shutil
import subprocess
import sys
import time
import uuid
import xml.etree.ElementTree as ET

COMMIT = 'f5c09a0eccc5786daf78881d883bbce586a00e6d'
TARGET = 'tests/full_model/test_models_stepcurrentmodule.py::TestLSV1MTinyStepCurrentModule'
POPULATIONS = ['X_ON', 'X_OFF', 'V1_Exc_L4', 'V1_Inh_L4', 'V1_Exc_L2/3', 'V1_Inh_L2/3']
EXPECTED = {f'test_spikes[{p}]' for p in POPULATIONS} | {f'test_voltages[{p}]' for p in POPULATIONS[2:]}


def classify_report(xml_path, exit_code):
    """Refuse an exact-pass label for missing, skipped, partial, or failed tests."""
    answer = {'status':'not_passed', 'pytest_exit_code':exit_code, 'expected_cases':10}
    if not pathlib.Path(xml_path).is_file():
        return dict(answer, reason='JUnit report missing')
    try:
        root = ET.parse(xml_path).getroot()
    except ET.ParseError:
        return dict(answer, reason='JUnit report malformed')
    cases = root.findall('.//testcase')
    names = [case.get('name') for case in cases]
    bad = [case.get('name') for case in cases if any(case.find(tag) is not None for tag in ['failure','error','skipped'])]
    right_classes = all((case.get('classname') or '').endswith('.TestLSV1MTinyStepCurrentModule') for case in cases)
    answer.update(observed_cases=len(cases), names=names, failed_errored_or_skipped=bad)
    if exit_code == 0 and len(cases) == 10 and set(names) == EXPECTED and right_classes and not bad:
        answer.update(status='upstream_exact_regression_pass', reason='All ten expected upstream comparisons passed')
    else:
        answer['reason'] = 'Exit status, test identities, count, or individual outcomes did not satisfy the unchanged upstream target'
    answer['claim_boundary'] = 'Software regression only; not full-paper reproduction, independent biological validation, or controller evidence'
    return answer


def git(checkout, *args):
    return subprocess.check_output(['git','-C',str(checkout),*args], text=True).strip()


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--checkout', required=True, type=pathlib.Path)
    parser.add_argument('--output', required=True, type=pathlib.Path)
    args = parser.parse_args()
    checkout, output = args.checkout.resolve(), args.output.resolve()
    if output == checkout or checkout in output.parents:
        parser.error('--output must be outside the source checkout')
    run_dir = output / (datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ') + '-' + uuid.uuid4().hex[:8])
    run_dir.mkdir(parents=True, exist_ok=False)
    record = {'status':'preflight', 'model_simulation_started':False, 'framework_commit':COMMIT,
              'python':sys.version, 'os':platform.system(), 'architecture':platform.machine(),
              'target':TARGET, 'packages':sorted((d.metadata['Name'],d.version) for d in importlib.metadata.distributions() if d.metadata['Name'])}
    try:
        if platform.system() != 'Linux' or platform.machine() not in ['x86_64','AMD64']:
            raise RuntimeError('This candidate baseline requires Linux x86_64')
        if sys.version_info[:2] != (3,9):
            raise RuntimeError('This candidate baseline requires Python 3.9, matching the upstream workflow')
        if git(checkout,'rev-parse','HEAD') != COMMIT:
            raise RuntimeError('Framework checkout does not match the pinned commit')
        if git(checkout,'status','--porcelain'):
            raise RuntimeError('Use a clean, dedicated framework checkout')
        smoke_code = 'import nest; assert nest.__version__ == "3.4", nest.__version__; nest.Install("stepcurrentmodule"); print("NEST 3.4 extension import passed")'
        smoke = subprocess.run([sys.executable,'-c',smoke_code], stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
        (run_dir/'simulator-import.log').write_text(smoke.stdout)
        if smoke.returncode:
            raise RuntimeError('NEST 3.4 / stepcurrentmodule import check failed; see simulator-import.log')
        work = run_dir/'framework'
        shutil.copytree(checkout,work,ignore=shutil.ignore_patterns('.git','__pycache__','.pytest_cache'))
        inputs = [p for p in work.rglob('*') if p.is_file()]
        inventory = [{'path':str(p.relative_to(work)), 'sha256':hashlib.sha256(p.read_bytes()).hexdigest()} for p in sorted(inputs)]
        (run_dir/'input-files.json').write_text(json.dumps(inventory,indent=2)+'\n')
        command = [sys.executable,'-m','pytest',TARGET,'-v','--durations=10','--junitxml='+str(run_dir/'pytest.xml')]
        if pathlib.Path('/usr/bin/time').is_file():
            command = ['/usr/bin/time','-v','-o',str(run_dir/'resources.txt')] + command
        record.update(status='tests_started', command=command, model_simulation_started='not independently confirmed; inspect the model log')
        (run_dir/'run.json').write_text(json.dumps(record,indent=2)+'\n')
        start = time.monotonic()
        with (run_dir/'pytest.log').open('w') as log:
            completed = subprocess.run(command,cwd=work,stdout=log,stderr=subprocess.STDOUT)
        record.update(classify_report(run_dir/'pytest.xml',completed.returncode))
        record['elapsed_wall_seconds'] = time.monotonic()-start
        record['raw_results_relative_path'] = 'framework/tests/full_model/models/LSV1M_tiny_stepcurrentmodule/LSV1M_pytest_____'
    except Exception as error:
        record.update(status='preflight_or_harness_error', error_type=type(error).__name__, error=str(error))
    (run_dir/'run.json').write_text(json.dumps(record,indent=2)+'\n')
    print(json.dumps({'status':record['status'],'run_directory':str(run_dir)}))
    return 0 if record['status']=='upstream_exact_regression_pass' else 1


if __name__ == '__main__':
    sys.exit(main())
