#!/usr/bin/env python3
"""Check manual evidence files for a 1C extension validation plan."""

from __future__ import annotations

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


PENDING_MARKERS = ("status: pending", '"status": "pending"')
PASSED_STATUSES = {"passed", "success", "ok"}
STATUS_LINE_RE = re.compile(r"^\s*status\s*:\s*([A-Za-zА-Яа-я0-9_-]+)\s*$", re.IGNORECASE | re.MULTILINE)
SECRET_TEXT_RE = re.compile(r"(password|passwd|pwd|secret|token|парол|секрет|usr|user)\s*[:=]", re.IGNORECASE)


def load_json(path: Path) -> dict[str, Any]:
    return json.loads(path.read_text(encoding="utf-8-sig"))


def write_json(path: Path, data: dict[str, Any]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")


def issue(severity: str, code: str, message: str, *, path: Path | str | None = None, detail: dict[str, Any] | None = None) -> dict[str, Any]:
    result: dict[str, Any] = {"severity": severity, "code": code, "message": message}
    if path is not None:
        result["path"] = str(path)
    if detail:
        result["detail"] = detail
    return result


def evidence_root(plan: dict[str, Any], override: Path | None = None) -> Path:
    if override:
        return override
    configured = ((plan.get("runner_config") or {}).get("evidence_root")) or ((plan.get("evidence") or {}).get("root"))
    if not configured:
        raise SystemExit("Validation plan has no evidence root.")
    return Path(str(configured))


def text_status(text: str) -> str | None:
    match = STATUS_LINE_RE.search(text)
    return match.group(1).casefold() if match else None


def check_json_evidence(path: Path, text: str, findings: list[dict[str, Any]]) -> tuple[bool, str | None]:
    try:
        payload = json.loads(text)
    except json.JSONDecodeError as exc:
        findings.append(issue("error", "invalid_json_evidence", f"Evidence JSON is invalid: {exc}", path=path))
        return False, None
    status = str(payload.get("status") or "").casefold()
    if status not in PASSED_STATUSES:
        findings.append(issue("error", "evidence_status_not_passed", "Evidence JSON status must be passed/success/ok.", path=path, detail={"status": status or None}))
        return False, status or None
    if path.name == "changed-objects-smoke.json":
        objects = payload.get("objects")
        if not isinstance(objects, list) or not objects:
            findings.append(issue("error", "missing_smoke_objects", "changed-objects-smoke.json must contain a non-empty objects list.", path=path))
            return False, status
        failed = [
            {"object_name": item.get("object_name"), "status": item.get("status")}
            for item in objects
            if not isinstance(item, dict) or str(item.get("status") or "").casefold() not in PASSED_STATUSES
        ]
        if failed:
            findings.append(issue("error", "smoke_object_status_not_passed", "Every changed object smoke record must have status passed/success/ok.", path=path, detail={"failed": failed}))
            return False, status
    return True, status


def check_text_evidence(path: Path, text: str, findings: list[dict[str, Any]]) -> tuple[bool, str | None]:
    status = text_status(text)
    if status not in PASSED_STATUSES:
        findings.append(issue("error", "evidence_status_not_passed", "Evidence text must contain a Status: passed/success/ok line.", path=path, detail={"status": status}))
        return False, status
    return True, status


def check_evidence(plan_path: Path, output_root: Path | None = None) -> dict[str, Any]:
    findings: list[dict[str, Any]] = []
    file_checks: list[dict[str, Any]] = []
    plan = load_json(plan_path)
    if plan.get("schema") != "onec_extension_validation_plan.v1":
        findings.append(issue("error", "invalid_plan_schema", "Validation plan schema is not onec_extension_validation_plan.v1.", path=plan_path, detail={"schema": plan.get("schema")}))
        return build_result(plan_path, Path("."), findings, file_checks)

    root = evidence_root(plan, output_root)
    if not root.exists() or not root.is_dir():
        findings.append(issue("error", "missing_evidence_root", "Evidence root is missing.", path=root))
        return build_result(plan_path, root, findings, file_checks)

    for name in (plan.get("evidence") or {}).get("required_files") or []:
        relative = Path(str(name).replace("\\", "/"))
        path = root / relative
        check: dict[str, Any] = {
            "relative_path": str(relative).replace("\\", "/"),
            "path": str(path),
            "exists": path.exists(),
            "filled": False,
        }
        if not path.exists():
            findings.append(issue("error", "missing_evidence_file", "Required evidence file is missing.", path=path))
        else:
            text = path.read_text(encoding="utf-8-sig", errors="replace")
            stripped = text.strip()
            check["size"] = len(text.encode("utf-8"))
            if SECRET_TEXT_RE.search(text):
                findings.append(issue("error", "secret_like_text_in_evidence", "Evidence file contains secret-like key/value text.", path=path))
            pending = any(marker in text.casefold() for marker in PENDING_MARKERS)
            if pending:
                findings.append(issue("error", "pending_evidence_file", "Evidence file still contains a pending template.", path=path))
            if not stripped:
                findings.append(issue("error", "empty_evidence_file", "Evidence file is empty.", path=path))
            elif path.suffix.casefold() == ".json":
                passed, status = check_json_evidence(path, text, findings)
                check["status"] = status
                check["filled"] = passed and not pending
            else:
                passed, status = check_text_evidence(path, text, findings)
                check["status"] = status
                check["filled"] = passed and not pending
        file_checks.append(check)

    manifest_path = root / "_codex_validation_evidence_manifest.json"
    if not manifest_path.exists():
        findings.append(issue("warning", "missing_evidence_manifest", "Evidence manifest is missing.", path=manifest_path))

    return build_result(plan_path, root, findings, file_checks)


def build_result(plan_path: Path, root: Path, findings: list[dict[str, Any]], file_checks: list[dict[str, Any]]) -> dict[str, Any]:
    errors = [row for row in findings if row.get("severity") == "error"]
    warnings = [row for row in findings if row.get("severity") == "warning"]
    return {
        "schema": "onec_extension_validation_evidence_check.v1",
        "plan_path": str(plan_path),
        "evidence_root": str(root),
        "passed": not errors,
        "status": "validated" if not errors else "pending_or_blocked",
        "findings": findings,
        "file_checks": file_checks,
        "counts": {
            "files": len(file_checks),
            "filled": sum(1 for item in file_checks if item.get("filled")),
            "errors": len(errors),
            "warnings": len(warnings),
        },
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="Check manual evidence files for a 1C extension validation plan.")
    parser.add_argument("--plan", type=Path, required=True)
    parser.add_argument("--evidence-root", type=Path)
    parser.add_argument("--output", type=Path)
    args = parser.parse_args()

    result = check_evidence(args.plan, args.evidence_root)
    if args.output:
        write_json(args.output, result)
    print(json.dumps({"output": str(args.output) if args.output else None, "passed": result["passed"], "status": result["status"], "counts": result["counts"]}, ensure_ascii=False))
    return 0 if result["passed"] else 2


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