"""complyform policy-gen command — generate policy-as-code artifacts."""

from __future__ import annotations

import json
import sys
from datetime import UTC, datetime
from pathlib import Path
from typing import TYPE_CHECKING, Annotated

import typer
from rich.panel import Panel

from complyform.cli.exit_codes import EXIT_RUNTIME_ERROR, EXIT_SUCCESS
from complyform.cli.main import AppContext, app
from complyform.core.cache import ScanCache
from complyform.core.errors import (
    NoCachedAssessmentError,
    PolicyGenNoFindingsError,
    PolicyGenOverwriteError,
    PolicyGenTierError,
)

if TYPE_CHECKING:
    from collections.abc import Mapping

    from complyform.core.generators.base import GenerationResult

_BF = "Brownfield (Existing Infrastructure)"

_FORMAT_TO_FEATURE: dict[str, str] = {
    "opa": "opa_rego",
    "checkov": "checkov_custom",
    "github-action": "github_action",
}


@app.command(name="policy-gen", rich_help_panel=_BF)
def policy_gen_cmd(  # noqa: PLR0913
    ctx: typer.Context,
    format: Annotated[
        str,
        typer.Option(
            "--format",
            help="Output format: checkov, opa, github-action, all",
        ),
    ] = "all",
    output: Annotated[
        str,
        typer.Option("--output", help="Output directory"),
    ] = "./policies",
    frameworks: Annotated[
        str,
        typer.Option("--frameworks", help="Comma-separated framework IDs"),
    ] = "",
    scan_id: Annotated[
        str | None,
        typer.Option("--scan-id", help="Scan ID to use"),
    ] = None,
    cloud: Annotated[
        str | None,
        typer.Option("--cloud", help="Cloud provider override"),
    ] = None,
    dry_run: Annotated[
        bool,
        typer.Option("--dry-run", help="Preview without writing"),
    ] = False,
    overwrite: Annotated[
        bool,
        typer.Option("--overwrite", help="Overwrite existing output"),
    ] = False,
) -> None:
    """Generate policy-as-code artifacts from cached assessment."""
    from complyform.core.auth.license import LicenseManager
    from complyform.core.auth.tier_enforcement import (
        check_feature,
        enforce_with_refresh,
    )
    from complyform.core.generators import GENERATORS

    app_ctx: AppContext = ctx.obj
    console = app_ctx.console

    # 1. Load cached assessment
    cache = ScanCache()
    cached = cache.read(scan_id) if scan_id else cache.latest()
    if cached is None:
        raise NoCachedAssessmentError("")

    resources, cache_meta = cached
    detected_cloud = cloud or str(cache_meta.get("cloud", "gcp"))

    # Re-run assessment to get findings
    all_findings = _load_findings(resources, detected_cloud)

    # 2. Filter to FAIL only
    fail_findings = [f for f in all_findings if f.status == "FAIL"]

    # Apply --frameworks filter
    fw_ids = [f.strip() for f in frameworks.split(",") if f.strip()]
    if fw_ids:
        fail_findings = [f for f in fail_findings if f.framework in fw_ids]

    # 3. No FAIL findings
    if not fail_findings:
        raise PolicyGenNoFindingsError()

    # 4. Check output dir
    output_dir = Path(output)
    if (
        not dry_run
        and output_dir.exists()
        and any(output_dir.iterdir())
        and not overwrite
    ):
        raise PolicyGenOverwriteError(str(output_dir))

    # Tier check
    license_mgr = LicenseManager()
    tier_info = license_mgr.resolve_tier_info()

    # Determine formats to generate
    if format == "all":
        formats_to_gen: list[str] = []
        for fmt, feature_key in _FORMAT_TO_FEATURE.items():
            if feature_key in tier_info.features:
                formats_to_gen.append(fmt)
            elif not app_ctx.quiet:
                console.print(
                    f"[dim]Skipping {fmt}: requires higher tier"
                    f" (current: {tier_info.tier})[/dim]"
                )
        if not formats_to_gen:
            raise PolicyGenTierError("all formats", tier_info.tier)
    else:
        feature_key = _FORMAT_TO_FEATURE.get(format)  # type: ignore[assignment]
        if feature_key is None:
            console.print(f"[red]Unknown format: {format}[/red]")
            raise typer.Exit(code=EXIT_RUNTIME_ERROR)
        tier_info = enforce_with_refresh(
            license_mgr, tier_info, check_feature, feature_key, tier_info
        )
        formats_to_gen = [format]

    # Convert findings to dicts
    findings_dicts = [f.model_dump(mode="json") for f in fail_findings]

    # Load mappings data
    mappings_data = _load_mappings_data(
        detected_cloud,
        fw_ids or list({f.framework for f in fail_findings}),
    )

    # Generate
    results: dict[str, GenerationResult] = {}
    for fmt in formats_to_gen:
        gen_cls = GENERATORS.get(fmt)
        if gen_cls is None:
            continue
        generator = gen_cls()
        if dry_run:
            console.print(f"[dim]Would generate: {generator.format_name()}[/dim]")
            continue
        result = generator.generate(
            findings_dicts, mappings_data, detected_cloud, tier_info.tier, output_dir
        )
        results[fmt] = result

    # Output
    if dry_run:
        console.print("[dim]Dry run — no files written.[/dim]")
        raise typer.Exit(code=EXIT_SUCCESS)

    # JSON envelope to stdout if requested
    if _wants_json(ctx):
        _emit_json(results)
    elif not app_ctx.quiet:
        _emit_summary(console, results, output_dir)

    raise typer.Exit(code=EXIT_SUCCESS)


def _load_findings(resources: list, cloud: str) -> list:  # type: ignore[type-arg]
    """Re-run assessment to get findings — same pattern as remediate_cmd."""
    from complyform.core.assessor.control_matcher import ControlMatcher, Finding
    from complyform.core.profiles.cross_mappings import (
        get_all as get_all_cross_mappings,
    )
    from complyform.core.profiles.mapping_loader import load_mappings
    from complyform.core.profiles.models import CrossMappingIndex
    from complyform.core.profiles.registry import list_frameworks
    from complyform.core.scanner.state_parser import ResourceRecord

    typed_resources: list[ResourceRecord] = []
    for r in resources:
        if isinstance(r, ResourceRecord):
            typed_resources.append(r)

    all_cross = get_all_cross_mappings()
    cross_index = CrossMappingIndex(schema_version=1, mappings=all_cross)

    all_findings: list[Finding] = []
    matcher = ControlMatcher()

    community_fws = [f.id for f in list_frameworks(tier="community")]
    for fw_id in community_fws:
        try:
            mapping_file = load_mappings(fw_id, cloud)
        except Exception:
            continue
        findings = matcher.match(typed_resources, mapping_file, cross_index)
        all_findings.extend(findings)

    return all_findings


def _load_mappings_data(cloud: str, fw_ids: list[str]) -> dict[str, object]:
    """Load mapping YAML data for generators."""
    from complyform.core.profiles.mapping_loader import load_mappings

    all_mappings: list[dict[str, object]] = []
    for fw_id in fw_ids:
        try:
            mf = load_mappings(fw_id, cloud)
            for m in mf.mappings:
                all_mappings.append(m.model_dump(mode="json"))
        except Exception:
            continue

    return {"mappings": all_mappings, "cloud": cloud}


def _wants_json(ctx: typer.Context) -> bool:
    """Check if JSON output was requested."""
    # Check if stdout is being piped
    return not sys.stdout.isatty()


def _emit_json(results: Mapping[str, object]) -> None:
    """Emit JSON envelope to stdout."""
    from complyform.core.generators.base import GenerationResult

    envelope: dict[str, object] = {
        "schema_version": 1,
        "tool": "complyform",
        "command": "policy-gen",
        "timestamp": datetime.now(UTC).isoformat(),
        "generated_artifacts": {},
    }
    artifacts: dict[str, object] = {}
    for fmt, result in results.items():
        if isinstance(result, GenerationResult):
            artifacts[fmt.replace("-", "_")] = {
                "count": result.artifact_count,
                "output_dir": str(result.output_dir),
                "files": [str(f.name) for f in result.files_written],
            }
    envelope["generated_artifacts"] = artifacts
    sys.stdout.write(json.dumps(envelope, indent=2) + "\n")


def _emit_summary(
    console: object,
    results: Mapping[str, object],
    output_dir: Path,
) -> None:
    """Emit terminal summary to stderr."""
    from rich.console import Console

    from complyform.core.generators.base import GenerationResult

    if not isinstance(console, Console):
        return

    lines: list[str] = []
    for fmt, result in results.items():
        if isinstance(result, GenerationResult) and result.artifact_count > 0:
            label = fmt.replace("-", " ").title()
            lines.append(
                f"  {label + ':':20s} {result.artifact_count} file(s)"
                f" -> {result.output_dir}"
            )

    if lines:
        summary = "\n".join(lines)
        console.print(
            Panel(
                f"{summary}\n\n"
                "  Usage:\n"
                f"    OPA:     conftest test tfplan.json"
                f" --policy {output_dir}/opa/\n"
                f"    Checkov: checkov -d ."
                f" --external-checks-dir {output_dir}/checkov/\n"
                "    Action:  git add .github/workflows/"
                "complyform-compliance.yaml && git push",
                title="✓ Policy artifacts generated",
                style="green",
            )
        )
