"""FastMCP server for multi-cloud Terraform module catalogs.

Provides tools and resources for discovering modules, understanding
costs/dependencies, and generating ready-to-use Terraform code
across AWS, Azure, and GCP.
"""

from __future__ import annotations

import json

from fastmcp import FastMCP

from tf_mcp.codegen import generate_single_module, generate_stack
from tf_mcp.dependencies import (
    get_dependency_graph_text,
    get_dependents,
    get_direct_dependencies,
    get_module_dependency_info,
    get_module_descriptions,
    resolve_transitive_dependencies,
)
from tf_mcp.models import CostRequest, StackModuleRequest
from tf_mcp.registry import ModuleRegistry

mcp = FastMCP("Terraform Modules")

registry = ModuleRegistry()


# ============================================================
# Tools
# ============================================================


@mcp.tool
def list_modules(cloud: str | None = None) -> str:
    """List available Terraform modules.

    Args:
        cloud: Filter by cloud provider (aws, azure, gcp). Lists all if omitted.
    """
    clouds = [cloud] if cloud else registry.clouds
    results = []
    for c in clouds:
        descriptions = get_module_descriptions(c)
        for name in registry.list_names(c):
            module = registry.get(c, name)
            if not module:
                continue
            deps = get_direct_dependencies(c, name)
            cost_summary = module.cost_estimate.raw_text[:200] if module.cost_estimate.raw_text else "See module README"
            results.append(
                {
                    "cloud": c,
                    "name": name,
                    "description": descriptions.get(name, module.description),
                    "dependencies": deps,
                    "cost_summary": cost_summary,
                }
            )
    return json.dumps(results, indent=2)


@mcp.tool
def get_module_details(cloud: str, module_name: str) -> str:
    """Get full details for a specific module.

    Args:
        cloud: Cloud provider (aws, azure, gcp).
        module_name: Name of the module (e.g., 'vpc', 'eks-cluster', 'vnet').
    """
    module = registry.get(cloud, module_name)
    if not module:
        available = ", ".join(registry.list_names(cloud))
        return json.dumps({"error": f"Module '{module_name}' not found in {cloud}. Available: {available}"})

    descriptions = get_module_descriptions(cloud)
    return json.dumps(
        {
            "cloud": cloud,
            "name": module.name,
            "description": descriptions.get(module.name, module.description),
            "variables": [v.model_dump() for v in module.variables],
            "outputs": [o.model_dump() for o in module.outputs],
            "cost_estimate": module.cost_estimate.model_dump(),
            "examples": module.examples[:3000] if module.examples else None,
        },
        indent=2,
    )


@mcp.tool
def get_module_dependencies(cloud: str, module_name: str) -> str:
    """Show dependency relationships and wiring for a module.

    Args:
        cloud: Cloud provider (aws, azure, gcp).
        module_name: Name of the module (e.g., 'eks-cluster').
    """
    module = registry.get(cloud, module_name)
    if not module:
        available = ", ".join(registry.list_names(cloud))
        return json.dumps({"error": f"Module '{module_name}' not found in {cloud}. Available: {available}"})

    direct_deps = get_direct_dependencies(cloud, module_name)
    all_deps = resolve_transitive_dependencies(cloud, [module_name])
    all_deps = [d for d in all_deps if d != module_name]

    dependents = get_dependents(cloud, module_name)

    dep_info = get_module_dependency_info(cloud, module_name)
    wiring = []
    for dep in dep_info:
        for w in dep.wiring:
            wiring.append(
                {
                    "from": f"module.{dep.source}.{w.source_output}",
                    "to": w.target_input,
                    "source_module": dep.source,
                }
            )

    return json.dumps(
        {
            "cloud": cloud,
            "module": module_name,
            "direct_dependencies": direct_deps,
            "transitive_dependencies": all_deps,
            "dependents": dependents,
            "wiring": wiring,
        },
        indent=2,
    )


@mcp.tool
def estimate_costs(cloud: str, modules: list[dict[str, str]]) -> str:
    """Estimate monthly costs for a set of modules.

    Args:
        cloud: Cloud provider (aws, azure, gcp).
        modules: List of {name, tier} where tier is dev/staging/prod.
    """
    requests = [CostRequest(**m) for m in modules]
    results = []
    for req in requests:
        module = registry.get(cloud, req.name)
        if not module:
            results.append({"name": req.name, "error": "Module not found"})
            continue

        results.append(
            {
                "name": req.name,
                "tier": req.tier,
                "cost_details": module.cost_estimate.raw_text,
                "line_items": module.cost_estimate.line_items,
            }
        )

    return json.dumps({"cloud": cloud, "modules": results}, indent=2)


@mcp.tool
def generate_module_code(
    cloud: str,
    module_name: str,
    name_prefix: str,
    config: dict[str, str] | None = None,
    include_scaffold: bool = True,
) -> str:
    """Generate Terraform code for a single module.

    Args:
        cloud: Cloud provider (aws, azure, gcp).
        module_name: Name of the module (e.g., 'vpc', 'ecs-cluster').
        name_prefix: Prefix for resource names (e.g., 'myapp-prod').
        config: Optional variable overrides.
        include_scaffold: Include terraform/provider/variable blocks (default true).
    """
    return generate_single_module(
        registry=registry,
        cloud=cloud,
        module_name=module_name,
        name_prefix=name_prefix,
        config=config,
        include_scaffold=include_scaffold,
    )


@mcp.tool
def generate_stack_code(
    cloud: str,
    modules: list[dict],
    project_name: str,
    environment: str = "prod",
    region: str | None = None,
    backend: str | None = None,
) -> str:
    """Generate a complete multi-module Terraform stack.

    Auto-resolves transitive dependencies, topologically orders modules,
    and generates wired module blocks.

    Args:
        cloud: Cloud provider (aws, azure, gcp).
        modules: List of {name, instance_name?, config?} for each module.
        project_name: Project name (used as prefix).
        environment: Environment name (default 'prod').
        region: Cloud region (defaults to cloud-specific default).
        backend: Optional backend configuration type.
    """
    requests = [StackModuleRequest(**m) for m in modules]
    return generate_stack(
        registry=registry,
        cloud=cloud,
        modules=requests,
        project_name=project_name,
        environment=environment,
        region=region,
        backend=backend,
    )


# ============================================================
# Resources
# ============================================================


@mcp.resource("modules://catalog")
def catalog_resource() -> str:
    """Full module catalog listing across all clouds."""
    return list_modules()


@mcp.resource("modules://{cloud}/catalog")
def cloud_catalog_resource(cloud: str) -> str:
    """Module catalog listing for a specific cloud."""
    return list_modules(cloud=cloud)


@mcp.resource("modules://{cloud}/{module_name}/readme")
def module_readme(cloud: str, module_name: str) -> str:
    """Module README.md content."""
    module = registry.get(cloud, module_name)
    if not module:
        return f"Module '{module_name}' not found in {cloud}."
    return module.readme


@mcp.resource("modules://{cloud}/{module_name}/examples")
def module_examples(cloud: str, module_name: str) -> str:
    """Module examples/main.tf content."""
    module = registry.get(cloud, module_name)
    if not module:
        return f"Module '{module_name}' not found in {cloud}."
    return module.examples or "No examples available."


@mcp.resource("modules://{cloud}/dependency-graph")
def dependency_graph_resource(cloud: str) -> str:
    """Text representation of the module dependency graph for a cloud."""
    return get_dependency_graph_text(cloud)


@mcp.resource("modules://conventions")
def conventions_resource() -> str:
    """Terraform conventions used by this module catalog."""
    return """\
Terraform Conventions
=====================

Module Structure: modules/<name>/ with main.tf, variables.tf, outputs.tf, versions.tf, README.md, examples/
Terraform version: >= 1.5.0
Naming: name_prefix variable, snake_case variables/outputs, hyphen-separated module dirs
Argument order: meta -> identity -> core config -> feature toggles -> tags/labels
Security: encryption on, logging on, public access off by default (all overridable)
Cost comments: top of main.tf with per-resource breakdown

Cloud-specific:
  AWS:   provider hashicorp/aws ~> 5.0, var.tags (map(string)), var.region
  Azure: provider hashicorp/azurerm ~> 3.0, var.tags (map(string)), var.location
  GCP:   provider hashicorp/google ~> 5.0, var.labels (map(string)), var.region + var.project_id
"""


def main() -> None:
    """Entry point for the tf-mcp CLI."""
    mcp.run()


if __name__ == "__main__":
    main()
