"""complyform export command -- evidence export with GRC API push."""

from __future__ import annotations

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

if TYPE_CHECKING:
    from complyform.core.generators.evidence_export.base import ExportResult

import typer

from complyform.cli.exit_codes import EXIT_SUCCESS
from complyform.cli.main import AppContext, app
from complyform.core.cache import ScanCache
from complyform.core.errors import (
    ExportCredentialsMissingError,
    ExportNoCachedAssessmentError,
)

_BF = "Brownfield (Existing Infrastructure)"

_GRC_FORMATS = frozenset({"vanta", "drata", "secureframe"})


@app.command(name="export", rich_help_panel=_BF)
def export_cmd(  # noqa: PLR0913
    ctx: typer.Context,
    format: Annotated[
        str,
        typer.Option(
            "--format",
            help=(
                "Export format: scc-json, audit-manager,"
                " purview, grc-json, vanta, drata, secureframe"
            ),
        ),
    ] = "",
    scan_id: Annotated[
        str | None,
        typer.Option("--scan-id", help="Scan ID to export"),
    ] = None,
    project_label: Annotated[
        str | None,
        typer.Option("--project-label", help="Project label"),
    ] = None,
    output: Annotated[
        str,
        typer.Option("--output", help="Output directory"),
    ] = "./exports",
    include_passing: Annotated[
        bool,
        typer.Option("--include-passing", help="Include PASS findings"),
    ] = False,
    # Stubbed flags for future phases
    batch: Annotated[
        str | None,
        typer.Option("--batch", help="Batch manifest path"),
    ] = None,
    api_key: Annotated[
        str | None,
        typer.Option("--api-key", help="API key for GRC push"),
    ] = None,
    api_secret: Annotated[
        str | None,
        typer.Option("--api-secret", help="API secret for GRC push"),
    ] = None,
    client_id: Annotated[
        str | None,
        typer.Option("--client-id", help="OAuth client ID"),
    ] = None,
    client_secret: Annotated[
        str | None,
        typer.Option("--client-secret", help="OAuth client secret"),
    ] = None,
    file_only: Annotated[
        bool,
        typer.Option("--file-only", help="File-only export"),
    ] = False,
    sf_region: Annotated[
        str | None,
        typer.Option("--sf-region", help="Secureframe region"),
    ] = None,
) -> None:
    """Export evidence to cloud-native or GRC formats."""
    from complyform.core.auth.license import LicenseManager
    from complyform.core.auth.tier_enforcement import (
        check_feature,
        enforce_with_refresh,
    )
    from complyform.core.generators.evidence_export import (
        FORMAT_TO_FEATURE,
        get_exporter,
    )

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

    # ── Batch mode ──────────────────────────────────────────────────────
    if batch:
        from complyform.cli.exit_codes import EXIT_PARTIAL_FAILURE
        from complyform.core.batch.executor import run_batch_operation
        from complyform.core.batch.manifest import BatchProject, load_batch_manifest

        license_mgr = LicenseManager()
        tier_info = license_mgr.resolve_tier_info()
        enforce_with_refresh(
            license_mgr, tier_info, check_feature, "evidence_export_batch", tier_info
        )

        manifest = load_batch_manifest(Path(batch))
        output_root = Path(output)

        def _per_project(proj: BatchProject) -> dict[str, object]:
            cache = ScanCache()
            cached = (
                cache.read_by_label(proj.label)
                if hasattr(cache, "read_by_label")
                else cache.latest()
            )
            if cached is None:
                return {"status": "skipped", "reason": "no cached assessment"}

            _resources, _meta = cached
            detected_cloud = str(_meta.get("cloud", "gcp"))
            findings = _load_findings(_resources, detected_cloud)

            statuses = {"FAIL", "MANUAL_REVIEW"}
            if include_passing:
                statuses.add("PASS")
            filtered = [f for f in findings if f.status in statuses]
            findings_dicts = [f.model_dump(mode="json") for f in filtered]

            meta: dict[str, object] = {
                "scan_id": str(_meta.get("state_hash", "unknown")).replace(
                    "sha256:", ""
                )[:16],
                "timestamp": str(_meta.get("scanned_at", "")),
                "cloud": detected_cloud,
            }

            proj_dir = output_root / proj.label
            proj_dir.mkdir(parents=True, exist_ok=True)

            exporter = get_exporter(format)
            result = exporter.export_to_file(findings_dicts, meta, proj_dir, proj.label)
            return {
                "status": "success",
                "format": result.format,
                "record_count": result.record_count,
                "output_files": [str(p) for p in result.output_files],
            }

        batch_result = run_batch_operation(
            manifest, "export", _per_project, output_root
        )
        exit_code = EXIT_SUCCESS if batch_result.failed == 0 else EXIT_PARTIAL_FAILURE
        raise typer.Exit(code=exit_code)

    if not format:
        console.print(
            "[red]--format is required."
            " Valid: scc-json, audit-manager, purview, grc-json,"
            " vanta, drata, secureframe[/red]"
        )
        raise typer.Exit(code=2)

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

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

    # 2. Tier check
    license_mgr = LicenseManager()
    tier_info = license_mgr.resolve_tier_info()
    feature_key = FORMAT_TO_FEATURE.get(format)
    if feature_key is None:
        # Try get_exporter which will raise ExportFormatError
        get_exporter(format)
        raise typer.Exit(code=2)

    tier_info = enforce_with_refresh(
        license_mgr, tier_info, check_feature, feature_key, tier_info
    )

    # 3. Re-run assessment
    all_findings = _load_findings(resources, detected_cloud)

    # 4. Filter findings
    statuses = {"FAIL", "MANUAL_REVIEW"}
    if include_passing:
        statuses.add("PASS")
    filtered = [f for f in all_findings if f.status in statuses]

    findings_dicts = [f.model_dump(mode="json") for f in filtered]

    # 5. Build metadata
    meta: dict[str, object] = {
        "scan_id": cache_meta.get("state_hash", "unknown").replace("sha256:", "")[:16],
        "timestamp": str(cache_meta.get("scanned_at", "")),
        "cloud": detected_cloud,
    }

    # 6. Export
    exporter = get_exporter(format)
    output_dir = Path(output)
    result = exporter.export_to_file(findings_dicts, meta, output_dir, project_label)

    # 7. GRC API push (unless --file-only)
    if format in _GRC_FORMATS and not file_only and exporter.SUPPORTS_API_PUSH:
        api_config = _resolve_api_config(
            format, api_key, api_secret, client_id, client_secret, sf_region
        )
        try:
            api_result: ExportResult = asyncio.run(
                exporter.push_to_api(findings_dicts, meta, api_config)  # type: ignore[arg-type]
            )
            result.api_push_status = api_result.api_push_status
            result.api_push_details = api_result.api_push_details
            if api_result.warnings:
                result.warnings.extend(api_result.warnings)
        except ExportCredentialsMissingError:
            raise
        except Exception as exc:
            # API push failed but file export succeeded
            result.api_push_status = "failed"
            result.api_push_details = {"error": str(exc)}
            result.warnings.append(f"API push failed: {exc}")

    # 8. Output
    if not sys.stdout.isatty():
        envelope = {
            "schema_version": 1,
            "tool": "complyform",
            "command": "export",
            "timestamp": datetime.now(UTC).isoformat(),
            "export_results": [
                {
                    "format": result.format,
                    "output_files": [str(p) for p in result.output_files],
                    "record_count": result.record_count,
                    "api_push_status": result.api_push_status,
                    "warnings": result.warnings,
                }
            ],
        }
        sys.stdout.write(json.dumps(envelope, indent=2) + "\n")
    elif not app_ctx.quiet:
        console.print(
            f"\n[bold green]Export complete:[/bold green]"
            f" {result.format} | {result.record_count} records"
            f" | {len(result.output_files)} file(s)"
            f" -> {output_dir}"
        )
        for f in result.output_files:
            console.print(f"  [dim]{f.name}[/dim]")
        if result.api_push_status:
            console.print(f"  [dim]API push: {result.api_push_status}[/dim]")

    raise typer.Exit(code=EXIT_SUCCESS)


def _resolve_api_config(
    format_id: str,
    api_key: str | None,
    api_secret: str | None,
    client_id: str | None,
    client_secret: str | None,
    sf_region: str | None,
) -> dict[str, object]:
    """Resolve API credentials from CLI flags or environment variables."""
    if format_id == "vanta":
        cid = client_id or os.environ.get("VANTA_CLIENT_ID", "")
        csec = client_secret or os.environ.get("VANTA_CLIENT_SECRET", "")
        if not cid or not csec:
            raise ExportCredentialsMissingError(
                "vanta", ["--client-id", "--client-secret"]
            )
        return {"client_id": cid, "client_secret": csec}

    if format_id == "drata":
        key = api_key or os.environ.get("DRATA_API_KEY", "")
        if not key:
            raise ExportCredentialsMissingError("drata", ["--api-key"])
        return {"api_key": key}

    if format_id == "secureframe":
        key = api_key or os.environ.get("SECUREFRAME_API_KEY", "")
        secret = api_secret or os.environ.get("SECUREFRAME_API_SECRET", "")
        region = sf_region or os.environ.get("SECUREFRAME_REGION", "us")
        if not key or not secret:
            raise ExportCredentialsMissingError(
                "secureframe", ["--api-key", "--api-secret"]
            )
        return {"api_key": key, "api_secret": secret, "region": region}

    return {}


def _load_findings(resources: list, cloud: str) -> list:  # type: ignore[type-arg]
    """Re-run assessment to get findings."""
    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
