#!/usr/bin/env python3
"""Automatisierte Zielabnahme mit Artefaktbindung; reale Emulatornachweise bleiben separat."""
import argparse
import datetime
import hashlib
import json
import os
from pathlib import Path
import platform
import subprocess
import sys

ROOT = Path(__file__).resolve().parents[2]
TARGETS = {
    ('Windows', 'AMD64'): 'x86_64-pc-windows-msvc',
    ('Darwin', 'arm64'): 'aarch64-apple-darwin',
    ('Linux', 'x86_64'): 'x86_64-unknown-linux-gnu',
    ('Linux', 'aarch64'): 'aarch64-unknown-linux-gnu',
}
TBL_SHA256 = '5d25c2f8c95ae535e55a6c84c1ddd0d964358863f4f3c780c50ac4a2eb3594df'


def digest(path):
    return hashlib.sha256(path.read_bytes()).hexdigest()


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--bin-dir', type=Path, default=ROOT / 'target/release')
    parser.add_argument('--output', type=Path, required=True, help='neues Verzeichnis für Bericht und Logs')
    parser.add_argument('--windows-console-test', type=Path, help='tb-ui-Testexecutable derselben Revision')
    args = parser.parse_args()
    target = TARGETS.get((platform.system(), platform.machine()))
    if target is None:
        parser.error(f'Kein vereinbartes Releaseziel: {platform.system()} {platform.machine()}')
    binaries = args.bin_dir.resolve()
    suffix = '.exe' if os.name == 'nt' else ''
    programs = {name: binaries / (name + suffix) for name in ('tb', 'tbc', 'tbrt', 'tb-template')}
    for path in programs.values():
        if not path.is_file():
            parser.error(f'Prüfwerkzeug fehlt: {path}')
    library = ROOT / 'tests/libraries/portable.tbl'
    if digest(library) != TBL_SHA256:
        parser.error('Gemeinsame portable.tbl hat eine abweichende Prüfsumme')
    revision = subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=ROOT, text=True).strip()
    changes = subprocess.check_output(['git', 'diff', '--binary', 'HEAD'], cwd=ROOT)
    status = subprocess.check_output(['git', 'status', '--porcelain'], cwd=ROOT, text=True)
    args.output.mkdir(parents=True)  # vorhandene Nachweise nie überschreiben
    report = {
        'scope': 'automated-target-checks',
        'started_utc': datetime.datetime.now(datetime.timezone.utc).isoformat(),
        'host_target': target, 'os_version': platform.platform(),
        'python_version': platform.python_version(), 'checkout_revision': revision,
        'working_tree_dirty': bool(status),
        'working_tree_diff_sha256': hashlib.sha256(changes).hexdigest(),
        'program_sha256': {name: digest(path) for name, path in programs.items()},
        'tbl_sha256': TBL_SHA256,
        'manual_terminal_status': 'not_run', 'matrix_complete': False, 'checks': {},
    }
    (args.output / 'working-tree.patch').write_bytes(changes)
    (args.output / 'working-tree-status.txt').write_text(status, encoding='utf-8')
    # Neue Prüfscripte können noch untracked sein; ihre genauen Bytes festhalten.
    report['harness_sha256'] = {p.name: digest(p) for p in Path(__file__).parent.glob('*abnahme.py')}
    report['harness_sha256']['ide-execution-pty.py'] = digest(Path(__file__).with_name('ide-execution-pty.py'))
    commands = {
        'library': [sys.executable, str(Path(__file__).with_name('library-abnahme.py')), '--bin-dir', str(binaries)],
        'native': [sys.executable, str(Path(__file__).with_name('native-abnahme.py')), '--bin-dir', str(binaries), '--target', target],
    }
    if os.name == 'posix':
        commands['ide-pty'] = [sys.executable, str(Path(__file__).with_name('ide-execution-pty.py')),
                               '--binary', str(programs['tb']), '--transcript-dir', str(args.output.resolve() / 'pty')]
    elif args.windows_console_test:
        executable = args.windows_console_test.resolve(strict=True)
        report['windows_console_test_sha256'] = digest(executable)
        commands['windows-console'] = [str(executable), '--exact',
            'terminal::tests::windows_console_restores_modes', '--ignored', '--nocapture']
    else:
        report['checks']['windows-console'] = {'status': 'missing', 'reason': '--windows-console-test fehlt'}
    report['checks'].update({name: {'status': 'not_run'} for name in commands})
    try:
        for name, command in commands.items():
            print('Prüfung:', name, flush=True)
            with (args.output / (name + '.log')).open('wb') as log:
                try:
                    result = subprocess.run(command, cwd=ROOT, stdout=log, stderr=subprocess.STDOUT, timeout=300)
                    report['checks'][name] = {'status': 'passed' if result.returncode == 0 else 'failed',
                                              'exit_code': result.returncode}
                except OSError as error:
                    report['checks'][name] = {'status': 'failed', 'reason': str(error)}
                except subprocess.TimeoutExpired:
                    report['checks'][name] = {'status': 'failed', 'reason': 'Zeitlimit 300s'}
    finally:
        stable = all(digest(path) == report['program_sha256'][name] for name, path in programs.items()) and digest(library) == TBL_SHA256
        report['checks']['artifact-stability'] = {'status': 'passed' if stable else 'failed'}
        report['all_automated_passed'] = bool(report['checks']) and all(
            check['status'] == 'passed' for check in report['checks'].values())
        (args.output / 'report.json').write_text(json.dumps(report, indent=2, ensure_ascii=False) + '\n', encoding='utf-8')
    print('Automatisierte Zielprüfung:', 'bestanden' if report['all_automated_passed'] else 'offen/fehlgeschlagen')
    print('Reale Terminalmatrix: weiterhin separat nachzuweisen')
    return 0 if report['all_automated_passed'] else 1


if __name__ == '__main__':
    raise SystemExit(main())
