#!/usr/bin/env python3
"""Static inventory of SwiftUI screens in iosApp/iosApp/.

Finds every `struct <Name>View: View { ... }` or `struct <Name>Screen: View { ... }`
declaration across the production iOS source tree (excluding generated/build
dirs) and categorises them by path.

Output schema:
{
  "screens": [
    {"name": "LoginView", "path": "iosApp/iosApp/Login/LoginView.swift", "category": "auth"},
    ...
  ]
}
"""
from __future__ import annotations

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

REPO_ROOT = Path(__file__).resolve().parent.parent
SOURCE_ROOT = REPO_ROOT / "iosApp" / "iosApp"
OUT_PATH = REPO_ROOT / "docs" / "ios-parity" / "screens.json"

# Also scan widget target (HoneyDue/) for completeness.
WIDGET_SOURCE_ROOT = REPO_ROOT / "iosApp" / "HoneyDue"

EXCLUDED_DIR_PARTS = {"build", "DerivedData", ".build", "Pods"}

STRUCT_RE = re.compile(
    r"^\s*struct\s+([A-Za-z_][A-Za-z0-9_]*(?:View|Screen))\s*:\s*View\s*\{",
    re.MULTILINE,
)

CATEGORY_RULES: list[tuple[str, str]] = [
    # path-part substring (case-insensitive)  -> category
    ("Login", "auth"),
    ("Register", "auth"),
    ("PasswordReset", "auth"),
    ("VerifyEmail", "auth"),
    ("Onboarding", "onboarding"),
    ("Task", "task"),
    ("Residence", "residence"),
    ("Document", "document"),
    ("Contractor", "contractor"),
    ("Profile", "profile"),
    ("Subscription", "subscription"),
    ("Widget", "widget"),  # matches WidgetIconView etc. (HoneyDue/)
    ("HoneyDue", "widget"),  # widget target dir
    ("Shared", "shared"),
    ("Core", "shared"),
    ("Subviews", "shared"),
    ("Dev", "dev"),
    ("AnimationTesting", "dev"),
]


def category_for(rel_path: Path) -> str:
    parts_lower = [p.lower() for p in rel_path.parts]
    for needle, cat in CATEGORY_RULES:
        if needle.lower() in parts_lower:
            return cat
    # filename fallback
    stem = rel_path.stem.lower()
    for needle, cat in CATEGORY_RULES:
        if needle.lower() in stem:
            return cat
    return "shared"


def should_skip(path: Path) -> bool:
    return any(part in EXCLUDED_DIR_PARTS for part in path.parts)


def find_swift_files(root: Path) -> list[Path]:
    if not root.is_dir():
        return []
    out: list[Path] = []
    for p in root.rglob("*.swift"):
        if should_skip(p.relative_to(REPO_ROOT)):
            continue
        out.append(p)
    return sorted(out)


def extract_from(path: Path) -> list[dict[str, Any]]:
    try:
        text = path.read_text(encoding="utf-8")
    except (UnicodeDecodeError, OSError):
        return []
    found: list[dict[str, Any]] = []
    seen: set[str] = set()
    for m in STRUCT_RE.finditer(text):
        name = m.group(1)
        if name in seen:
            continue
        seen.add(name)
        rel = path.relative_to(REPO_ROOT)
        found.append(
            {
                "name": name,
                "path": str(rel),
                "category": category_for(rel),
            }
        )
    return found


def main() -> int:
    screens: list[dict[str, Any]] = []
    for swift_file in find_swift_files(SOURCE_ROOT):
        screens.extend(extract_from(swift_file))
    for swift_file in find_swift_files(WIDGET_SOURCE_ROOT):
        screens.extend(extract_from(swift_file))

    # Sort by category then name for stable output.
    screens.sort(key=lambda s: (s["category"], s["name"], s["path"]))

    output = {"screens": screens}
    OUT_PATH.parent.mkdir(parents=True, exist_ok=True)
    with OUT_PATH.open("w", encoding="utf-8") as f:
        json.dump(output, f, indent=2)
        f.write("\n")

    print(f"[extract_ios_screens] wrote {OUT_PATH}")
    print(f"  screens={len(screens)}")
    # category histogram
    hist: dict[str, int] = {}
    for s in screens:
        hist[s["category"]] = hist.get(s["category"], 0) + 1
    for cat, n in sorted(hist.items(), key=lambda kv: (-kv[1], kv[0])):
        print(f"    {cat}: {n}")
    return 0


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