#!/usr/bin/env python3.12
"""
Validate Design.md structure, content quality, and completeness.

Checks:
- Required headings present
- No placeholder content
- Minimum content length per section
- Color palette format (hex codes)
- Typography mentions
- Stamp presence
- Uniqueness notes quality
"""
from __future__ import annotations

import argparse
import pathlib
import re
import sys
from typing import List, Set, Optional, Dict, Tuple

REQUIRED_HEADINGS = [
    "## Aesthetics and Tone",
    "## Layout Motifs and Structure",
    "## Typography and Palette",
    "## Imagery and Motifs",
    "## Prompts for Implementation",
    "## Uniqueness Notes",
]

MIN_SECTION_LENGTH = 50  # Minimum characters per section
MIN_UNIQUENESS_DIFFERENTIATORS = 3


def find_latest_batch(root: pathlib.Path) -> pathlib.Path | None:
    batch_dir = root / ".smbatcher" / "batches"
    candidates = list(batch_dir.glob("Batch_*.md")) + list(batch_dir.glob("batch-*-design.md"))
    if not candidates:
        return None
    return sorted(candidates, key=lambda p: p.stat().st_mtime)[-1]


def extract_design_paths(batch_path: pathlib.Path, root: pathlib.Path) -> List[pathlib.Path]:
    design_paths: List[pathlib.Path] = []
    seen: Set[str] = set()
    header_cols: Optional[List[str]] = None

    for line in batch_path.read_text(encoding="utf-8").splitlines():
        if line.strip().startswith("## Planned Seeds/Styles"):
            break
        if not line.startswith("|"):
            continue
        if "--------" in line:
            continue
        cols_raw = [c.strip() for c in line.strip("|").split("|")]
        if any("domain" in c.lower() or "directory" in c.lower() for c in cols_raw):
            header_cols = [c.lower() for c in cols_raw]
            continue
        if not cols_raw or (len(cols_raw) == 1 and not cols_raw[0]):
            continue

        directory: Optional[str] = None
        domain: Optional[str] = None

        if header_cols:
            def idx(name: str) -> Optional[int]:
                try:
                    return header_cols.index(name)
                except ValueError:
                    return None

            dir_idx = idx("directory")
            dom_idx = idx("domain")
            if dir_idx is not None and dir_idx < len(cols_raw) and cols_raw[dir_idx]:
                directory = cols_raw[dir_idx]
            if dom_idx is not None and dom_idx < len(cols_raw) and cols_raw[dom_idx]:
                domain = cols_raw[dom_idx]

        if domain is None:
            for c in cols_raw:
                if c and "." in c:
                    domain = c
                    break
            if domain is None and cols_raw and cols_raw[0]:
                domain = cols_raw[0]
        if domain and "/" in domain and directory is None:
            directory = domain
            domain = pathlib.Path(domain).name.split("-")[0]

        if directory:
            design_dir = root / directory
        elif domain:
            design_dir = root / "sites" / f"{domain}-v1"
        else:
            design_dir = None

        if design_dir:
            design_path = (design_dir / "DESIGN.md").resolve()
            key = str(design_path)
            if key not in seen:
                seen.add(key)
                design_paths.append(design_path)

    return design_paths


def extract_sections(text: str) -> Dict[str, str]:
    """Extract content for each section."""
    sections: Dict[str, str] = {}

    for heading in REQUIRED_HEADINGS:
        pattern = re.compile(
            rf"{re.escape(heading)}\s*(.*?)(?=^##|\Z)",
            re.DOTALL | re.MULTILINE
        )
        match = pattern.search(text)
        if match:
            sections[heading] = match.group(1).strip()
        else:
            sections[heading] = ""

    return sections


def check_placeholders(text: str) -> List[str]:
    """Check for placeholder content."""
    issues = []
    placeholder_patterns = [
        r"\(placeholder",
        r"TODO",
        r"FIXME",
        r"\[fill in\]",
        r"\[insert\]",
        r"lorem ipsum",
        r"example\.com",
        r"xxx",
    ]

    for pattern in placeholder_patterns:
        if re.search(pattern, text, re.IGNORECASE):
            issues.append(f"Contains placeholder: {pattern}")

    return issues


def check_colors(text: str) -> Tuple[List[str], List[str]]:
    """Check for color definitions."""
    colors_found = re.findall(r"#[0-9A-Fa-f]{3,6}\b", text)
    issues = []

    if len(colors_found) < 3:
        issues.append(f"Only {len(colors_found)} colors defined (recommend 3+)")

    # Check for rgba patterns too
    rgba_found = re.findall(r"rgba?\([^)]+\)", text)

    return colors_found, issues


def check_typography(text: str) -> Tuple[List[str], List[str]]:
    """Check for typography definitions."""
    # Common font names
    font_pattern = r"(Inter|Helvetica|Arial|Roboto|Open Sans|Lato|Montserrat|Poppins|Source|Space|IBM Plex|JetBrains|Fira|Monaco|Georgia|Merriweather|Playfair|Lora|Work Sans|Canela|Cormorant|Sora|Futura)[^,;)]*"
    fonts_found = re.findall(font_pattern, text, re.IGNORECASE)
    issues = []

    if len(fonts_found) < 1:
        issues.append("No typography/font specifications found")

    return list(set(fonts_found)), issues


def check_uniqueness_notes(text: str) -> List[str]:
    """Check uniqueness notes section quality."""
    issues = []

    # Find uniqueness notes section
    match = re.search(r"##\s*Uniqueness Notes(.*?)(?=##|$)", text, re.DOTALL | re.IGNORECASE)
    if not match:
        issues.append("Missing Uniqueness Notes section")
        return issues

    section = match.group(1)

    # Count differentiators (bullet points or numbered items)
    differentiators = len(re.findall(r"^[\s]*[-*\d.]+\s+", section, re.MULTILINE))
    if differentiators < MIN_UNIQUENESS_DIFFERENTIATORS:
        issues.append(f"Only {differentiators} differentiators (need {MIN_UNIQUENESS_DIFFERENTIATORS}+)")

    # Check for seed/style mention
    if not re.search(r"(seed|style)", section, re.IGNORECASE):
        issues.append("No seed/style mentioned in Uniqueness Notes")

    return issues


def check_stamp(text: str) -> List[str]:
    """Check for design stamp."""
    issues = []

    if "DESIGN STAMP" not in text and "updated" not in text:
        issues.append("No design stamp found (run write-design.py)")

    return issues


def validate_design(path: pathlib.Path, verbose: bool = False) -> Tuple[bool, List[str]]:
    """Validate a single DESIGN.md file."""
    issues: List[str] = []

    if not path.exists():
        return False, ["File not found"]

    text = path.read_text(encoding="utf-8").strip()

    if not text:
        return False, ["File is empty"]

    # Check title
    if not re.search(r"^#\s+Design Language", text, re.MULTILINE):
        issues.append("Missing title (# Design Language for ...)")

    # Check required headings
    sections = extract_sections(text)
    for heading in REQUIRED_HEADINGS:
        if heading not in text:
            issues.append(f"Missing: {heading}")
        elif len(sections.get(heading, "")) < MIN_SECTION_LENGTH:
            issues.append(f"Section too short: {heading} ({len(sections.get(heading, ''))} chars)")

    # Check for placeholders
    issues.extend(check_placeholders(text))

    # Check colors
    colors, color_issues = check_colors(text)
    issues.extend(color_issues)

    # Check typography
    fonts, font_issues = check_typography(text)
    issues.extend(font_issues)

    # Check uniqueness notes
    issues.extend(check_uniqueness_notes(text))

    # Check stamp
    issues.extend(check_stamp(text))

    return len(issues) == 0, issues


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Validate DESIGN.md files",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  %(prog)s sites/example.com-v1/DESIGN.md
  %(prog)s --batch-id 001
  %(prog)s --root . --verbose
        """
    )
    group = parser.add_mutually_exclusive_group()
    group.add_argument("path", nargs="?", help="Path to a single DESIGN.md")
    group.add_argument("--batch-id", help="Validate all DESIGN.md files for a batch")
    parser.add_argument("--root", default=".", help="Repo root (default: .)")
    parser.add_argument("--verbose", "-v", action="store_true", help="Show detailed issues")
    parser.add_argument("--strict", action="store_true", help="Fail on any issue")
    args = parser.parse_args()

    root = pathlib.Path(args.root).resolve()

    targets: List[pathlib.Path]
    if args.batch_id:
        batch_path = root / ".smbatcher" / "batches" / f"Batch_{args.batch_id}.md"
        if not batch_path.exists():
            print(f"ERROR:batch_not_found:{batch_path}")
            return 2
        targets = extract_design_paths(batch_path, root)
        if not targets:
            print(f"ERROR:no_designs_in_batch:{batch_path}")
            return 2
    elif args.path:
        targets = [pathlib.Path(args.path).resolve()]
    else:
        batch_path = find_latest_batch(root)
        if batch_path is None:
            print("ERROR:no_batch_files:.smbatcher/batches")
            return 2
        targets = extract_design_paths(batch_path, root)
        if not targets:
            print(f"ERROR:no_designs_in_batch:{batch_path}")
            return 2

    passed = 0
    warnings = 0
    failed = 0

    for path in targets:
        valid, issues = validate_design(path, args.verbose)

        if valid:
            print(f"✓ {path.parent.name}")
            passed += 1
        elif issues and all("stamp" in i.lower() or "short" in i.lower() for i in issues):
            print(f"⚠ {path.parent.name}")
            if args.verbose:
                for issue in issues:
                    print(f"    {issue}")
            warnings += 1
        else:
            print(f"✗ {path.parent.name}")
            if args.verbose or len(targets) == 1:
                for issue in issues:
                    print(f"    {issue}")
            failed += 1

    print("")
    print(f"Results: {passed} passed, {warnings} warnings, {failed} failed")

    if args.strict:
        if failed > 0 or warnings > 0:
            print(f"FAIL:check_failed:{failed} failures, {warnings} warnings")
            return 1
        print("OK:check_passed")
        return 0
    if failed > 0:
        print(f"FAIL:check_failed:{failed} failures")
        return 1
    print("OK:check_passed")
    return 0


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