#!/usr/bin/env python3
"""Prepare the reviewed Laravel patch in an isolated local copy; never deploy."""
import argparse
import hashlib
import json
from pathlib import Path
import shutil
import subprocess
import tarfile
import tempfile

ROOT = Path(__file__).resolve().parents[1]


def prepare(source, snapshot, output):
    source, snapshot, output = source.resolve(), snapshot.resolve(), output.resolve()
    if output.exists():
        raise ValueError('Output must be a new isolated directory; existing work will not be overwritten')
    if source == output or source in output.parents:
        raise ValueError('Output must be outside the original Laravel checkout')
    manifest = json.loads((ROOT / 'integrations/laravel/source-manifest.json').read_text())
    for name, hashes in manifest.items():
        file = snapshot / name
        if not file.is_file() or hashlib.sha256(file.read_bytes()).hexdigest() != hashes['baseline_sha256']:
            raise ValueError(f'Inspected baseline differs: {name}; reconcile source before applying patch')
    output.mkdir(parents=True, mode=0o700)
    try:
        with tempfile.TemporaryFile() as archive:
            subprocess.run(['git', '-C', str(source), 'archive', '--format=tar', 'HEAD'], stdout=archive, check=True)
            archive.seek(0)
            with tarfile.open(fileobj=archive) as stream:
                stream.extractall(output, filter='data')
        for name, hashes in manifest.items():
            destination = output / name
            destination.parent.mkdir(parents=True, exist_ok=True)
            shutil.copyfile(snapshot / name, destination)
        patch = ROOT / 'integrations/laravel/mobile-readiness.patch'
        subprocess.run(['git', 'apply', '--unidiff-zero', '--check', str(patch)], cwd=output, check=True)
        subprocess.run(['git', 'apply', '--unidiff-zero', str(patch)], cwd=output, check=True)
        for name, hashes in manifest.items():
            if hashlib.sha256((output / name).read_bytes()).hexdigest() != hashes['candidate_sha256']:
                raise ValueError(f'Candidate checksum mismatch: {name}')
        for folder in ['app/Services', 'tests/Feature']:
            for file in (ROOT / 'integrations/laravel' / folder).glob('*.php'):
                destination = output / folder / file.name
                destination.parent.mkdir(parents=True, exist_ok=True)
                shutil.copyfile(file, destination)
        # Never use a vendor symlink into the source checkout; the Docker test command mounts it read-only.
        (output / 'vendor').mkdir(exist_ok=True)
        print(f'Isolated Laravel candidate prepared: {output}; original checkout unchanged')
    except BaseException:
        # Keep the failed candidate reviewable; do not erase source or user work.
        print(f'Preparation stopped; inspect retained candidate: {output}')
        raise


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--source', type=Path, required=True)
    parser.add_argument('--snapshot', type=Path, default=ROOT / '.deploy/laravel-source')
    parser.add_argument('--output', type=Path, required=True)
    args = parser.parse_args()
    prepare(args.source, args.snapshot, args.output)
