from __future__ import annotations

import argparse
import json
from pathlib import Path
from typing import Any


ROOT = Path(__file__).resolve().parents[1]
DEFAULT_MANIFEST = ROOT / "reports" / "llm-artifact-manifest.json"
DEFAULT_OUTPUT = ROOT / "reports" / "llm-artifact-check.json"


def load_json(path: Path) -> dict[str, Any]:
    data = json.loads(path.read_text(encoding="utf-8-sig"))
    if not isinstance(data, dict):
        raise ValueError(f"{path} must contain a JSON object")
    return data


def iter_files(path: Path) -> list[Path]:
    if not path.exists():
        return []
    return sorted(item for item in path.rglob("*") if item.is_file())


def resolve_record_path(record_path: str, *, source_root: Path, target_root: Path | None) -> Path:
    path = Path(record_path)
    if target_root is None:
        return path
    try:
        relative = path.relative_to(source_root)
        return target_root / relative
    except ValueError:
        return target_root / path.name


def check_artifact(record: dict[str, Any], *, source_root: Path, target_root: Path | None, strict_counts: bool) -> list[dict[str, Any]]:
    findings: list[dict[str, Any]] = []
    path = resolve_record_path(str(record.get("path") or ""), source_root=source_root, target_root=target_root)
    expected_exists = bool(record.get("exists"))
    if expected_exists and not path.exists():
        return [{"severity": "error", "code": "artifact_missing", "artifact": record.get("name"), "path": str(path)}]
    if not path.exists():
        return []
    if not path.is_dir():
        findings.append({"severity": "error", "code": "artifact_not_directory", "artifact": record.get("name"), "path": str(path)})
        return findings

    files = iter_files(path)
    size = sum(item.stat().st_size for item in files)
    expected_count = int(record.get("file_count") or 0)
    expected_size = int(record.get("total_size_bytes") or 0)
    if strict_counts and len(files) != expected_count:
        findings.append(
            {
                "severity": "error",
                "code": "file_count_mismatch",
                "artifact": record.get("name"),
                "path": str(path),
                "expected": expected_count,
                "actual": len(files),
            }
        )
    elif len(files) < expected_count:
        findings.append(
            {
                "severity": "warning",
                "code": "file_count_decreased",
                "artifact": record.get("name"),
                "path": str(path),
                "expected_at_least": expected_count,
                "actual": len(files),
            }
        )
    if strict_counts and size != expected_size:
        findings.append(
            {
                "severity": "error",
                "code": "total_size_mismatch",
                "artifact": record.get("name"),
                "path": str(path),
                "expected": expected_size,
                "actual": size,
            }
        )
    elif size < expected_size:
        findings.append(
            {
                "severity": "warning",
                "code": "total_size_decreased",
                "artifact": record.get("name"),
                "path": str(path),
                "expected_at_least": expected_size,
                "actual": size,
            }
        )
    return findings


def check_manifest(manifest: dict[str, Any], *, target_root: Path | None, strict_counts: bool) -> dict[str, Any]:
    source_root = Path(str(manifest.get("workspace_root") or ROOT))
    findings: list[dict[str, Any]] = []
    for record in manifest.get("artifacts") or []:
        findings.extend(check_artifact(record, source_root=source_root, target_root=target_root, strict_counts=strict_counts))

    errors = [item for item in findings if item.get("severity") == "error"]
    warnings = [item for item in findings if item.get("severity") == "warning"]
    return {
        "schema": "llm_artifact_manifest_check.v1",
        "manifest_schema": manifest.get("schema"),
        "manifest_created_at": manifest.get("created_at"),
        "target_root": str(target_root) if target_root else None,
        "strict_counts": strict_counts,
        "passed": not errors,
        "counts": {
            "artifacts": len(manifest.get("artifacts") or []),
            "errors": len(errors),
            "warnings": len(warnings),
            "findings": len(findings),
        },
        "findings": findings,
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="Check that local LLM/RAG artifacts from a manifest are present after transfer or before Docker launch.")
    parser.add_argument("--manifest", type=Path, default=DEFAULT_MANIFEST)
    parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT)
    parser.add_argument("--target-root", type=Path, help="New workspace root after transfer. If omitted, paths are checked as recorded.")
    parser.add_argument("--strict-counts", action="store_true", help="Fail when file counts or total sizes differ exactly.")
    args = parser.parse_args()

    result = check_manifest(load_json(args.manifest), target_root=args.target_root, strict_counts=args.strict_counts)
    args.output.parent.mkdir(parents=True, exist_ok=True)
    args.output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    print(json.dumps({"output": str(args.output), "passed": result["passed"], "counts": result["counts"]}, ensure_ascii=False))
    return 0 if result["passed"] else 2


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