"""Multi-cloud Terraform code generation engine.

Generates .tf content for any supported cloud provider.
"""

from __future__ import annotations

from tf_mcp.dependencies import (
    get_direct_dependencies,
    get_identity_defaults,
    get_key_outputs,
    get_module_layers,
    get_provider_config,
    get_short_name,
    get_wiring_map,
    resolve_transitive_dependencies,
)
from tf_mcp.models import ModuleInfo, StackModuleRequest
from tf_mcp.registry import ModuleRegistry


def _terraform_block(cloud: str) -> str:
    cfg = get_provider_config(cloud)
    return f"""\
terraform {{
  required_version = ">= 1.5.0"

  required_providers {{
    {cfg["provider_name"]} = {{
      source  = "{cfg["source"]}"
      version = "{cfg["version_constraint"]}"
    }}
  }}
}}"""


def _provider_block(cloud: str) -> str:
    return get_provider_config(cloud)["provider_block"]


def _variables_block(cloud: str, project_name: str, environment: str) -> str:
    cfg = get_provider_config(cloud)
    region_var = cfg["region_variable"]
    region_default = cfg["region_default"]

    lines = f"""\
variable "{region_var}" {{
  description = "{cloud.upper()} {region_var}"
  type        = string
  default     = "{region_default}"
}}

variable "environment" {{
  description = "Environment name (e.g., prod, staging)"
  type        = string
  default     = "{environment}"
}}

variable "name" {{
  description = "Project name used as prefix for all resources"
  type        = string
  default     = "{project_name}"
}}"""

    # GCP needs project_id
    if cloud == "gcp":
        lines += """

variable "project_id" {
  description = "GCP project ID"
  type        = string
}"""

    # Azure needs resource_group_name and location is already the region var
    if cloud == "azure":
        lines += """

variable "resource_group_name" {
  description = "Azure resource group name"
  type        = string
}"""

    return lines


def _locals_block(cloud: str, stack_name: str) -> str:
    cfg = get_provider_config(cloud)
    region_var = cfg["region_variable"]
    tags_var = cfg["tags_variable"]

    azs_line = ""
    if cloud == "aws":
        azs_line = f'\n  azs         = ["${{{{{region_var}}}}}a", "${{{{{region_var}}}}}b"]'

    return f"""\
locals {{
  name_prefix = "${{var.name}}-${{var.environment}}"{azs_line}

  common_{tags_var} = {{
    Project     = var.name
    Environment = var.environment
    ManagedBy   = "terraform"
    Stack       = "{stack_name}"
  }}
}}"""


def _section_header(title: str) -> str:
    return f"""\
########################################
# {title}
########################################"""


def _generate_module_block(
    cloud: str,
    module: ModuleInfo,
    instance_name: str,
    config_overrides: dict[str, str],
    all_instances: dict[str, str],
    source_prefix: str = "../../modules",
    standalone: bool = False,
) -> str:
    cfg = get_provider_config(cloud)
    tags_var = cfg["tags_variable"]
    wiring_map = get_wiring_map(cloud)
    lines: list[str] = []

    deps = get_direct_dependencies(cloud, module.name)
    dep_sources = [s for s in deps if s in all_instances]
    if dep_sources:
        wired_from: list[str] = []
        for src in dep_sources:
            wiring = wiring_map.get((src, module.name), {})
            for src_out in wiring:
                wired_from.append(f"{all_instances[src]}.{src_out}")
        if wired_from:
            lines.append(f"# Consumes: {', '.join(wired_from)}")
            lines.append("")

    lines.append(f'module "{instance_name}" {{')
    lines.append(f'  source = "{source_prefix}/{module.name}"')
    lines.append("")

    name_prefix_ref = "var.name_prefix" if standalone else "local.name_prefix"
    lines.append(f"  name_prefix = {name_prefix_ref}")

    identity_fields = get_identity_defaults(cloud, module.name)
    for field_name, field_val in identity_fields.items():
        if field_name in config_overrides:
            val = config_overrides.pop(field_name)
            lines.append(f"  {field_name} = {val}")
        else:
            lines.append(f"  {field_name} = {field_val}")

    wired_inputs: dict[str, str] = {}
    for src in dep_sources:
        src_instance = all_instances[src]
        wiring = wiring_map.get((src, module.name), {})
        for src_output, tgt_input in wiring.items():
            if "." in tgt_input:
                continue
            ref = f"module.{src_instance}.{src_output}"
            wired_inputs[tgt_input] = f"{ref} # <-- wired from {src_instance}"

    if wired_inputs:
        lines.append("")
        for tgt_input, ref_comment in wired_inputs.items():
            assignment = f"  {tgt_input}"
            padding = max(1, 24 - len(assignment))
            lines.append(f"{assignment}{' ' * padding}= {ref_comment}")

    if config_overrides:
        lines.append("")
        for key, val in config_overrides.items():
            lines.append(f"  {key} = {val}")

    tags_ref = f"var.{tags_var}" if standalone else f"local.common_{tags_var}"
    lines.append("")
    lines.append(f"  {tags_var} = {tags_ref}")
    lines.append("}")

    return "\n".join(lines)


def _generate_outputs(
    cloud: str,
    modules: list[tuple[str, ModuleInfo]],
) -> str:
    key_outputs = get_key_outputs(cloud)
    lines: list[str] = []

    for instance_name, module in modules:
        outputs = key_outputs.get(module.name, [])
        for out_name in outputs:
            tf_out_name = f"{instance_name}_{out_name}" if instance_name != out_name else out_name
            if tf_out_name.startswith(f"{instance_name}_{instance_name}_"):
                tf_out_name = tf_out_name[len(f"{instance_name}_"):]
            lines.append(f'output "{tf_out_name}" {{')
            lines.append(f"  value = module.{instance_name}.{out_name}")
            lines.append("}")
            lines.append("")

    return "\n".join(lines).rstrip()


def generate_single_module(
    registry: ModuleRegistry,
    cloud: str,
    module_name: str,
    name_prefix: str,
    config: dict[str, str] | None = None,
    include_scaffold: bool = True,
) -> str:
    """Generate .tf content for a single module."""
    module = registry.get(cloud, module_name)
    if not module:
        return f"# Error: module '{module_name}' not found in {cloud} catalog"

    cfg = get_provider_config(cloud)
    config_overrides = dict(config) if config else {}
    parts: list[str] = []

    if include_scaffold:
        parts.append(_terraform_block(cloud))
        parts.append("")
        parts.append(_provider_block(cloud))
        parts.append("")
        parts.append(f"""\
variable "name_prefix" {{
  description = "Prefix for all resource names"
  type        = string
  default     = "{name_prefix}"
}}""")
        parts.append("")
        parts.append(f"""\
variable "{cfg["tags_variable"]}" {{
  description = "{cfg["tags_variable"].title()} applied to all resources"
  type        = {cfg["tags_type"]}
  default     = {{}}
}}""")
        parts.append("")

    instance_name = module_name.replace("-", "_")
    all_instances: dict[str, str] = {module_name: instance_name}

    block = _generate_module_block(
        cloud=cloud,
        module=module,
        instance_name=instance_name,
        config_overrides=config_overrides,
        all_instances=all_instances,
        standalone=True,
    )
    parts.append(block)

    return "\n".join(parts)


def generate_stack(
    registry: ModuleRegistry,
    cloud: str,
    modules: list[StackModuleRequest],
    project_name: str,
    environment: str = "prod",
    region: str | None = None,
    backend: str | None = None,
) -> str:
    """Generate a complete stack .tf file from requested modules."""
    cfg = get_provider_config(cloud)
    if region is None:
        region = cfg["region_default"]

    requested_names = [m.name for m in modules]
    all_module_names = resolve_transitive_dependencies(cloud, requested_names)

    instance_overrides = {m.name: m.instance_name for m in modules if m.instance_name}
    config_overrides = {m.name: m.config for m in modules}

    instance_names: dict[str, str] = {}
    for name in all_module_names:
        if name in instance_overrides:
            instance_names[name] = instance_overrides[name]
        else:
            instance_names[name] = get_short_name(cloud, name)

    stack_name = _infer_stack_name(cloud, requested_names)

    parts: list[str] = []

    parts.append(_section_header(f"{stack_name.replace('-', ' ').title()} Stack ({cloud.upper()})"))
    parts.append("#")
    parts.append(f"# Modules: {', '.join(all_module_names)}")
    parts.append("#")
    parts.append("")
    parts.append(_terraform_block(cloud))
    parts.append("")
    parts.append(_provider_block(cloud))
    parts.append("")
    parts.append(_section_header("Variables"))
    parts.append("")
    parts.append(_variables_block(cloud, project_name, environment))
    parts.append("")
    parts.append(_locals_block(cloud, stack_name))
    parts.append("")

    layers = get_module_layers(cloud)
    current_layer = 0
    ordered_modules: list[tuple[str, ModuleInfo]] = []

    for mod_name in all_module_names:
        module = registry.get(cloud, mod_name)
        if not module:
            continue

        layer = layers.get(mod_name, 99)
        instance_name = instance_names[mod_name]
        config = dict(config_overrides.get(mod_name, {}))

        if layer != current_layer:
            current_layer = layer
            parts.append(
                _section_header(f"Layer {layer}: {module.name.replace('-', ' ').title()}")
            )

        block = _generate_module_block(
            cloud=cloud,
            module=module,
            instance_name=instance_name,
            config_overrides=config,
            all_instances=instance_names,
        )
        parts.append(block)
        parts.append("")
        ordered_modules.append((instance_name, module))

    parts.append(_section_header("Outputs"))
    parts.append("")
    parts.append(_generate_outputs(cloud, ordered_modules))
    parts.append("")

    return "\n".join(parts)


def _infer_stack_name(cloud: str, requested_modules: list[str]) -> str:
    if cloud == "aws":
        if any(m.startswith("eks") for m in requested_modules):
            return "eks-cluster"
        if any(m.startswith("ecs") for m in requested_modules):
            return "ecs-app"
        if "ec2-asg" in requested_modules:
            return "web-app"
        if "lambda-function" in requested_modules:
            return "serverless"
    elif cloud == "azure":
        if any(m.startswith("aks") for m in requested_modules):
            return "aks-cluster"
        if any(m.startswith("container") for m in requested_modules):
            return "container-app"
        if "vm-scale-set" in requested_modules:
            return "web-app"
        if "function-app" in requested_modules:
            return "serverless"
    elif cloud == "gcp":
        if any(m.startswith("gke") for m in requested_modules):
            return "gke-cluster"
        if "cloud-run" in requested_modules:
            return "cloud-run-app"
    return "infrastructure"
