from __future__ import annotations

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


ROOT = Path(__file__).resolve().parents[1]
DEFAULT_STATIC_DIR = ROOT / "plugins" / "1c" / "rag" / "official-docs" / "static"
DEFAULT_OUTPUT = ROOT / "reports" / "1c-official-docs-static-check.json"


LOCAL_REF_RE = re.compile(r"""(?:href|src)=["']([^"']+)["']""", re.IGNORECASE)
EXTERNAL_IMG_RE = re.compile(r"""<img[^>]+src=["']https?://""", re.IGNORECASE)
EXTERNAL_ASSET_RE = re.compile(r"""<(?:link|script)[^>]+(?:href|src)=["']https?://""", re.IGNORECASE)
EXTERNAL_LINK_RE = re.compile(r"""<a[^>]+href=["']https?://""", re.IGNORECASE)


def html_files(static_dir: Path) -> list[Path]:
    if not static_dir.exists():
        return []
    return sorted(static_dir.rglob("*.html"))


def is_local_ref(value: str) -> bool:
    lowered = value.casefold()
    return not (
        lowered.startswith("http://")
        or lowered.startswith("https://")
        or lowered.startswith("mailto:")
        or lowered.startswith("javascript:")
        or lowered.startswith("#")
    )


def check_local_refs(path: Path, text: str) -> list[dict[str, str]]:
    broken = []
    for match in LOCAL_REF_RE.finditer(text):
        ref = match.group(1)
        if not is_local_ref(ref):
            continue
        target = (path.parent / ref.split("#", 1)[0].split("?", 1)[0]).resolve()
        if not target.exists():
            broken.append({"file": str(path), "ref": ref})
    return broken


def build_report(static_dir: Path) -> dict[str, Any]:
    files = html_files(static_dir)
    external_images = []
    external_assets = []
    external_links = []
    broken_refs = []
    for path in files:
        text = path.read_text(encoding="utf-8-sig", errors="replace")
        if EXTERNAL_IMG_RE.search(text):
            external_images.append(str(path))
        if EXTERNAL_ASSET_RE.search(text):
            external_assets.append(str(path))
        external_links.extend({"file": str(path), "count": len(EXTERNAL_LINK_RE.findall(text))} for _ in [0] if EXTERNAL_LINK_RE.search(text))
        broken_refs.extend(check_local_refs(path, text))

    manifest_path = static_dir / "manifest.json"
    manifest = {}
    if manifest_path.exists():
        manifest = json.loads(manifest_path.read_text(encoding="utf-8-sig"))
    counts = {
        "html_files": len(files),
        "pages": len((manifest.get("pages") or [])) if isinstance(manifest, dict) else 0,
        "media_files": len(list((static_dir / "media").glob("*"))) if (static_dir / "media").exists() else 0,
        "asset_files": len(list((static_dir / "assets").glob("*"))) if (static_dir / "assets").exists() else 0,
        "external_image_pages": len(external_images),
        "external_asset_pages": len(external_assets),
        "external_link_pages": len(external_links),
        "broken_local_refs": len(broken_refs),
        "asset_errors": len((manifest.get("asset_errors") or [])) if isinstance(manifest, dict) else 0,
    }
    findings = []
    if not (static_dir / "index.html").exists():
        findings.append({"severity": "error", "message": "static index.html is missing"})
    if counts["external_image_pages"]:
        findings.append({"severity": "error", "message": "some static pages still reference remote images"})
    if counts["external_asset_pages"]:
        findings.append({"severity": "warning", "message": "some raw pages still reference remote CSS/JS assets"})
    if counts["broken_local_refs"]:
        findings.append({"severity": "error", "message": "some local href/src references are broken"})
    if counts["asset_errors"]:
        findings.append({"severity": "warning", "message": "some CSS/JS assets failed to download"})

    return {
        "schema": "onec_its_static_site_check.v1",
        "passed": not any(item["severity"] == "error" for item in findings),
        "static_dir": str(static_dir),
        "counts": counts,
        "findings": findings,
        "samples": {
            "external_images": external_images[:20],
            "external_assets": external_assets[:20],
            "external_links": external_links[:20],
            "broken_local_refs": broken_refs[:20],
            "asset_errors": (manifest.get("asset_errors") or [])[:20] if isinstance(manifest, dict) else [],
        },
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="Check local static 1C:ITS archive self-containment and links.")
    parser.add_argument("--static-dir", type=Path, default=DEFAULT_STATIC_DIR)
    parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT)
    parser.add_argument("--print", action="store_true", dest="print_report")
    args = parser.parse_args()

    report = build_report(args.static_dir)
    args.output.parent.mkdir(parents=True, exist_ok=True)
    args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    if args.print_report:
        print(json.dumps(report, ensure_ascii=False, indent=2))
    return 0 if report["passed"] else 1


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