from __future__ import annotations

import argparse
from datetime import datetime
import hashlib
import json
from pathlib import Path
import zipfile


ROOT = Path(__file__).resolve().parent
INCLUDE_ROOT_FILES = {
    "README.md", "README_DEPLOY.md", "requirements.txt", "package_release.py",
    "run_daily_update.py", "run_daily_update.cmd", "run_dashboard.py", "run_m1.py",
    "run_m2.py", "run_m2_research.py", "refresh_external_data.py", "serve_dashboard.py",
}
INCLUDE_DIRS = ("config", "data/input", "data/processed", "prompts", "reports", "scripts", "src")


def should_skip(path: Path) -> bool:
    parts = {part.lower() for part in path.parts}
    return "__pycache__" in parts or ".pytest_cache" in parts or path.suffix.lower() in {".pyc", ".pyo"} or "logs" in parts


def iter_files(include_vendor: bool):
    for name in sorted(INCLUDE_ROOT_FILES):
        path = ROOT / name
        if path.is_file():
            yield path
    for directory in INCLUDE_DIRS + (("vendor",) if include_vendor else ()):
        base = ROOT / directory
        if not base.exists():
            continue
        for path in sorted(base.rglob("*")):
            relative = path.relative_to(ROOT)
            if path.is_file() and not should_skip(relative):
                if include_vendor and relative.parts[0] == "vendor" and "tests" in {p.lower() for p in relative.parts}:
                    continue
                yield path


def main() -> Path:
    parser = argparse.ArgumentParser(description="Build the portable research workbench release archive")
    parser.add_argument("--without-vendor", action="store_true", help="Exclude bundled Windows Python 3.12 dependencies")
    args = parser.parse_args()
    include_vendor = not args.without_vendor
    dist = ROOT / "dist"
    dist.mkdir(exist_ok=True)
    stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    output = dist / f"fundamental-research-workbench_{stamp}.zip"
    files = list(iter_files(include_vendor))
    manifest = []
    with zipfile.ZipFile(output, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=6, allowZip64=True) as archive:
        for index, path in enumerate(files, 1):
            relative = path.relative_to(ROOT).as_posix()
            archive.write(path, relative)
            digest = hashlib.sha256(path.read_bytes()).hexdigest()
            manifest.append({"path": relative, "size": path.stat().st_size, "sha256": digest})
            if index % 250 == 0:
                print(f"Packed {index}/{len(files)} files")
        archive.writestr("release_manifest.json", json.dumps({
            "createdAt": datetime.now().astimezone().isoformat(timespec="seconds"),
            "pythonTarget": "CPython 3.12 x64 on Windows" if include_vendor else "Install requirements.txt",
            "fileCount": len(manifest),
            "files": manifest,
        }, ensure_ascii=False, indent=2))
    print(f"Release: {output}")
    print(f"Size: {output.stat().st_size / 1024 / 1024:.1f} MB")
    return output


if __name__ == "__main__":
    main()
