"""Parse Terraform module files (variables, outputs, costs, README).

Cloud-agnostic — works with any provider's .tf files.
"""

from __future__ import annotations

import re
from pathlib import Path

import hcl2

from tf_mcp.models import CostEstimate, ModuleInfo, OutputInfo, VariableInfo


def _hcl_type_to_str(type_val: object) -> str:
    """Convert an hcl2-parsed type value to a readable string."""
    if isinstance(type_val, str):
        return type_val
    if isinstance(type_val, list):
        if len(type_val) == 0:
            return "any"
        if len(type_val) == 1:
            return str(type_val[0])
        outer = type_val[0]
        if outer in ("list", "set", "map"):
            inner = _hcl_type_to_str(type_val[1])
            return f"{outer}({inner})"
        if outer == "object":
            return f"object({type_val[1]})"
        return str(type_val)
    if isinstance(type_val, dict):
        return f"object({type_val})"
    return str(type_val)


def _default_to_str(val: object) -> str | None:
    """Convert a default value to a readable string."""
    if val is None:
        return None
    if isinstance(val, bool):
        return "true" if val else "false"
    if isinstance(val, (int, float)):
        return str(val)
    if isinstance(val, str):
        return f'"{val}"'
    if isinstance(val, list):
        if len(val) == 0:
            return "[]"
        return str(val)
    if isinstance(val, dict):
        if len(val) == 0:
            return "{}"
        return str(val)
    return str(val)


def parse_variables(variables_tf: Path) -> list[VariableInfo]:
    """Parse variables.tf and return a list of VariableInfo."""
    if not variables_tf.exists():
        return []

    with open(variables_tf) as f:
        parsed = hcl2.load(f)

    variables: list[VariableInfo] = []
    for var_block in parsed.get("variable", []):
        for var_name, var_body in var_block.items():
            type_raw = var_body.get("type", "any")
            type_str = _hcl_type_to_str(type_raw)
            description = var_body.get("description", "")

            has_default = "default" in var_body
            default_val = _default_to_str(var_body.get("default")) if has_default else None

            validation_msg = None
            if "validation" in var_body:
                validations = var_body["validation"]
                if isinstance(validations, list) and len(validations) > 0:
                    validation_msg = validations[0].get("error_message", "")
                elif isinstance(validations, dict):
                    validation_msg = validations.get("error_message", "")

            variables.append(
                VariableInfo(
                    name=var_name,
                    type=type_str,
                    description=description,
                    default=default_val,
                    required=not has_default,
                    validation=validation_msg,
                )
            )

    return variables


def parse_outputs(outputs_tf: Path) -> list[OutputInfo]:
    """Parse outputs.tf and return a list of OutputInfo."""
    if not outputs_tf.exists():
        return []

    with open(outputs_tf) as f:
        parsed = hcl2.load(f)

    outputs: list[OutputInfo] = []
    for out_block in parsed.get("output", []):
        for out_name, out_body in out_block.items():
            outputs.append(
                OutputInfo(
                    name=out_name,
                    description=out_body.get("description", ""),
                )
            )

    return outputs


def parse_cost_block(main_tf: Path) -> CostEstimate:
    """Extract the cost comment block from the top of main.tf."""
    if not main_tf.exists():
        return CostEstimate()

    text = main_tf.read_text()

    pattern = r"(?m)(^#.*(?:Estimated Monthly Cost|Pricing).*$(?:\n#.*$)*)"
    match = re.search(pattern, text, re.IGNORECASE)
    if not match:
        lines = text.splitlines()
        comment_lines: list[str] = []
        in_header = True
        for line in lines:
            stripped = line.strip()
            if in_header and (stripped.startswith("#") or stripped == ""):
                comment_lines.append(stripped.lstrip("# ").rstrip())
            elif in_header and not stripped.startswith("#"):
                break
        raw = "\n".join(comment_lines).strip()
        if not raw:
            return CostEstimate()
        items = [l.strip() for l in raw.splitlines() if l.strip() and not l.strip().startswith("##")]
        return CostEstimate(raw_text=raw, line_items=items)

    raw = match.group(1)
    cleaned_lines: list[str] = []
    for line in raw.splitlines():
        cleaned = line.lstrip("# ").rstrip()
        if cleaned:
            cleaned_lines.append(cleaned)

    raw_clean = "\n".join(cleaned_lines)
    items = [l for l in cleaned_lines if not l.startswith("##") and "---" not in l]
    return CostEstimate(raw_text=raw_clean, line_items=items)


def parse_readme_description(readme_path: Path) -> str:
    """Extract the first paragraph after the H1 from README.md."""
    if not readme_path.exists():
        return ""

    text = readme_path.read_text()
    lines = text.splitlines()

    found_h1 = False
    para_lines: list[str] = []
    for line in lines:
        if not found_h1:
            if line.startswith("# "):
                found_h1 = True
            continue
        if not para_lines and line.strip() == "":
            continue
        if para_lines and line.strip() == "":
            break
        para_lines.append(line.strip())

    return " ".join(para_lines)


def parse_examples(examples_path: Path) -> str:
    """Read examples/main.tf content."""
    if not examples_path.exists():
        return ""
    return examples_path.read_text()


def parse_module(module_dir: Path, cloud: str = "") -> ModuleInfo:
    """Parse a complete Terraform module directory into a ModuleInfo."""
    name = module_dir.name

    variables = parse_variables(module_dir / "variables.tf")
    outputs = parse_outputs(module_dir / "outputs.tf")
    cost_estimate = parse_cost_block(module_dir / "main.tf")
    description = parse_readme_description(module_dir / "README.md")
    readme = (module_dir / "README.md").read_text() if (module_dir / "README.md").exists() else ""
    examples = parse_examples(module_dir / "examples" / "main.tf")

    return ModuleInfo(
        name=name,
        cloud=cloud,
        description=description,
        variables=variables,
        outputs=outputs,
        cost_estimate=cost_estimate,
        readme=readme,
        examples=examples,
        source_path=str(module_dir),
    )
