"""Brand management MCP tools for TokenMoulds.

Provides tools for:
- Extracting brand from URL (CSS → Design Tokens)
- Building recipes from minimal inputs
- Loading/managing brand tokens
- Switching between brands
"""

from __future__ import annotations

import asyncio
import json
from pathlib import Path
from typing import Any, Dict, TYPE_CHECKING


if TYPE_CHECKING:
    from tokenmoulds.mcp.server import TokenMouldsServer


# ============================================================================
# JSON Schemas for MCP Tools
# ============================================================================

EXTRACT_BRAND_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "url": {
            "type": "string",
            "description": "Website URL to extract brand from",
        },
        "brand_name": {
            "type": "string",
            "description": "Brand identifier (optional, auto-detected from site)",
        },
        "save_tokens": {
            "type": "boolean",
            "default": True,
            "description": "Save extracted tokens to cache",
        },
    },
    "required": ["url"],
}

BUILD_RECIPE_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "primary_color": {
            "type": "string",
            "description": "Primary brand color (hex, e.g., #2563EB)",
        },
        "secondary_color": {
            "type": "string",
            "description": "Secondary/accent color (hex, e.g., #DC2626)",
        },
        "font_sans": {
            "type": "string",
            "description": "Sans-serif font family name (e.g., Inter)",
        },
        "font_serif": {
            "type": "string",
            "description": "Serif font family name (e.g., Georgia)",
        },
        "brand_name": {
            "type": "string",
            "description": "Brand identifier for caching",
        },
        "baseline_pt": {
            "type": "number",
            "default": 11.0,
            "description": "Base font size in points",
        },
        "type_scale_ratio": {
            "type": "number",
            "default": 1.25,
            "description": "Modular scale ratio (1.25=major third, 1.333=perfect fourth)",
        },
        "brand_tone": {
            "type": "number",
            "default": 0.5,
            "description": "Brand tone: 0=formal, 1=casual",
        },
        "content_density": {
            "type": "number",
            "default": 0.4,
            "description": "Content density: 0=sparse, 1=dense",
        },
        "locale": {
            "type": "string",
            "default": "US",
            "description": "Locale for paper size and formatting (US, GB, DE, NL, etc.)",
        },
        "paper_size": {
            "type": "string",
            "default": "auto",
            "description": "Paper size: auto, letter, a4, legal, tabloid",
        },
        "document_type": {
            "type": "string",
            "default": "report",
            "description": "Document type: letter, memo, report, book, newsletter, article",
        },
        "wcag_level": {
            "type": "string",
            "enum": ["AA", "AAA"],
            "default": "AA",
            "description": "WCAG accessibility level",
        },
        "save_recipe": {
            "type": "boolean",
            "default": True,
            "description": "Save recipe to cache",
        },
    },
    "required": [
        "primary_color",
        "secondary_color",
        "font_sans",
        "font_serif",
        "brand_name",
    ],
}

LOAD_BRAND_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "path": {
            "type": "string",
            "description": "Path to tokens directory",
        },
        "brand_name": {
            "type": "string",
            "description": "Brand identifier (optional, uses directory name)",
        },
        "set_active": {
            "type": "boolean",
            "default": True,
            "description": "Set as active brand after loading",
        },
    },
    "required": ["path"],
}

LIST_BRANDS_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {},
}

SET_ACTIVE_BRAND_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "brand_name": {
            "type": "string",
            "description": "Brand identifier to set as active",
        },
    },
    "required": ["brand_name"],
}


# ============================================================================
# Tool Implementations
# ============================================================================


async def extract_brand_from_url(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Extract brand from URL and generate DTCG tokens.

    Full pipeline: URL → CSS parsing → Brand extraction → Recipe → DTCG tokens

    Args:
        arguments: Tool arguments from MCP call
        server: TokenMouldsServer instance

    Returns:
        Result dictionary with extracted brand info and token paths
    """
    url = arguments.get("url")
    if not url:
        return {"error": "Missing required 'url' parameter"}

    brand_name = arguments.get("brand_name")
    save_tokens = arguments.get("save_tokens", True)

    try:
        # Import brand extraction module
        from tokenmoulds.brand_from_url import brand_from_url
        from tokenmoulds.dtcg.emitter import emit_tokens_to_string

        # Run extraction in thread pool (blocking I/O)
        loop = asyncio.get_running_loop()
        brand_templates = await loop.run_in_executor(None, brand_from_url, url)

        # Use extracted name if not provided
        if not brand_name:
            brand_name = brand_templates.brand.name

        # Get tokens as dict
        from tokenmoulds.dtcg.recipe import build_recipe

        recipe = build_recipe(brand_templates.recipe_inputs)
        tokens_json = emit_tokens_to_string(recipe)
        tokens_dict = json.loads(tokens_json)

        # Save to cache if requested
        tokens_path = None
        if save_tokens:
            cache = server.cache_manager
            tokens_path = cache.save_tokens(brand_name, tokens_dict)

            # Store in server's loaded tokens
            server.loaded_tokens[brand_name] = tokens_dict
            server.active_brand = brand_name

        return {
            "success": True,
            "action": "extract_brand_from_url",
            "url": url,
            "brand_name": brand_name,
            "extracted": {
                "primary_color": brand_templates.brand.primary_color,
                "secondary_color": brand_templates.brand.secondary_color,
                "heading_font": brand_templates.brand.heading_font,
                "body_font": brand_templates.brand.body_font,
                "text_color": brand_templates.brand.text_color,
                "locale": brand_templates.brand.locale,
                "brand_tone": brand_templates.brand.brand_tone,
                "type_scale_ratio": brand_templates.brand.type_scale_ratio,
            },
            "all_colors_count": len(brand_templates.brand.all_colors),
            "all_fonts_count": len(brand_templates.brand.all_fonts),
            "tokens_saved": save_tokens,
            "tokens_path": str(tokens_path) if tokens_path else None,
            "active_brand": server.active_brand,
            "message": f"Extracted brand '{brand_name}' from {url}. Full DTCG tokens generated with derivation formulas.",
        }

    except Exception as e:
        return {
            "error": f"Failed to extract brand from URL: {str(e)}",
            "url": url,
            "suggestion": "Check if the URL is accessible and has extractable CSS. Try using build_recipe_from_inputs with manual values instead.",
        }


async def build_recipe_from_inputs(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Build DTCG recipe from minimal design inputs.

    Creates a complete token system from just colors, fonts, and parameters.

    Args:
        arguments: Tool arguments from MCP call
        server: TokenMouldsServer instance

    Returns:
        Result dictionary with recipe info and token paths
    """
    # Validate required fields
    required = [
        "primary_color",
        "secondary_color",
        "font_sans",
        "font_serif",
        "brand_name",
    ]
    for field in required:
        if field not in arguments:
            return {"error": f"Missing required parameter: {field}"}

    brand_name = arguments["brand_name"]
    save_recipe = arguments.get("save_recipe", True)

    try:
        from tokenmoulds.dtcg.generator import TokenGenerator

        # Map accessibility level to contrast ratio
        accessibility_level = arguments.get("wcag_level", "AA")

        # Build inputs for TokenGenerator (produces emitter-ready format)
        generator_inputs = {
            "font_pair": {
                "major": arguments["font_sans"],
                "minor": arguments["font_sans"],
                "serif": arguments["font_serif"],
            },
            "base_colors": {
                "primary_color": arguments["primary_color"],
                "secondary_color": arguments["secondary_color"],
                "text_color": "#333333",
            },
            "locale": arguments.get("locale", "US"),
            "brand_tone": arguments.get("brand_tone", 0.5),
            "content_density": arguments.get("content_density", 0.4),
            "accessibility_level": accessibility_level,
        }

        # Generate tokens in emitter-ready format
        tokens_dict = TokenGenerator().generate(generator_inputs)

        # Count tokens
        def count_tokens(obj: Any) -> int:
            if isinstance(obj, dict):
                count = 0
                if "$value" in obj:
                    count = 1
                for v in obj.values():
                    count += count_tokens(v)
                return count
            elif isinstance(obj, list):
                return sum(count_tokens(item) for item in obj)
            return 0

        token_count = count_tokens(tokens_dict)

        # Save to cache if requested
        tokens_path = None
        if save_recipe:
            cache = server.cache_manager
            tokens_path = cache.save_tokens(brand_name, tokens_dict)

            # Invalidate any cached templates (tokens changed)
            cache.invalidate(brand_name)

            # Store in server's loaded tokens
            server.loaded_tokens[brand_name] = tokens_dict
            server.active_brand = brand_name

        return {
            "success": True,
            "action": "build_recipe_from_inputs",
            "brand_name": brand_name,
            "inputs": {
                "primary_color": arguments["primary_color"],
                "secondary_color": arguments["secondary_color"],
                "font_sans": arguments["font_sans"],
                "font_serif": arguments["font_serif"],
                "locale": arguments.get("locale", "US"),
                "wcag_level": arguments.get("wcag_level", "AA"),
            },
            "token_count": token_count,
            "tokens_saved": save_recipe,
            "tokens_path": str(tokens_path) if tokens_path else None,
            "active_brand": server.active_brand,
            "message": f"Built tokens for '{brand_name}' with {token_count} design tokens.",
        }

    except Exception as e:
        return {
            "error": f"Failed to build recipe: {str(e)}",
            "brand_name": brand_name,
        }


async def load_brand(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Load existing tokens from a directory.

    Args:
        arguments: Tool arguments from MCP call
        server: TokenMouldsServer instance

    Returns:
        Result dictionary with load status
    """
    path_str = arguments.get("path")
    if not path_str:
        return {"error": "Missing required 'path' parameter"}

    path = Path(path_str)
    if not path.exists():
        return {"error": f"Directory not found: {path}"}

    brand_name = arguments.get("brand_name", path.name)
    set_active = arguments.get("set_active", True)

    try:
        # Look for token files
        token_file = None

        # DTCG format first
        dtcg_files = list(path.glob("*.tokens.json"))
        if dtcg_files:
            token_file = dtcg_files[0]
        else:
            # Legacy format
            legacy_files = list(path.glob("*-design-tokens.json"))
            if legacy_files:
                token_file = legacy_files[0]

        if token_file is None:
            return {
                "error": f"No token files found in {path}",
                "suggestion": "Directory should contain *.tokens.json (DTCG) or *-design-tokens.json files",
            }

        # Load tokens
        with open(token_file, "r", encoding="utf-8") as f:
            tokens = json.load(f)

        # Store in server
        server.loaded_tokens[brand_name] = tokens

        if set_active:
            server.active_brand = brand_name

        # Save to cache
        cache = server.cache_manager
        cache.save_tokens(brand_name, tokens)

        # Detect format
        is_dtcg = (
            any(key.startswith("$") for key in tokens.keys()) or "inputs" in tokens
        )

        return {
            "success": True,
            "action": "load_brand",
            "brand_name": brand_name,
            "path": str(path),
            "token_file": str(token_file),
            "format": "dtcg" if is_dtcg else "legacy",
            "active_brand": server.active_brand,
            "loaded_brands": list(server.loaded_tokens.keys()),
            "message": f"Loaded brand '{brand_name}' from {token_file.name}",
        }

    except Exception as e:
        return {
            "error": f"Failed to load brand: {str(e)}",
            "path": str(path),
        }


async def list_brands(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """List all loaded brands.

    Args:
        arguments: Tool arguments from MCP call
        server: TokenMouldsServer instance

    Returns:
        Result dictionary with brand list
    """
    brands_info = []

    for brand_name, tokens in server.loaded_tokens.items():
        # Get basic info from tokens
        is_dtcg = (
            any(key.startswith("$") for key in tokens.keys()) or "inputs" in tokens
        )

        info = {
            "name": brand_name,
            "format": "dtcg" if is_dtcg else "legacy",
            "is_active": brand_name == server.active_brand,
        }

        # Try to extract colors from tokens
        if is_dtcg and "inputs" in tokens:
            inputs = tokens.get("inputs", {})
            if "color" in inputs:
                color_inputs = inputs["color"]
                if "primary" in color_inputs:
                    primary = color_inputs["primary"]
                    info["primary_color"] = (
                        primary.get("$value", primary)
                        if isinstance(primary, dict)
                        else str(primary)
                    )
                if "secondary" in color_inputs:
                    secondary = color_inputs["secondary"]
                    info["secondary_color"] = (
                        secondary.get("$value", secondary)
                        if isinstance(secondary, dict)
                        else str(secondary)
                    )

        # Get cache info
        cache_info = server.cache_manager.get_brand_info(brand_name)
        if cache_info:
            info["cached_templates"] = list(cache_info.get("templates", {}).keys())

        brands_info.append(info)

    # Also list cached brands not currently loaded
    cached_brands = server.cache_manager.list_brands()
    for cached_name in cached_brands:
        if cached_name not in server.loaded_tokens:
            cache_info = server.cache_manager.get_brand_info(cached_name)
            brands_info.append(
                {
                    "name": cached_name,
                    "format": "cached",
                    "is_active": False,
                    "cached_templates": list(cache_info.get("templates", {}).keys())
                    if cache_info
                    else [],
                    "note": "Tokens in cache but not loaded",
                }
            )

    return {
        "success": True,
        "action": "list_brands",
        "active_brand": server.active_brand,
        "loaded_count": len(server.loaded_tokens),
        "cached_count": len(cached_brands),
        "brands": brands_info,
    }


async def set_active_brand(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Set the active brand for generation.

    Args:
        arguments: Tool arguments from MCP call
        server: TokenMouldsServer instance

    Returns:
        Result dictionary with activation status
    """
    brand_name = arguments.get("brand_name")
    if not brand_name:
        return {"error": "Missing required 'brand_name' parameter"}

    # Check if brand is loaded
    if brand_name not in server.loaded_tokens:
        # Try to load from cache
        cached_tokens = server.cache_manager.load_tokens(brand_name)
        if cached_tokens:
            server.loaded_tokens[brand_name] = cached_tokens
        else:
            return {
                "error": f"Brand '{brand_name}' not found",
                "available_brands": list(server.loaded_tokens.keys()),
                "suggestion": "Use load_brand to load tokens from a directory, or extract_brand_from_url to create new tokens",
            }

    server.active_brand = brand_name

    return {
        "success": True,
        "action": "set_active_brand",
        "active_brand": server.active_brand,
        "loaded_brands": list(server.loaded_tokens.keys()),
        "message": f"Active brand set to '{brand_name}'",
    }
