"""User context MCP tools for TokenMoulds.

Provides tools for:
- Setting user context (email, organization)
- Inferring brand from email domain (fetches website CSS)
- Getting current user context
"""

from __future__ import annotations

import asyncio
import re
from typing import Any, Dict, Optional, TYPE_CHECKING

if TYPE_CHECKING:
    from tokenmoulds.mcp.server import TokenMouldsServer


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

SET_USER_CONTEXT_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "email": {
            "type": "string",
            "description": "User's email address (used to infer organization domain)",
        },
        "organization": {
            "type": "string",
            "description": "Organization name (optional, inferred from email if not provided)",
        },
        "auto_infer_brand": {
            "type": "boolean",
            "default": True,
            "description": "Automatically fetch brand from organization's website",
        },
    },
    "required": ["email"],
}

INFER_BRAND_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "email": {
            "type": "string",
            "description": "Email address to extract domain from (e.g., user@democorp.com)",
        },
        "domain": {
            "type": "string",
            "description": "Domain to fetch brand from (alternative to email)",
        },
        "protocol": {
            "type": "string",
            "enum": ["https", "http"],
            "default": "https",
            "description": "Protocol to use when fetching website",
        },
    },
}

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


# ============================================================================
# Helper Functions
# ============================================================================


def extract_domain_from_email(email: str) -> Optional[str]:
    """Extract domain from email address.

    Args:
        email: Email address (e.g., user@democorp.com)

    Returns:
        Domain string (e.g., democorp.com) or None if invalid
    """
    if not email or "@" not in email:
        return None

    # Extract domain part
    parts = email.split("@")
    if len(parts) != 2:
        return None

    domain = parts[1].strip().lower()

    # Basic validation
    if not domain or "." not in domain:
        return None

    return domain


def extract_brand_name_from_domain(domain: str) -> str:
    """Extract brand name from domain.

    Args:
        domain: Domain string (e.g., democorp.com)

    Returns:
        Brand name (e.g., democorp)
    """
    # Remove common TLDs and subdomains
    # democorp.com -> democorp
    # www.democorp.co.uk -> democorp

    parts = domain.split(".")

    # Filter out common prefixes
    prefixes_to_skip = {"www", "mail", "email", "app", "portal"}
    parts = [p for p in parts if p not in prefixes_to_skip]

    # Filter out common TLDs (keep the main name)
    tlds = {"com", "org", "net", "io", "co", "uk", "de", "nl", "fr", "eu", "gov", "edu"}

    # Find the main brand name (usually first non-TLD part)
    for part in parts:
        if part not in tlds and len(part) > 1:
            # Clean up - alphanumeric and hyphens only
            clean = re.sub(r"[^a-z0-9-]", "", part.lower())
            if clean:
                return clean

    # Fallback to first part
    return parts[0] if parts else "unknown"


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


async def set_user_context(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Set user context and optionally infer brand from email domain.

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

    Returns:
        Result dictionary with user context and optional brand info
    """
    email = arguments.get("email")
    if not email:
        return {"error": "Missing required 'email' parameter"}

    # Extract domain
    domain = extract_domain_from_email(email)
    if not domain:
        return {
            "error": "Invalid email format",
            "email": email,
            "suggestion": "Email should be in format user@domain.com",
        }

    # Extract brand name
    brand_name = extract_brand_name_from_domain(domain)
    organization = arguments.get("organization", brand_name)

    # Store user context
    server.user_context = {
        "email": email,
        "domain": domain,
        "organization": organization,
        "brand_name": brand_name,
    }

    result = {
        "success": True,
        "action": "set_user_context",
        "email": email,
        "domain": domain,
        "organization": organization,
        "inferred_brand": brand_name,
    }

    # Auto-infer brand from website if requested
    auto_infer = arguments.get("auto_infer_brand", True)
    if auto_infer:
        infer_result = await infer_brand_from_email(
            {"domain": domain},
            server,
        )

        if infer_result.get("success"):
            result["brand_extracted"] = True
            result["brand_info"] = infer_result.get("extracted")
            result["applied"] = infer_result.get("applied")
            result["active_brand"] = server.active_brand
            result["message"] = (
                f"User context set. Brand '{brand_name}' extracted from {domain}"
            )
        else:
            result["brand_extracted"] = False
            result["brand_error"] = infer_result.get("error")
            result["message"] = (
                f"User context set. Could not extract brand from {domain}: {infer_result.get('error')}"
            )
            result["suggestion"] = (
                "Use build_recipe_from_inputs to manually create brand tokens"
            )
    else:
        result["brand_extracted"] = False
        result["message"] = (
            f"User context set. Use infer_brand_from_email to extract brand from {domain}"
        )

    return result


async def infer_brand_from_email(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Infer brand by fetching website CSS from email domain.

    Extracts domain from email, fetches the website, parses CSS,
    and creates design tokens for the brand.

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

    Returns:
        Result dictionary with extracted brand info
    """
    # Get domain from arguments or email
    domain = arguments.get("domain")
    email = arguments.get("email")

    if not domain and email:
        domain = extract_domain_from_email(email)

    if not domain:
        return {
            "error": "Missing domain. Provide 'domain' or 'email' parameter",
            "suggestion": "Use domain='example.com' or email='user@example.com'",
        }

    # Build URL
    protocol = arguments.get("protocol", "https")
    url = f"{protocol}://{domain}"

    # Extract brand name
    brand_name = extract_brand_name_from_domain(domain)

    try:
        # Import brand extraction (same as extract_brand_from_url)
        from tokenmoulds.brand_from_url import brand_from_url
        from tokenmoulds.dtcg.generator import TokenGenerator

        # 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)

        # Build emitter-ready tokens using TokenGenerator
        # Map extracted brand to generator inputs

        # Import font resolution
        from tokenmoulds.mcp.fonts import (
            clean_font_value,
            resolve_brand_fonts,
        )

        # Clean and resolve fonts with embeddable fallbacks
        raw_heading = clean_font_value(brand_templates.brand.heading_font)
        raw_body = clean_font_value(brand_templates.brand.body_font)

        font_compliance = resolve_brand_fonts(raw_heading, raw_body)
        heading_font = font_compliance.fonts["heading"].resolved
        body_font = font_compliance.fonts["body"].resolved

        # Clean up colors - ensure valid hex
        def clean_color(color: Optional[str], fallback: str) -> str:
            if not color:
                return fallback
            if color.startswith("#") and len(color) in (7, 9):
                return color
            return fallback

        primary = clean_color(brand_templates.brand.primary_color, "#2563EB")
        secondary = clean_color(brand_templates.brand.secondary_color, "#DC2626")
        text_color = clean_color(brand_templates.brand.text_color, "#333333")

        generator_inputs = {
            "font_pair": {
                "major": heading_font,
                "minor": heading_font,
                "serif": body_font,
            },
            "base_colors": {
                "primary": primary,
                "secondary": secondary,
            },
            "locale": brand_templates.brand.locale or "US",
            "brand_tone": brand_templates.brand.brand_tone or 0.5,
            "content_density": 0.4,
            "accessibility_level": "AA",
        }

        tokens_dict = TokenGenerator().generate(generator_inputs)

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

        # Store in server
        server.loaded_tokens[brand_name] = tokens_dict
        server.active_brand = brand_name

        # Update user context if it exists
        if hasattr(server, "user_context") and server.user_context:
            server.user_context["brand_name"] = brand_name
            server.user_context["brand_loaded"] = True

        return {
            "success": True,
            "action": "infer_brand_from_email",
            "email": email,
            "domain": domain,
            "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,
            },
            "applied": {
                "primary_color": primary,
                "secondary_color": secondary,
                "heading_font": heading_font,
                "body_font": body_font,
                "text_color": text_color,
            },
            "all_colors_found": len(brand_templates.brand.all_colors),
            "all_fonts_found": len(brand_templates.brand.all_fonts),
            "tokens_path": str(tokens_path) if tokens_path else None,
            "active_brand": server.active_brand,
            "message": f"Brand '{brand_name}' extracted from {url}. Ready to generate templates.",
        }

    except Exception as e:
        error_msg = str(e)

        # Provide helpful suggestions based on error type
        suggestion = (
            "Use build_recipe_from_inputs to manually specify brand colors and fonts."
        )

        if "connection" in error_msg.lower() or "timeout" in error_msg.lower():
            suggestion = f"Could not connect to {url}. Check if the website is accessible, or use build_recipe_from_inputs with manual values."
        elif "ssl" in error_msg.lower() or "certificate" in error_msg.lower():
            suggestion = f"SSL error for {url}. Try protocol='http' or use build_recipe_from_inputs with manual values."
        elif "404" in error_msg or "not found" in error_msg.lower():
            suggestion = f"Website {url} not found. Check the domain or use build_recipe_from_inputs with manual values."

        return {
            "error": f"Failed to extract brand from {url}: {error_msg}",
            "domain": domain,
            "brand_name": brand_name,
            "suggestion": suggestion,
            "fallback": {
                "tool": "build_recipe_from_inputs",
                "example": {
                    "brand_name": brand_name,
                    "primary_color": "#2563EB",
                    "secondary_color": "#DC2626",
                    "font_sans": "Inter",
                    "font_serif": "Georgia",
                },
            },
        }


async def get_user_context(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Get current user context.

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

    Returns:
        Result dictionary with current user context
    """
    if not hasattr(server, "user_context") or not server.user_context:
        return {
            "success": True,
            "action": "get_user_context",
            "user_context": None,
            "message": "No user context set. Use set_user_context with your email to initialize.",
        }

    context = server.user_context.copy()
    context["active_brand"] = server.active_brand
    context["loaded_brands"] = list(server.loaded_tokens.keys())

    return {
        "success": True,
        "action": "get_user_context",
        "user_context": context,
    }
