"""Token generation and summary report logic for the CLI."""

from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path
from typing import Any, Dict

import yaml

from tokenmoulds import __version__
from tokenmoulds.cli_utils import (
    _count_tokens,
    _print_preview_samples,
    _render_preview_section,
    _resolve_license,
    parse_font_pair,
    validate_brand_tone,
    validate_content_density,
    validate_hex_color,
)
from tokenmoulds.dtcg import (
    RecipeInputs,
    adapt_dtcg_layers,
    build_recipe,
    emit_tokens_to_string,
)
from tokenmoulds.dtcg.generator import TokenGenerator
from tokenmoulds.ir import build_document_ir


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

    print("\U0001f3a8 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"\U0001f310 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)

            default_formats = ["potx", "dotx", "xltx"]
            if getattr(args, "format_filter", None):
                default_formats = [f.strip() for f in args.format_filter.split(",")]

            if getattr(args, "dry_run", False):
                print("   (dry run — skipping emission)")
                return

            config = BuildConfig(
                org_id=org_id,
                output_formats=default_formats,
                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

        # Handle --manifest: build from brand-manifest.yaml
        if getattr(args, "manifest", None):
            from tokenmoulds.brand.manifest import (
                load_manifest,
                manifest_to_build_config,
            )
            from tokenmoulds.pipeline import build as build_pipeline

            manifest_path = Path(args.manifest)
            print(f"📋 Building from manifest: {manifest_path}")
            manifest = load_manifest(manifest_path)
            config = manifest_to_build_config(manifest, manifest_path.parent)

            # Apply CLI overrides
            if getattr(args, "format_filter", None):
                config.output_formats = [
                    f.strip() for f in args.format_filter.split(",")
                ]
            if args.fonts_dir:
                config.fonts_dir = Path(args.fonts_dir)
            config.embed_fonts = not args.disable_font_embedding
            config.validate = not args.skip_validation

            if getattr(args, "dry_run", False):
                print(f"   Brand: {manifest.brand_id}")
                print(f"   Formats: {config.output_formats}")
                print("   (dry run — skipping emission)")
                return

            result = build_pipeline(config)
            output_dir = Path(args.output_dir or f"./{manifest.brand_id}-templates")
            output_dir.mkdir(parents=True, exist_ok=True)

            if result.success:
                for fmt, data in result.artifacts.items():
                    path = output_dir / f"{manifest.artifact_prefix}.{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/manifest 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"\U0001f4c2 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"   \u26a0\ufe0f {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("\U0001f4dd 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("\U0001f527 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"\u2705 Loaded {token_count:,} design tokens from layered DTCG")
            print(f"\U0001f4cf 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("\U0001f4dd 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("\U0001f527 Initializing formulaic token engine...")
            token_payload = TokenGenerator().generate(inputs)
            token_count = _count_tokens(token_payload)
            baseline_emu = token_payload["typography"]["baseline_emu"]

            print(f"\u2705 Generated {token_count:,} design tokens")
            print(f"\U0001f4cf Baseline grid: {baseline_emu:,} EMU")

        if token_payload is None:
            raise SystemExit(
                "Error: no token source specified (use --tokens, --layers-dir, or --primary-color)"
            )

        # Build a lightweight document IR for diagnostics (color analysis).
        # This does NOT go through BuildPipeline — it is a cheap IR-only pass
        # used to print palette QA info on the console.
        document_ir = build_document_ir(token_payload, args.org_id, locale)
        if document_ir.color_analysis:
            palette = document_ir.color_analysis.palette_summary
            print("\U0001f3af 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("   \u26a0\ufe0f Color QA Warnings:")
                for warning in warnings:
                    print(f"      \u2022 {warning}")
            else:
                print("   \u2705 No color QA warnings detected.")
            previews = document_ir.color_analysis.previews or {}
            if previews:
                print("   \U0001f3a8 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("\U0001f4be 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
        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"   \u2022 {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"   \u2022 {json_path}")
            print(f"   \u2022 {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("\U0001f4c4 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", "\U0001f4ca"),
                    "dotx": ("document", "\U0001f4dd"),
                    "xltx": ("workbook", "\U0001f4c8"),
                    "ott": ("document", "\U0001f4c4"),
                    "otp": ("presentation", "\U0001f4fd"),
                    "ots": ("spreadsheet", "\U0001f4ca"),
                    "odg": ("drawing", "\U0001f3a8"),
                }
                for fmt, data in pipeline_result.artifacts.items():
                    name, icon = _FMT_NAMES.get(fmt, (fmt, "\U0001f4c4"))
                    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"   \u26a0 {fmt}: {len(issues)} validation issues")

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

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

            print("\U0001f9ea 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"   \u2022 {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"\U0001f4cb Summary report: {summary_path}")

        print()
        print("\U0001f389 TokenMoulds generation completed successfully!")

    except Exception as e:
        print(f"\u274c 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: Any,
    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")
