"""Template generation MCP tools for TokenMoulds.

Provides tools for generating all template formats:
- Word (.dotx)
- PowerPoint (.potx)
- Excel (.xltx)
- LibreOffice Writer (.ott)
- LibreOffice Impress (.otp)
- Google Docs (.dotx)
- Office Theme (.thmx)

All templates are generated through the full TokenMoulds pipeline with:
- Font embedding (always enabled)
- ISO 29500 compliance validation
- WCAG accessibility
- Full style coverage (276 Word styles, etc.)
"""

from __future__ import annotations

import asyncio
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional

from tokenmoulds.mcp.cache import TemplateFormat
from tokenmoulds.mcp.tools.token_utils import extract_token_payload
from tokenmoulds.mcp.validation import validate_template

if TYPE_CHECKING:
    from tokenmoulds.mcp.server import TokenMouldsServer


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

GENERATE_WORD_TEMPLATE_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "brand": {
            "type": "string",
            "description": "Brand identifier (optional, uses active brand)",
        },
        "filename": {
            "type": "string",
            "description": "Output filename without extension",
        },
        "embed_fonts": {
            "type": "boolean",
            "default": True,
            "description": "Embed fonts in template (always enabled for production)",
        },
        "validate": {
            "type": "boolean",
            "default": True,
            "description": "Validate for ISO 29500 compliance",
        },
        "use_cache": {
            "type": "boolean",
            "default": True,
            "description": "Use cached template if available",
        },
    },
}

GENERATE_POWERPOINT_TEMPLATE_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "brand": {
            "type": "string",
            "description": "Brand identifier (optional, uses active brand)",
        },
        "filename": {
            "type": "string",
            "description": "Output filename without extension",
        },
        "embed_fonts": {
            "type": "boolean",
            "default": True,
            "description": "Embed fonts in template",
        },
        "validate": {
            "type": "boolean",
            "default": True,
            "description": "Validate for ISO 29500 compliance",
        },
        "use_cache": {
            "type": "boolean",
            "default": True,
            "description": "Use cached template if available",
        },
    },
}

GENERATE_EXCEL_TEMPLATE_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "brand": {
            "type": "string",
            "description": "Brand identifier (optional, uses active brand)",
        },
        "filename": {
            "type": "string",
            "description": "Output filename without extension",
        },
        "embed_fonts": {
            "type": "boolean",
            "default": True,
            "description": "Embed fonts in template",
        },
        "validate": {
            "type": "boolean",
            "default": True,
            "description": "Validate for ISO 29500 compliance",
        },
        "use_cache": {
            "type": "boolean",
            "default": True,
            "description": "Use cached template if available",
        },
    },
}

GENERATE_WRITER_TEMPLATE_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "brand": {
            "type": "string",
            "description": "Brand identifier (optional, uses active brand)",
        },
        "filename": {
            "type": "string",
            "description": "Output filename without extension",
        },
        "embed_fonts": {
            "type": "boolean",
            "default": True,
            "description": "Embed fonts in template",
        },
        "validate": {
            "type": "boolean",
            "default": True,
            "description": "Validate for ODF compliance",
        },
        "use_cache": {
            "type": "boolean",
            "default": True,
            "description": "Use cached template if available",
        },
    },
}

GENERATE_IMPRESS_TEMPLATE_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "brand": {
            "type": "string",
            "description": "Brand identifier (optional, uses active brand)",
        },
        "filename": {
            "type": "string",
            "description": "Output filename without extension",
        },
        "embed_fonts": {
            "type": "boolean",
            "default": True,
            "description": "Embed fonts in template",
        },
        "validate": {
            "type": "boolean",
            "default": True,
            "description": "Validate for ODF compliance",
        },
        "use_cache": {
            "type": "boolean",
            "default": True,
            "description": "Use cached template if available",
        },
    },
}

GENERATE_GOOGLE_DOCS_TEMPLATE_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "brand": {
            "type": "string",
            "description": "Brand identifier (optional, uses active brand)",
        },
        "filename": {
            "type": "string",
            "description": "Output filename without extension",
        },
        "use_cache": {
            "type": "boolean",
            "default": True,
            "description": "Use cached template if available",
        },
    },
}

GENERATE_THEME_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "brand": {
            "type": "string",
            "description": "Brand identifier (optional, uses active brand)",
        },
        "filename": {
            "type": "string",
            "description": "Output filename without extension",
        },
        "embed_fonts": {
            "type": "boolean",
            "default": True,
            "description": "Embed fonts in theme",
        },
        "use_cache": {
            "type": "boolean",
            "default": True,
            "description": "Use cached theme if available",
        },
    },
}

GENERATE_ALL_TEMPLATES_SCHEMA: Dict[str, Any] = {
    "type": "object",
    "properties": {
        "brand": {
            "type": "string",
            "description": "Brand identifier (optional, uses active brand)",
        },
        "formats": {
            "type": "array",
            "items": {
                "type": "string",
                "enum": [
                    "word",
                    "powerpoint",
                    "excel",
                    "writer",
                    "impress",
                    "google_docs",
                    "theme",
                ],
            },
            "description": "Formats to generate (default: all)",
        },
        "embed_fonts": {
            "type": "boolean",
            "default": True,
            "description": "Embed fonts in templates",
        },
        "validate": {
            "type": "boolean",
            "default": True,
            "description": "Validate all templates",
        },
        "use_cache": {
            "type": "boolean",
            "default": True,
            "description": "Use cached templates if available",
        },
    },
}


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


def _get_brand_tokens(
    server: "TokenMouldsServer",
    brand_name: Optional[str],
) -> tuple[str, Dict[str, Any]]:
    """Get tokens for a brand.

    Args:
        server: TokenMouldsServer instance
        brand_name: Brand name or None for active brand

    Returns:
        Tuple of (brand_name, tokens_dict)

    Raises:
        ValueError: If no brand is available
    """
    if brand_name is None:
        brand_name = server.active_brand

    if brand_name is None:
        raise ValueError(
            "No active brand. Use extract_brand_from_url or build_recipe_from_inputs first."
        )

    if brand_name not in server.loaded_tokens:
        # Try cache
        cached = server.cache_manager.load_tokens(brand_name)
        if cached:
            server.loaded_tokens[brand_name] = cached
        else:
            raise ValueError(
                f"Brand '{brand_name}' not loaded. Available: {list(server.loaded_tokens.keys())}"
            )

    return brand_name, server.loaded_tokens[brand_name]


def _build_document_ir(tokens: Dict[str, Any], brand_name: str, locale: str = "US"):
    """Build DocumentIR from tokens."""
    from tokenmoulds.ir import build_document_ir

    token_payload = extract_token_payload(tokens)
    return build_document_ir(token_payload, brand_name, locale)


# ============================================================================
# Template Generation – shared helper and format-specific config
# ============================================================================


@dataclass
class _FormatConfig:
    """Per-format parameters consumed by :func:`_generate_template`."""

    template_format: TemplateFormat
    file_extension: str
    action_name: str
    error_label: str
    success_message: str
    cached_message: str
    validation_type: Optional[str] = None
    supports_embed_fonts: bool = True
    # Callable(document_ir, tokens, embed_fonts, server, validate_output) ->
    # (bytes, extra_response, extra_cache)
    emitter_factory: Callable[..., tuple[bytes, Dict[str, Any], Dict[str, Any]]] = None  # type: ignore[assignment]
    # Extra static fields merged into the cached-hit response
    cached_extra: Dict[str, Any] = field(default_factory=dict)
    # Filename builder override: receives (filename | None, brand_name) -> stem
    filename_stem: Optional[Callable[[Optional[str], str], str]] = None


def _default_filename_stem(filename: Optional[str], brand_name: str) -> str:
    return filename if filename else brand_name


async def _generate_template(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
    config: _FormatConfig,
) -> Dict[str, Any]:
    """Core template generation pipeline shared by all format functions.

    Steps: get brand tokens -> check cache -> build IR in executor ->
    emitter_factory -> optionally validate -> cache -> save -> return result.
    """
    try:
        brand_name, tokens = _get_brand_tokens(server, arguments.get("brand"))
    except ValueError as e:
        return {"error": str(e)}

    embed_fonts = (
        arguments.get("embed_fonts", True) if config.supports_embed_fonts else False
    )
    do_validate = arguments.get("validate", True) if config.validation_type else False
    use_cache = arguments.get("use_cache", True)
    filename = arguments.get("filename")

    stem_fn = config.filename_stem or _default_filename_stem
    file_stem = stem_fn(filename, brand_name)

    cache = server.cache_manager
    token_hash = cache.compute_token_hash(tokens)

    # -- Cache hit --
    if use_cache:
        cached = cache.get_template(brand_name, config.template_format, token_hash)
        if cached:
            output_path = server.output_path / f"{file_stem}{config.file_extension}"
            output_path.parent.mkdir(parents=True, exist_ok=True)
            output_path.write_bytes(cached)

            result: Dict[str, Any] = {
                "success": True,
                "action": config.action_name,
                "brand": brand_name,
                "file_path": str(output_path),
                "cached": True,
                "message": config.cached_message,
            }
            result.update(config.cached_extra)
            return result

    # -- Build via BuildPipeline --
    try:
        from tokenmoulds.pipeline import (
            BuildConfig,
            BuildResult,
            build as build_pipeline,
        )

        # Convert TemplateFormat enum to pipeline extension string
        fmt_ext = config.file_extension.lstrip(".")  # e.g. ".dotx" → "dotx"

        def _run_pipeline() -> BuildResult:
            pipeline_config = BuildConfig(
                org_id=brand_name,
                output_formats=[fmt_ext],
                token_payload=tokens,
                embed_fonts=embed_fonts,
                validate=False,  # MCP does its own validation below
                fonts_dir=server.fonts_dir,
            )
            return build_pipeline(pipeline_config)

        loop = asyncio.get_running_loop()
        pipeline_result = await loop.run_in_executor(None, _run_pipeline)

        if not pipeline_result.success:
            return {"error": pipeline_result.error or "Build failed"}

        template_bytes = pipeline_result.artifacts.get(fmt_ext, b"")
        document_ir = pipeline_result.document_ir
        extra_response: Dict[str, Any] = {}
        extra_cache: Dict[str, Any] = {}

        # Validate
        validation_status = "skipped"
        iso_compliant = False
        if do_validate and config.validation_type:
            vr = validate_template(template_bytes, config.validation_type)
            validation_status = "passed" if vr.valid else "failed"
            iso_compliant = vr.iso_compliant

        # Embedded fonts list
        fonts_embedded: List[str] = []
        if embed_fonts:
            fonts_embedded = list(
                {
                    document_ir.typography.body_font,
                    document_ir.typography.heading_font,
                }
            )

        # Cache result
        cache_kwargs: Dict[str, Any] = {}
        if fonts_embedded:
            cache_kwargs["fonts_embedded"] = fonts_embedded
        if do_validate:
            cache_kwargs["validation_status"] = validation_status
            cache_kwargs["iso_compliant"] = iso_compliant
        cache_kwargs.update(extra_cache)

        cache.set_template(
            brand_name,
            config.template_format,
            template_bytes,
            token_hash,
            **cache_kwargs,
        )

        # Save to output
        output_path = server.output_path / f"{file_stem}{config.file_extension}"
        output_path.parent.mkdir(parents=True, exist_ok=True)
        output_path.write_bytes(template_bytes)

        result = {
            "success": True,
            "action": config.action_name,
            "brand": brand_name,
            "file_path": str(output_path),
            "cached": False,
            "size_bytes": len(template_bytes),
            "message": config.success_message,
        }
        if fonts_embedded:
            result["fonts_embedded"] = fonts_embedded
        if do_validate:
            result["validation_status"] = validation_status
            result["iso_compliant"] = iso_compliant
        result.update(extra_response)
        return result

    except Exception as e:
        return {
            "error": f"Failed to generate {config.error_label}: {str(e)}",
            "brand": brand_name,
        }


# ============================================================================
# Per-format emitter factories
# ============================================================================


def _word_emitter(document_ir, tokens, embed_fonts, server, validate_output):
    from tokenmoulds.emitters import WordDocumentEmitter
    from tokenmoulds.styles import create_style_resolver_from_tokens

    token_payload = extract_token_payload(tokens)
    style_resolver = create_style_resolver_from_tokens(
        token_payload,
        body_color=(
            document_ir.body_style.color if document_ir.body_style else "#000000"
        ),
        heading_color=(
            document_ir.heading_styles[0].color
            if document_ir.heading_styles
            else "#000000"
        ),
    )
    emitter = WordDocumentEmitter(
        document_ir,
        template_root=None,
        embed_fonts=embed_fonts,
        fonts_dir=server.fonts_dir,
        style_resolver=style_resolver,
    )
    template_bytes = emitter.build_package(
        validate=validate_output,
        validation_strict=True,
    )
    styles_count = len(style_resolver.resolve_all())
    extra_response = {
        "styles_count": styles_count,
        "message": f"Word template generated with {styles_count} themed styles. Zero Office defaults.",
    }
    extra_cache = {"styles_count": styles_count}
    return template_bytes, extra_response, extra_cache


def _powerpoint_emitter(document_ir, tokens, embed_fonts, server, validate_output):
    from tokenmoulds.emitters import PowerPointEmitter

    emitter = PowerPointEmitter(
        document_ir,
        template_root=None,
        embed_fonts=embed_fonts,
        fonts_dir=server.fonts_dir,
    )
    return (
        emitter.build_package(
            validate=validate_output,
            validation_strict=True,
        ),
        {},
        {},
    )


def _excel_emitter(document_ir, tokens, embed_fonts, server, validate_output):
    from tokenmoulds.emitters import ExcelEmitter

    emitter = ExcelEmitter(
        document_ir,
        template_root=None,
    )
    return (
        emitter.build_package(
            validate=validate_output,
            validation_strict=True,
        ),
        {},
        {},
    )


def _writer_emitter(document_ir, tokens, embed_fonts, server, validate_output):
    from tokenmoulds.emitters import WriterTemplateEmitter

    emitter = WriterTemplateEmitter(
        document_ir,
        template_root=None,
    )
    return emitter.build_package(), {}, {}


def _impress_emitter(document_ir, tokens, embed_fonts, server, validate_output):
    from tokenmoulds.emitters import ImpressTemplateEmitter

    emitter = ImpressTemplateEmitter(
        document_ir,
        template_root=None,
    )
    return emitter.build_package(), {}, {}


def _google_docs_emitter(document_ir, tokens, embed_fonts, server, validate_output):
    from tokenmoulds.emitters import GoogleDocsEmitter

    emitter = GoogleDocsEmitter(
        document_ir,
        template_root=None,
    )
    return emitter.build_package(), {}, {}


def _theme_emitter(document_ir, tokens, embed_fonts, server, validate_output):
    from tokenmoulds.themes.thmx import ThemeDefinition, ThmxEmitter

    theme_def = ThemeDefinition.from_color_theme(
        document_ir.color_theme,
        typography=document_ir.typography,
    )
    return ThmxEmitter(theme_def).emit(), {}, {}


# ============================================================================
# Public template generation functions (thin wrappers)
# ============================================================================


def _gdocs_stem(filename: Optional[str], brand_name: str) -> str:
    return f"{filename}-gdocs" if filename else f"{brand_name}-gdocs"


async def generate_word_template(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Generate Word template (.dotx) from brand tokens."""
    return await _generate_template(
        arguments,
        server,
        _FormatConfig(
            template_format=TemplateFormat.WORD,
            file_extension=".dotx",
            action_name="generate_word_template",
            error_label="Word template",
            validation_type="word_template",
            emitter_factory=_word_emitter,
            cached_extra={"styles_count": 276},
            cached_message="Word template retrieved from cache",
            success_message="Word template generated with themed styles. Zero Office defaults.",
        ),
    )


async def generate_powerpoint_template(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Generate PowerPoint template (.potx) from brand tokens."""
    return await _generate_template(
        arguments,
        server,
        _FormatConfig(
            template_format=TemplateFormat.POWERPOINT,
            file_extension=".potx",
            action_name="generate_powerpoint_template",
            error_label="PowerPoint template",
            validation_type="powerpoint_template",
            emitter_factory=_powerpoint_emitter,
            cached_message="PowerPoint template retrieved from cache",
            success_message="PowerPoint template generated with professional table styles and theme.",
        ),
    )


async def generate_excel_template(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Generate Excel template (.xltx) from brand tokens."""
    return await _generate_template(
        arguments,
        server,
        _FormatConfig(
            template_format=TemplateFormat.EXCEL,
            file_extension=".xltx",
            action_name="generate_excel_template",
            error_label="Excel template",
            validation_type="excel_template",
            emitter_factory=_excel_emitter,
            cached_message="Excel template retrieved from cache",
            success_message="Excel template generated with themed styles.",
        ),
    )


async def generate_writer_template(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Generate LibreOffice Writer template (.ott) from brand tokens."""
    return await _generate_template(
        arguments,
        server,
        _FormatConfig(
            template_format=TemplateFormat.WRITER,
            file_extension=".ott",
            action_name="generate_writer_template",
            error_label="Writer template",
            validation_type="writer",
            emitter_factory=_writer_emitter,
            cached_message="Writer template retrieved from cache",
            success_message="LibreOffice Writer template generated (ODF format).",
        ),
    )


async def generate_impress_template(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Generate LibreOffice Impress template (.otp) from brand tokens."""
    return await _generate_template(
        arguments,
        server,
        _FormatConfig(
            template_format=TemplateFormat.IMPRESS,
            file_extension=".otp",
            action_name="generate_impress_template",
            error_label="Impress template",
            validation_type="impress",
            emitter_factory=_impress_emitter,
            cached_message="Impress template retrieved from cache",
            success_message="LibreOffice Impress template generated (ODF format).",
        ),
    )


async def generate_google_docs_template(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Generate Google Docs-compatible template (.dotx) from brand tokens."""
    return await _generate_template(
        arguments,
        server,
        _FormatConfig(
            template_format=TemplateFormat.GOOGLE_DOCS,
            file_extension=".dotx",
            action_name="generate_google_docs_template",
            error_label="Google Docs template",
            validation_type=None,
            supports_embed_fonts=False,
            emitter_factory=_google_docs_emitter,
            filename_stem=_gdocs_stem,
            cached_message="Google Docs template retrieved from cache",
            success_message="Google Docs-compatible template generated. Import to Google Drive.",
        ),
    )


async def generate_theme(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Generate standalone Office theme (.thmx) from brand tokens."""
    return await _generate_template(
        arguments,
        server,
        _FormatConfig(
            template_format=TemplateFormat.THEME,
            file_extension=".thmx",
            action_name="generate_theme",
            error_label="theme",
            validation_type=None,
            emitter_factory=_theme_emitter,
            cached_message="Theme file retrieved from cache",
            success_message="Office theme (.thmx) generated. Apply to any Office document.",
        ),
    )


async def generate_all_templates(
    arguments: Dict[str, Any],
    server: "TokenMouldsServer",
) -> Dict[str, Any]:
    """Generate all template formats for a brand.

    Args:
        arguments: Tool arguments
        server: TokenMouldsServer instance

    Returns:
        Result dictionary with all template paths
    """
    try:
        brand_name, tokens = _get_brand_tokens(server, arguments.get("brand"))
    except ValueError as e:
        return {"error": str(e)}

    formats = arguments.get(
        "formats",
        ["word", "powerpoint", "excel", "writer", "impress", "google_docs", "theme"],
    )
    embed_fonts = arguments.get("embed_fonts", True)
    do_validate = arguments.get("validate", True)
    use_cache = arguments.get("use_cache", True)

    results = {}
    errors = []
    start_time = datetime.now(timezone.utc)

    # Generate each format
    format_handlers = {
        "word": generate_word_template,
        "powerpoint": generate_powerpoint_template,
        "excel": generate_excel_template,
        "writer": generate_writer_template,
        "impress": generate_impress_template,
        "google_docs": generate_google_docs_template,
        "theme": generate_theme,
    }

    for fmt in formats:
        if fmt not in format_handlers:
            errors.append(f"Unknown format: {fmt}")
            continue

        handler = format_handlers[fmt]
        fmt_args = {
            "brand": brand_name,
            "embed_fonts": embed_fonts,
            "validate": do_validate,
            "use_cache": use_cache,
        }

        result = await handler(fmt_args, server)

        if result.get("success"):
            results[fmt] = {
                "file_path": result.get("file_path"),
                "cached": result.get("cached", False),
                "size_bytes": result.get("size_bytes"),
            }
        else:
            errors.append(f"{fmt}: {result.get('error', 'Unknown error')}")

    end_time = datetime.now(timezone.utc)
    duration = (end_time - start_time).total_seconds()

    return {
        "success": len(results) > 0,
        "action": "generate_all_templates",
        "brand": brand_name,
        "formats_requested": formats,
        "formats_generated": list(results.keys()),
        "templates": results,
        "errors": errors if errors else None,
        "duration_seconds": round(duration, 2),
        "message": f"Generated {len(results)}/{len(formats)} templates in {duration:.1f}s",
    }
