#!/usr/bin/env python3.12
import argparse
import datetime
import fcntl
import json
import pathlib
import subprocess
import sys
from typing import List, Tuple

# Add root directory to path for imports
_root = pathlib.Path(__file__).parent.parent.parent
if str(_root) not in sys.path:
    sys.path.insert(0, str(_root))

from tools.design import history


def parse_sites(raw: str) -> List[Tuple[str, str, str, str]]:
    sites: List[Tuple[str, str, str, str]] = []
    for item in raw.split(","):
        parts = item.split(":")
        if len(parts) < 2:
            continue
        domain = parts[0]
        title = parts[1]
        desc = parts[2] if len(parts) > 2 else ""
        theme = parts[3] if len(parts) > 3 else "default"
        sites.append((domain, title, desc, theme))
    return sites


def read_sites_from_registry(registry_path: pathlib.Path, statuses: List[str]) -> List[Tuple[str, str, str, str]]:
    """Read sites from registry with given status codes."""
    if not registry_path.exists():
        return []
    sites: List[Tuple[str, str, str, str]] = []
    lines = registry_path.read_text().splitlines()
    for line in lines:
        if line.startswith("|") and "Domain" not in line and "--------" not in line:
            cols = [c.strip() for c in line.strip("|").split("|")]
            if len(cols) >= 6 and cols[3] in statuses:
                domain = cols[0]
                title = cols[1]
                desc = cols[2]
                theme = "default"  # Registry doesn't store theme, use default
                sites.append((domain, title, desc, theme))
    return sites


def format_sites_string(sites: List[Tuple[str, str, str, str]]) -> str:
    """Format sites list to command string format."""
    return ",".join(f"{d}:{t}:{desc}:{th}" for d, t, desc, th in sites)


def update_status(registry_path: pathlib.Path, domains: List[str], status: str) -> None:
    """Update status for domains in registry with fcntl locking."""
    if not registry_path.exists():
        return
    lock_path = registry_path.parent / "REGISTRY.lock"
    lock_path.parent.mkdir(parents=True, exist_ok=True)
    with lock_path.open("w") as lf:
        fcntl.flock(lf, fcntl.LOCK_EX)
        try:
            lines = registry_path.read_text().splitlines()
            new_lines = []
            now = datetime.datetime.now().isoformat(timespec="seconds")
            for line in lines:
                if line.startswith("|") and "Domain" not in line and "--------" not in line:
                    cols = [c.strip() for c in line.strip("|").split("|")]
                    if len(cols) >= 6 and cols[0] in domains:
                        cols[3] = status
                        cols[5] = now
                        line = f"| {cols[0]} | {cols[1]} | {cols[2]} | {cols[3]} | {cols[4]} | {cols[5]} |"
                new_lines.append(line)
            registry_path.write_text("\n".join(new_lines) + "\n")
        finally:
            fcntl.flock(lf, fcntl.LOCK_UN)


def write_design_manifest(site_dir: pathlib.Path, domain: str, title: str, theme: str, batch_id: str) -> None:
    manifest_path = site_dir / "DesignPackage.json"
    data = {
        "domain": domain,
        "title": title,
        "theme": theme,
        "batch": batch_id,
        "generated_at": datetime.datetime.now().isoformat(timespec="seconds"),
        "status": "D",
    }
    manifest_path.write_text(json.dumps(data, indent=2))


def render_design_md(domain: str, template: pathlib.Path) -> str:
    if not template.exists():
        return f"# Design for {domain}\n\nPlaceholder design language.\n"
    text = template.read_text(encoding="utf-8")
    return (
        text.replace("{{domain}}", domain)
        .replace("{{style}}", "underused aesthetic")
        .replace("{{tone}}", "distinct tone")
        .replace("{{diffs}}", "list 3 differences")
        .replace("{{layout}}", "layout outline")
        .replace("{{nav}}", "navigation pattern")
        .replace("{{palette}}", "palette direction")
        .replace("{{type}}", "typefaces")
        .replace("{{imagery}}", "imagery direction")
        .replace("{{motifs}}", "motifs")
        .replace("{{sections}}", "sections to build")
        .replace("{{interactions}}", "interactions to implement")
        .replace("{{avoid}}", "overused patterns")
        .replace("{{prefer}}", "underused patterns")
        .replace("{{similarity_ref}}", "similarity findings ref")
    )


def similarity_guard(root_path: pathlib.Path, texts: List[str], threshold: float = 0.5) -> bool:
    try:
        from tools.shared import similarity  # type: ignore
    except Exception:
        sys.path.append(str(root_path / "tools" / "shared"))
        import similarity  # type: ignore
    max_sim = similarity.max_similarity(texts)
    return max_sim >= threshold


def main() -> int:
    parser = argparse.ArgumentParser(description="Design workflow runner")
    parser.add_argument("--sites", default=None, help="Comma-separated site list domain:title:desc[:theme] (auto-detects from registry if not provided)")
    parser.add_argument("--batch-size", type=int, default=20, help="Batch size for design")
    parser.add_argument("--dry-run", action="store_true", help="Simulate without writing assets")
    parser.add_argument("--root", default=None, help="Override repository root (defaults to CWD)")
    args = parser.parse_args()

    ROOT = pathlib.Path(args.root).resolve() if args.root else pathlib.Path.cwd().resolve()
    REGISTRY = ROOT / ".smbatcher" / "REGISTRY.md"
    RUNS_DIR = ROOT / ".smbatcher" / "runs"
    DESIGN_TEMPLATE = ROOT / "tools" / "design" / "templates" / "Design.md"

    # Auto-detect and claim sites if not provided
    # Exit codes: 0=found, 1=no work, 2=error
    sites_arg = args.sites
    result_data = {}
    if not sites_arg:
        # Use find-next with --claim for atomic find + status update (prevents race conditions)
        try:
            find_result = subprocess.run(
                [
                    str(ROOT / "tools/shared/find-next.sh"),
                    "--registry", str(REGISTRY),
                    "--mode", "sites",
                    "--phase", "design",
                    "--format", "json",
                    "--claim",  # Atomic find + claim with fcntl lock
                ],
                capture_output=True,
                text=True,
                check=False,
            )
            if find_result.returncode == 2:
                # Actual error - show output and exit
                print("[design] Error during auto-detection:")
                print(find_result.stdout)
                if find_result.stderr:
                    print(find_result.stderr)
                return 2
            elif find_result.returncode == 0:
                result_data = json.loads(find_result.stdout)
                auto_sites = result_data.get("sites", [])
                batch_id_hint = result_data.get("batch")
                claimed = result_data.get("claimed", False)

                if auto_sites:
                    # Read full site info from registry for formatting
                    full_sites = read_sites_from_registry(REGISTRY, ["d", "B", "-"])
                    site_map = {s[0]: s for s in full_sites}
                    selected = [site_map[d] for d in auto_sites[:args.batch_size] if d in site_map]

                    if selected:
                        sites_arg = format_sites_string(selected)
                        if claimed:
                            print(f"[design] Auto-detected and claimed {len(selected)} sites")
                        else:
                            print(f"[design] Continuing work on {len(selected)} sites")
            else:
                # Exit 1 = no work available
                result_data = json.loads(find_result.stdout) if find_result.stdout.strip() else {}
        except Exception as exc:
            print(f"[design] Auto-detection failed: {exc}")

        if not sites_arg:
            print("FAIL:no_sites_ready:run with --sites or register sites first")
            return 1

    RUNS_DIR.mkdir(parents=True, exist_ok=True)
    log_path = RUNS_DIR / f"design-{datetime.datetime.now().strftime('%Y%m%dT%H%M%S')}.log"
    log_path.write_text(f"args={args}\nsites_arg={sites_arg}\n")

    if args.dry_run:
        print("OK:dry_run:skipping writes")
        return 0

    # Register sites
    subprocess.run(
        [str(ROOT / "tools/design/register.sh"), "--sites", sites_arg],
        check=True,
    )

    # Batch selection
    batch_proc = subprocess.run(
        [
            str(ROOT / "tools/design/batch.sh"),
            "--sites",
            sites_arg,
            "--batch-size",
            str(args.batch_size),
        ],
        check=True,
        capture_output=True,
        text=True,
    )
    with open(log_path, "a", encoding="utf-8") as fh:
        fh.write(batch_proc.stdout)
    batch_id = "unknown"
    for line in batch_proc.stdout.splitlines():
        if line.startswith("BATCH_ID="):
            batch_id = line.split("=", 1)[1].strip()
            break
    print(f"[design] batch_id={batch_id}")

    # Transition B→d→D and write manifests
    sites = parse_sites(sites_arg)[: args.batch_size]
    domains = [s[0] for s in sites]
    update_status(REGISTRY, domains, "d")
    prior_texts = history.load_design_texts(ROOT)
    for domain, title, desc, theme in sites:
        site_dir = ROOT / "sites" / f"{domain}-v1"
        site_dir.mkdir(parents=True, exist_ok=True)
        design_md = render_design_md(domain, DESIGN_TEMPLATE)
        (site_dir / "DESIGN.md").write_text(design_md)
        write_design_manifest(site_dir, domain, title, theme, batch_id)
        prior_texts.append(design_md)
        if similarity_guard(ROOT, prior_texts, threshold=0.5):
            with open(log_path, "a", encoding="utf-8") as fh:
                fh.write(f"similarity high for {domain}, advisory redesign suggested\n")
            print(f"[design] similarity high for {domain}; advisory only")
    update_status(REGISTRY, domains, "D")

    # Advisory frequency report (non-blocking)
    try:
        freq_proc = subprocess.run(
            [
                str(ROOT / "tools/design/frequency.py"),
                "--root",
                str(ROOT),
                "--overused",
                "0.3",
                "--underused",
                "0.1",
                "--top",
                "5",
            ],
            check=False,
            capture_output=True,
            text=True,
        )
        with open(log_path, "a", encoding="utf-8") as fh:
            fh.write("frequency_report:\n")
            fh.write(freq_proc.stdout)
        if freq_proc.stdout.strip():
            print(freq_proc.stdout.strip())
    except Exception as exc:  # noqa: BLE001
        with open(log_path, "a", encoding="utf-8") as fh:
            fh.write(f"frequency_report_error: {exc}\n")

    # Log completion
    with open(log_path, "a", encoding="utf-8") as fh:
        fh.write(f"batch_id={batch_id}\n")
        fh.write(f"domains={','.join(domains)}\n")

    return 0


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