#!/usr/bin/env python3
"""
TokenMoulds CLI Entry Point
Command-line interface for the formulaic design token system
"""

import argparse
import io
import json
import subprocess
import sys
import tempfile
import zipfile
from pathlib import Path
from typing import Any, Dict, List

import yaml

from tokenmoulds import __version__
from tokenmoulds.dtcg import (
    RecipeInputs,
    adapt_dtcg_layers,
    build_recipe,
    emit_tokens_to_string,
)
from tokenmoulds.dtcg.generator import TokenGenerator
from tokenmoulds.emitters import (
    TemplateResolver,
)
from tokenmoulds.ir import build_document_ir
from tokenmoulds.licensing import resolve_license_context, validate_or_exit

_ODF_EXTENSIONS = {".odt", ".ods", ".odp", ".odg", ".ott", ".ots", ".otp", ".otg"}


def _preview_sample(
    previews: Dict[str, List[str]], key: str, *, limit: int = 4, separator: str = ", "
) -> str:
    values = previews.get(key) or []
    if not values:
        return ""
    sample = separator.join(values[:limit])
    return sample


def _print_preview_samples(
    previews: Dict[str, List[str]],
    key: str,
    label: str,
    *,
    limit: int = 4,
    separator: str = ", ",
) -> None:
    sample = _preview_sample(previews, key, limit=limit, separator=separator)
    if sample:
        print(f"      {label}: {sample}")


def _render_preview_section(previews: Dict[str, List[str]]) -> str:
    lines: List[str] = []
    for label, key in (
        ("Lighten", "lighten"),
        ("Darken", "darken"),
        ("Saturate", "saturate"),
    ):
        sample = _preview_sample(previews, key, limit=6)
        if sample:
            lines.append(f"- **{label}:** {sample}")
    contrast_sample = _preview_sample(
        previews, "heading_contrast", limit=6, separator=" | "
    )
    if contrast_sample:
        lines.append(f"- **Heading vs Body contrast:** {contrast_sample}")
    for mode in ("protanopia", "deuteranopia", "tritanopia"):
        sample = _preview_sample(previews, f"color_blind_{mode}", limit=6)
        if sample:
            label = f"{mode.capitalize()} palette"
            lines.append(f"- **{label}:** {sample}")
    if not lines:
        return ""
    return "### Palette Previews\n" + "\n".join(lines) + "\n\n"


def _count_tokens(payload: Any) -> int:
    if isinstance(payload, dict):
        return sum(_count_tokens(value) for value in payload.values())
    if isinstance(payload, list):
        return sum(_count_tokens(value) for value in payload)
    return 1


def _resolve_license(args: argparse.Namespace) -> None:
    context = resolve_license_context(
        license_id=getattr(args, "license_id", None),
        license_repo=getattr(args, "license_repo", None),
        default_repo=f"tokenmoulds/{args.org_id}",
    )
    validate_or_exit(context)


def _run_command(
    command: List[str], description: str, *, verbose: bool = False
) -> None:
    if verbose:
        print(f"🔧 {description}: {' '.join(command)}")
    result = subprocess.run(
        command,
        capture_output=not verbose,
        text=True,
        cwd=Path.cwd(),
    )
    if result.returncode != 0:
        stderr = result.stderr.strip() if result.stderr else "Unknown error"
        raise RuntimeError(f"{description} failed: {stderr}")


def _package_emitter_output(
    payload: bytes,
    output_path: Path,
    *,
    tokens_path: Path,
    embed_fonts: bool,
    fonts_dir: Path,
    validate: bool,
    verbose: bool,
) -> None:
    output_path.parent.mkdir(parents=True, exist_ok=True)
    with tempfile.TemporaryDirectory(prefix="tokenmoulds_emit_") as temp_dir:
        input_dir = Path(temp_dir) / "input"
        input_dir.mkdir(parents=True, exist_ok=True)
        with zipfile.ZipFile(io.BytesIO(payload), "r") as zf:
            for member in zf.namelist():
                member_path = (input_dir / member).resolve()
                if not member_path.is_relative_to(input_dir.resolve()):
                    raise ValueError(f"Zip entry escapes target directory: {member}")
            zf.extractall(input_dir)

        command = [
            sys.executable,
            "bin/package-template",
            "--input",
            str(input_dir),
            "--output",
            str(output_path),
        ]
        if embed_fonts:
            command.append("--embed-fonts")
        command.extend(["--tokens", str(tokens_path)])
        command.extend(["--fonts-dir", str(fonts_dir)])
        if verbose:
            command.append("--verbose")
        _run_command(command, "Packaging template", verbose=verbose)

        # Optional LibreOffice / OpenOffice macro injection
        if output_path.suffix.lower() in _ODF_EXTENSIONS:
            macros_root = Path("macros/libreoffice")
            if macros_root.exists():
                from tokenmoulds.odf_macros import (  # pyright: ignore[reportMissingImports]
                    inject_libreoffice_macros,  # type: ignore[import-not-found]
                )

                tmp_out = output_path.with_suffix(output_path.suffix + ".tmp")
                inject_libreoffice_macros(output_path, macros_root, tmp_out)
                tmp_out.rename(output_path)

    if validate:
        format_type = (
            "odf" if output_path.suffix.lower() in _ODF_EXTENSIONS else "ooxml"
        )
        command = [
            sys.executable,
            "bin/validate-template",
            "--template",
            str(output_path),
            "--format",
            format_type,
            "--extract",
        ]
        if verbose:
            command.append("--verbose")
        _run_command(command, "Validating packaged template", verbose=verbose)


def parse_font_pair(font_pair_str: str) -> Dict[str, str]:
    """Parse font pair string into sans/serif dictionary."""
    font_pairs = {
        "inter-roboto": {"sans": "Inter", "serif": "Roboto Slab"},
        "work-sans-source": {"sans": "Work Sans", "serif": "Source Serif Pro"},
        "public-sans-spectral": {"sans": "Public Sans", "serif": "Spectral"},
    }

    if font_pair_str not in font_pairs:
        available = ", ".join(font_pairs.keys())
        raise ValueError(f"Invalid font pair '{font_pair_str}'. Available: {available}")

    return font_pairs[font_pair_str]


def validate_hex_color(color: str) -> str:
    """Validate and normalize hex color."""
    if not color.startswith("#"):
        color = "#" + color

    if len(color) != 7:
        raise ValueError(f"Invalid hex color '{color}'. Must be 6 digits after #")

    try:
        int(color[1:], 16)
    except ValueError:
        raise ValueError(f"Invalid hex color '{color}'. Must contain only hex digits")

    return color.upper()


def validate_brand_tone(tone: float) -> float:
    """Validate brand tone range."""
    if not 0.0 <= tone <= 1.0:
        raise ValueError(f"Brand tone must be between 0.0 and 1.0, got {tone}")
    return tone


def validate_content_density(density: float) -> float:
    """Validate content density range."""
    if not 0.0 <= density <= 1.0:
        raise ValueError(f"Content density must be between 0.0 and 1.0, got {density}")
    return density


def generate_tokens(args: argparse.Namespace) -> None:
    """Generate design tokens and optionally templates."""

    print("🎨 TokenMoulds Formulaic Design Token Generator")
    print("=" * 50)

    try:
        # Handle --output alias
        if getattr(args, "output_alias", None) and not args.output_dir:
            args.output_dir = args.output_alias

        # Handle --url: extract brand and generate via BuildPipeline
        if args.url:
            from tokenmoulds.brand_from_url import extract_brand
            from tokenmoulds.pipeline import BuildConfig, build as build_pipeline

            print(f"🌐 Extracting brand from {args.url}...")
            brand = extract_brand(args.url)
            org_id = args.org_id or brand.name.lower().replace(" ", "-")
            print(f"   Brand: {brand.name}")
            print(
                f"   Primary: {brand.primary_color}, Secondary: {brand.secondary_color}"
            )
            print(f"   Fonts: {brand.heading_font} / {brand.body_font}")

            output_dir = Path(args.output_dir or f"./{org_id}-templates")
            output_dir.mkdir(parents=True, exist_ok=True)

            config = BuildConfig(
                org_id=org_id,
                output_formats=["potx", "dotx", "xltx"],
                brand_inputs={
                    "font_pair": {"sans": brand.body_font, "serif": brand.heading_font},
                    "base_colors": {
                        "primary": brand.primary_color,
                        "secondary": brand.secondary_color,
                    },
                    "locale": brand.locale,
                    "brand_tone": brand.brand_tone,
                },
                fonts_dir=Path(args.fonts_dir) if args.fonts_dir else None,
                embed_fonts=not args.disable_font_embedding,
                validate=not args.skip_validation,
            )
            result = build_pipeline(config)

            if result.success:
                for fmt, data in result.artifacts.items():
                    path = output_dir / f"{org_id}.{fmt}"
                    path.write_bytes(data)
                    print(f"   {path} ({len(data):,} bytes)")
                print(f"   Built in {result.timing_ms:.0f}ms")
            else:
                print(f"   Build failed: {result.error}")
            return

        # Validate org-id is present for non-URL flows
        if not args.org_id:
            print("Error: --org-id is required (or use --url)")
            return

        # Defaults for variables set in conditional branches below
        token_payload: dict | None = None
        baseline_emu: int | None = None
        token_count: int = 0

        # Check if using layered DTCG files (ADR 017)
        if args.layers_dir or args.layers_manifest:
            layers_source = args.layers_dir or args.layers_manifest
            print(f"📂 Loading layered DTCG tokens from: {layers_source}")

            # Use the layered adapter to merge, resolve references, and convert
            adapter_result = adapt_dtcg_layers(
                layers_dir=Path(args.layers_dir) if args.layers_dir else None,
                manifest_path=(
                    Path(args.layers_manifest) if args.layers_manifest else None
                ),
                detect_conflicts=True,
                strict_references=False,
            )

            # Report adapter results
            print(f"   Layers used: {', '.join(adapter_result.layers_used)}")
            print(f"   Input tokens: {adapter_result.input_token_count}")
            print(f"   Derived tokens: {adapter_result.derived_token_count}")
            if adapter_result.messages:
                for msg in adapter_result.messages:
                    print(f"   ⚠️ {msg}")

            # Extract values from the adapted payload
            payload = adapter_result.payload
            inputs = payload.get("inputs", {})
            font_pair = inputs.get("font_pair", {"sans": "Inter", "serif": "Inter"})
            if "sans" not in font_pair:
                font_pair["sans"] = font_pair.get("major", "Inter")
            if "serif" not in font_pair:
                font_pair["serif"] = font_pair.get("major", "Inter")
            base_colors = inputs.get("base_colors", {})
            primary_color = base_colors.get("primary", "#2563EB")
            secondary_color = base_colors.get("secondary", "#DC2626")
            brand_tone = inputs.get("brand_tone", 0.5)
            content_density = inputs.get("content_density", 0.4)
            locale = inputs.get("locale", args.locale or "US")

            print("📝 Configuration (from layered DTCG):")
            print(f"   Organization: {args.org_id}")
            print(
                f"   Font Pair: {font_pair.get('sans', 'Inter')} + {font_pair.get('serif', 'Inter')}"
            )
            print(f"   Colors: {primary_color} / {secondary_color}")
            print(f"   Locale: {locale}")
            print(
                f"   Brand Tone: {brand_tone} ({'formal' if brand_tone < 0.5 else 'casual'})"
            )
            print(
                f"   Content Density: {content_density} ({'sparse' if content_density < 0.5 else 'dense'})"
            )
            print()

            # Override locale if specified on command line
            if args.locale:
                locale = args.locale

            # Use the payload directly - it's already in internal format
            _resolve_license(args)

            print("🔧 Using pre-merged DTCG token payload...")
            token_payload = payload
            token_count = _count_tokens(token_payload)
            from tokenmoulds.design.layout_defaults import DEFAULT_BASELINE_EMU

            baseline_emu = token_payload.get("typography", {}).get(
                "baseline_emu", DEFAULT_BASELINE_EMU
            )

            print(f"✅ Loaded {token_count:,} design tokens from layered DTCG")
            print(f"📏 Baseline grid: {baseline_emu:,} EMU")

        # Check if using a tokens file instead of manual inputs
        elif args.tokens_file:
            tokens_path = Path(args.tokens_file)
            if not tokens_path.exists():
                raise FileNotFoundError(f"Tokens file not found: {tokens_path}")

            raise NotImplementedError(
                "--tokens-file is deprecated. Use --recipe to generate tokens "
                "from a DTCG recipe file."
            )

        else:
            # Parse and validate inputs from command line
            font_pair = parse_font_pair(args.font_pair)
            primary_color = validate_hex_color(args.primary_color)
            secondary_color = validate_hex_color(args.secondary_color)
            brand_tone = validate_brand_tone(args.brand_tone)
            content_density = validate_content_density(args.content_density)
            locale = args.locale

            base_colors = {"primary": primary_color, "secondary": secondary_color}

            print("📝 Configuration:")
            print(f"   Organization: {args.org_id}")
            print(f"   Font Pair: {font_pair['sans']} + {font_pair['serif']}")
            print(f"   Colors: {primary_color} / {secondary_color}")
            print(f"   Locale: {locale}")
            print(
                f"   Brand Tone: {brand_tone} ({'formal' if brand_tone < 0.5 else 'casual'})"
            )
            print(
                f"   Content Density: {content_density} ({'sparse' if content_density < 0.5 else 'dense'})"
            )
            print(f"   Accessibility: {args.accessibility_level}")
            print()

            inputs = {
                "font_pair": {
                    "major": font_pair["sans"],
                    "minor": font_pair["sans"],
                    "serif": font_pair["serif"],
                },
                "base_colors": base_colors,
                "locale": locale,
                "brand_tone": brand_tone,
                "content_density": content_density,
                "accessibility_level": args.accessibility_level,
            }

        # Skip license check and token generation if already done in layers branch
        if not (args.layers_dir or args.layers_manifest):
            _resolve_license(args)

            # Create the formulaic token engine
            print("🔧 Initializing formulaic token engine...")
            token_payload = TokenGenerator().generate(inputs)
            token_count = _count_tokens(token_payload)
            baseline_emu = token_payload["typography"]["baseline_emu"]

            print(f"✅ Generated {token_count:,} design tokens")
            print(f"📏 Baseline grid: {baseline_emu:,} EMU")

        if token_payload is None:
            raise SystemExit(
                "Error: no token source specified (use --tokens, --layers-dir, or --primary-color)"
            )
        document_ir = build_document_ir(token_payload, args.org_id, locale)
        if document_ir.color_analysis:
            palette = document_ir.color_analysis.palette_summary
            print("🎯 Palette diagnostics:")
            unique = palette.get("unique", "?")
            complexity = palette.get("complexity", 0.0)
            print(f"   Unique colors: {unique}")
            if isinstance(complexity, (int, float)):
                print(f"   Complexity score: {complexity:.2f}")
            warnings = document_ir.color_analysis.warnings
            if warnings:
                print("   ⚠️ Color QA Warnings:")
                for warning in warnings:
                    print(f"      • {warning}")
            else:
                print("   ✅ No color QA warnings detected.")
            previews = document_ir.color_analysis.previews or {}
            if previews:
                print("   🎨 Preview swatches:")
                _print_preview_samples(previews, "lighten", "Lighten")
                _print_preview_samples(previews, "darken", "Darken")
                _print_preview_samples(previews, "saturate", "Saturate")
                _print_preview_samples(
                    previews,
                    "heading_contrast",
                    "Heading vs Body contrast",
                    separator=" | ",
                )
                for mode in ("protanopia", "deuteranopia", "tritanopia"):
                    _print_preview_samples(
                        previews,
                        f"color_blind_{mode}",
                        f"{mode.capitalize()} palette",
                    )

        # Create output directory
        output_dir = Path(args.output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)

        tokens_dir = output_dir / "tokens"
        tokens_dir.mkdir(exist_ok=True)

        # Export tokens
        print("💾 Exporting tokens...")

        token_output = {
            "metadata": {
                "version": __version__,
                "generator": "TokenMoulds AdvancedTokenEngine",
                "parameters": inputs,
                "baseline_emu": baseline_emu,
                "tokens_count": token_count,
            },
            "tokens": token_payload,
        }

        # Default tokens path for template generation (used by _package_emitter_output)
        json_path = tokens_dir / f"{args.org_id}-design-tokens.json"

        # Export DTCG format (default) - as a self-contained recipe with formulas
        if args.token_format in ("dtcg", "both"):
            dtcg_path = tokens_dir / f"{args.org_id}.tokens.json"

            # Build recipe with derivation formulas
            recipe_inputs = RecipeInputs(
                primary_color=primary_color,
                secondary_color=secondary_color,
                font_sans=font_pair["sans"],
                font_serif=font_pair["serif"],
                baseline_pt=inputs.get("base_grid_pt", 11.0),
                brand_tone=brand_tone,
                content_density=content_density,
                locale=locale,
            )
            dtcg_file = build_recipe(recipe_inputs)
            dtcg_content = emit_tokens_to_string(dtcg_file)
            dtcg_path.write_text(dtcg_content, encoding="utf-8")
            print(f"   • {dtcg_path} (DTCG recipe with formulas)")

            # Use DTCG path as tokens path if not exporting legacy
            if args.token_format == "dtcg":
                json_path = dtcg_path

        # Export legacy format
        if args.token_format in ("legacy", "both"):
            json_path = tokens_dir / f"{args.org_id}-design-tokens.json"
            yaml_path = tokens_dir / f"{args.org_id}-design-tokens.yaml"
            json_path.write_text(json.dumps(token_output, indent=2), encoding="utf-8")
            yaml_path.write_text(
                yaml.dump(token_output, sort_keys=False), encoding="utf-8"
            )
            print(f"   • {json_path}")
            print(f"   • {yaml_path}")

        embed_fonts = not args.disable_font_embedding
        if args.embed_fonts:
            embed_fonts = True
        validate_outputs = not args.skip_validation
        fonts_dir = Path(args.fonts_dir) if args.fonts_dir else None

        # Determine which formats to generate
        output_formats: list[str] = []
        if args.generate_templates:
            output_formats.extend(["potx", "dotx", "xltx"])
        if args.generate_odf:
            output_formats.extend(["ott", "otp", "ots", "odg"])

        if output_formats:
            from tokenmoulds.pipeline import BuildConfig, build as build_pipeline

            print("📄 Generating templates via BuildPipeline...")
            templates_dir = output_dir / "templates"
            templates_dir.mkdir(exist_ok=True)

            pipeline_config = BuildConfig(
                org_id=args.org_id,
                output_formats=output_formats,
                token_payload=token_payload,
                locale=locale,
                template_root=(
                    Path(args.template_root) if args.template_root else None
                ),
                fonts_dir=fonts_dir,
                embed_fonts=embed_fonts,
                validate=validate_outputs,
            )
            pipeline_result = build_pipeline(pipeline_config)

            if not pipeline_result.success:
                print(f"   Build failed: {pipeline_result.error}")
            else:
                # Format name mapping for file output
                _FMT_NAMES = {
                    "potx": ("presentation", "📊"),
                    "dotx": ("document", "📝"),
                    "xltx": ("workbook", "📈"),
                    "ott": ("document", "📄"),
                    "otp": ("presentation", "📽"),
                    "ots": ("spreadsheet", "📊"),
                    "odg": ("drawing", "🎨"),
                }
                for fmt, data in pipeline_result.artifacts.items():
                    name, icon = _FMT_NAMES.get(fmt, (fmt, "📄"))
                    out_path = templates_dir / f"{args.org_id}-{name}.{fmt}"
                    out_path.write_bytes(data)
                    print(f"   {icon} {out_path} ({len(data)} bytes)")

                if pipeline_result.validation_results:
                    for fmt, issues in pipeline_result.validation_results.items():
                        print(f"   ⚠ {fmt}: {len(issues)} validation issues")

                print(f"   ✓ Built in {pipeline_result.timing_ms:.0f}ms")

        if args.compare_baselines:
            from tokenmoulds.testing.baseline_suite import run_baseline_comparison

            print("🧪 Comparing generated previews against baselines...")
            comparison = run_baseline_comparison(
                output_dir, Path(args.baselines_workspace)
            )
            for fmt, report in comparison.reports.items():
                status = "PASS" if report.result.passed else "FAIL"
                print(
                    f"   • {fmt}: {status} (similarity {report.result.similarity:.3f})"
                )
                if report.diff_path:
                    print(f"      Diff: {report.diff_path}")

        # Create summary
        if args.create_summary:
            summary_path = output_dir / "GENERATION_SUMMARY.md"
            create_summary_report(
                args,
                token_payload,
                token_count,
                summary_path,
                document_ir,
                baseline_emu or 0,
            )  # baseline_emu is always set when token_payload is set
            print(f"📋 Summary report: {summary_path}")

        print()
        print("🎉 TokenMoulds generation completed successfully!")

    except Exception as e:
        print(f"❌ Error: {e}", file=sys.stderr)
        sys.exit(1)


def create_summary_report(
    args: argparse.Namespace,
    token_payload: Dict[str, Any],
    token_count: int,
    output_path: Path,
    document_ir,
    baseline_emu: int,
) -> None:
    """Create a detailed summary report."""

    from datetime import datetime, timezone

    summary = f"""# TokenMoulds Generation Summary

**Organization:** `{args.org_id}`
**Generated:** `{datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")}`
**CLI Version:** `tokenmoulds v{__version__}`

## Input Parameters

| Parameter | Value |
|-----------|-------|
| Font Pair | {args.font_pair} |
| Primary Color | {args.primary_color} |
| Secondary Color | {args.secondary_color} |
| Locale | {args.locale} |
| Brand Tone | {args.brand_tone} |
| Content Density | {args.content_density} |
| Accessibility Level | {args.accessibility_level} |

## Generated Tokens

- **Total Tokens:** {token_count:,}
- **Baseline Grid (EMU):** {baseline_emu:,}
- **Typography Scales:** {len(token_payload["typography"]["scale_emu"])}
- **Color Palette:** {len(token_payload["colors"]["palette"])} colors
- **Spacing Scale:** {len(token_payload["spacing"]["scale_emu"])} sizes
- **Style Chunks:** {len(token_payload["styles"])}

## Token Categories

### Typography
- Fonts: {token_payload["typography"]["fonts"]["minor"]} (body), {token_payload["typography"]["fonts"]["serif"]} (headings)
- Scale Entries: {len(token_payload["typography"]["scale_emu"])}

### Colors
- Palette: {", ".join(token_payload["colors"]["palette"].keys())}
- Theme Mapping: {", ".join(token_payload["colors"]["theme_mapping"].keys())}
- Text Colors: {len(token_payload["colors"]["text"])} variants

### Styles
- Families: {", ".join(token_payload["styles"].keys())}
- Heading Levels: {len(token_payload["styles"]["heading"]["levels"])}
- Table Accents: {len(token_payload["styles"]["table"]["accents"])}

## Files Generated

### Design Tokens
- `{args.org_id}-design-tokens.json` - Complete token system (JSON)
- `{args.org_id}-design-tokens.yaml` - Complete token system (YAML)

"""

    if getattr(args, "generate_templates", False):
        summary += f"""
### Office Templates
- `{args.org_id}-presentation.potx` - PowerPoint template
- `{args.org_id}-document.dotx` - Word document template
- `{args.org_id}-google-docs.dotx` - Google Docs compatible template
- `{args.org_id}-workbook.xltx` - Excel workbook template
- `{args.org_id}-writer-styles.xml` - LibreOffice Writer styles

"""

    if document_ir.color_analysis:
        palette = document_ir.color_analysis.palette_summary
        summary += f"""## Palette Diagnostics

- **Unique Colors:** {palette.get("unique", "?")}
- **Complexity Score:** {palette.get("complexity", 0.0):.2f}
- **Max OKLab Distance:** {palette.get("max_oklab_distance", 0.0):.3f}

"""
        if document_ir.color_analysis.warnings:
            summary += "### Color QA Warnings\n"
            for warning in document_ir.color_analysis.warnings:
                summary += f"- {warning}\n"
            summary += "\n"
        previews = document_ir.color_analysis.previews or {}
        summary += _render_preview_section(previews)

    summary += """## Usage

These generated files can be used to:

1. **Apply design system** to Office documents
2. **Import tokens** into design tools
3. **Share brand assets** across teams
4. **Maintain consistency** across all materials

---
*Generated by TokenMoulds Formulaic Design Token System*
"""

    output_path.write_text(summary, encoding="utf-8")


def run_mcp_server(args: List[str]) -> None:
    """Run the MCP server subcommand."""
    import argparse as mcp_argparse

    parser = mcp_argparse.ArgumentParser(
        prog="tokenmoulds mcp-server",
        description="Start TokenMoulds MCP server for AI document generation",
    )
    parser.add_argument(
        "--tokens",
        type=str,
        help="Path to design tokens directory",
    )
    parser.add_argument(
        "--brands",
        type=str,
        help="Path to multi-brand directory (alternative to --tokens)",
    )
    parser.add_argument(
        "--output",
        type=str,
        default="./generated",
        help="Output directory for generated documents (default: ./generated)",
    )

    parsed = parser.parse_args(args)

    from pathlib import Path

    from tokenmoulds.mcp import run_server

    tokens_path = Path(parsed.tokens) if parsed.tokens else None
    brands_path = Path(parsed.brands) if parsed.brands else None
    output_path = Path(parsed.output)

    print("🚀 Starting TokenMoulds MCP Server...", file=sys.stderr)
    if tokens_path:
        print(f"   Tokens: {tokens_path}", file=sys.stderr)
    if brands_path:
        print(f"   Brands: {brands_path}", file=sys.stderr)
    print(f"   Output: {output_path}", file=sys.stderr)
    print("   Ready for MCP connections", file=sys.stderr)

    run_server(
        tokens_path=tokens_path,
        brands_path=brands_path,
        output_path=output_path,
    )


def main() -> None:
    """Main CLI entry point."""

    # Check for mcp-server subcommand
    if len(sys.argv) > 1 and sys.argv[1] == "mcp-server":
        run_mcp_server(sys.argv[2:])
        return

    parser = argparse.ArgumentParser(
        prog="tokenmoulds",
        description="TokenMoulds Formulaic Design Token Generator",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Generate design tokens and templates from manual inputs
  tokenmoulds --org-id="acme" --font-pair="inter-roboto" --primary-color="#2563EB" --secondary-color="#DC2626" --locale="GB" --brand-tone=0.3

  tokenmoulds --org-id="startup" --font-pair="work-sans-source" --primary-color="#6366F1" --locale="US" --brand-tone=0.8 --generate-templates

  # Generate templates from a DTCG tokens file
  tokenmoulds --org-id="acme" --tokens-file="design.tokens.json" --generate-templates

  # Generate templates from layered DTCG files (ADR 017)
  tokenmoulds --org-id="acme" --layers-dir="data/layers/" --generate-templates
  tokenmoulds --org-id="acme" --layers-manifest="data/manifest.json" --generate-templates

  # Start MCP server for AI document generation
  tokenmoulds mcp-server --tokens ./data/
  tokenmoulds mcp-server --brands ./brands/ --output ./generated/
        """,
    )

    # Brand source — URL or manual
    parser.add_argument(
        "--url",
        type=str,
        default=None,
        help="Extract brand from website URL and generate templates",
    )

    parser.add_argument(
        "--output",
        type=str,
        default=None,
        dest="output_alias",
        help="Output directory (alias for --output-dir)",
    )

    # Organization ID (required unless --url provides it)
    parser.add_argument(
        "--org-id",
        required=False,
        default=None,
        help='Organization identifier (e.g., "acme", "startup")',
    )

    parser.add_argument(
        "--font-pair",
        choices=["inter-roboto", "work-sans-source", "public-sans-spectral"],
        help="Font pair selection (required unless using --tokens-file)",
    )

    parser.add_argument(
        "--primary-color",
        help='Primary brand color (hex, e.g., "#2563EB") (required unless using --tokens-file)',
    )

    parser.add_argument(
        "--secondary-color",
        help='Secondary brand color (hex, e.g., "#DC2626") (required unless using --tokens-file)',
    )

    parser.add_argument(
        "--locale",
        choices=["GB", "US", "DE", "FR", "JP", "HE", "NL"],
        help="Locale for formatting and punctuation (required unless using --tokens-file)",
    )

    parser.add_argument(
        "--brand-tone",
        type=float,
        help="Brand tone: 0.0 = formal, 1.0 = casual (required unless using --tokens-file)",
    )

    # Optional arguments
    parser.add_argument(
        "--content-density",
        type=float,
        default=0.4,
        help="Content density: 0.0 = sparse, 1.0 = dense (default: 0.4)",
    )

    parser.add_argument(
        "--accessibility-level",
        choices=["AA", "AAA"],
        default="AA",
        help="Accessibility compliance level (default: AA)",
    )

    parser.add_argument(
        "--license-id",
        help="License identifier to validate before generation (defaults to lic_local_dev)",
    )

    parser.add_argument(
        "--license-token",
        help="Signed license token for future remote validation (optional)",
    )

    parser.add_argument(
        "--license-repo",
        help="GitHub repository slug associated with this license (e.g., tokenmoulds/acme-suite)",
    )

    parser.add_argument(
        "--output-dir",
        default="generated",
        help="Output directory (default: generated/)",
    )

    parser.add_argument(
        "--token-format",
        choices=["legacy", "dtcg", "both"],
        default="dtcg",
        help="Token output format: legacy (internal), dtcg (W3C standard), or both (default: dtcg)",
    )

    parser.add_argument(
        "--tokens-file",
        type=str,
        default=None,
        help="Path to DTCG tokens file (.tokens.json) to generate templates from. "
        "When provided, skips token generation and uses pre-computed token values.",
    )

    parser.add_argument(
        "--layers-dir",
        type=str,
        default=None,
        help="Directory containing DTCG layer files (core.tokens.json, fork.tokens.json, etc.). "
        "Layers are merged in precedence order: core → fork → org → group → personal.",
    )

    parser.add_argument(
        "--layers-manifest",
        type=str,
        default=None,
        help="Path to manifest JSON file pointing to DTCG layer files. "
        "Alternative to --layers-dir for explicit layer configuration.",
    )

    parser.add_argument(
        "--template-root",
        default=None,
        help="Override directory containing format-specific template subfolders (word/excel/powerpoint)",
    )

    parser.add_argument(
        "--fonts-dir",
        default=None,
        help="Directory containing vendored font files for embedding",
    )

    parser.add_argument(
        "--disable-font-embedding",
        action="store_true",
        help="Skip font embedding during packaging",
    )

    parser.add_argument(
        "--embed-fonts",
        action="store_true",
        help="Force-enable font embedding (alias for the default behavior)",
    )

    parser.add_argument(
        "--skip-validation",
        action="store_true",
        help="Skip validate-template checks on packaged outputs",
    )

    parser.add_argument(
        "--generate-templates",
        action="store_true",
        help="Generate OOXML templates (.potx, .dotx, .xltx)",
    )

    parser.add_argument(
        "--generate-odf",
        action="store_true",
        help="Generate ODF templates (.ott, .otp)",
    )

    parser.add_argument(
        "--compare-baselines",
        action="store_true",
        help="After generating templates, compare screenshots against stored baselines",
    )

    parser.add_argument(
        "--baselines-workspace",
        default="tests/baselines",
        help="Path to baseline workspace (default: tests/baselines)",
    )

    parser.add_argument(
        "--create-summary",
        action="store_true",
        default=True,
        help="Create generation summary report (default: True)",
    )

    parser.add_argument("--verbose", action="store_true", help="Enable verbose output")

    parser.add_argument(
        "--preload-templates",
        action="store_true",
        help="Preload and cache all XML templates at startup for faster batch generation",
    )

    # Parse arguments
    args = parser.parse_args()

    # Validate required arguments when not using --tokens-file, --layers-dir, or --layers-manifest
    if not args.tokens_file and not args.layers_dir and not args.layers_manifest:
        missing = []
        if not args.font_pair:
            missing.append("--font-pair")
        if not args.primary_color:
            missing.append("--primary-color")
        if not args.secondary_color:
            missing.append("--secondary-color")
        if not args.locale:
            missing.append("--locale")
        if args.brand_tone is None:
            missing.append("--brand-tone")
        if missing:
            parser.error(
                f"The following arguments are required when not using --tokens-file, "
                f"--layers-dir, or --layers-manifest: {', '.join(missing)}"
            )

    # Set verbose mode
    if args.verbose:
        print("🔍 Verbose mode enabled")

    # Preload templates if requested
    if args.preload_templates:
        if args.verbose:
            print("⚡ Preloading template cache...")
        count = TemplateResolver.preload_all()
        if args.verbose:
            print(f"   Cached {count} templates")

    # Generate tokens
    generate_tokens(args)


if __name__ == "__main__":
    main()
