"""Drata GRC evidence exporter with Bearer token API push."""

from __future__ import annotations

import hashlib
import json
from typing import TYPE_CHECKING, Any

import httpx

from complyform.core.errors import ExportAPIPushError
from complyform.core.generators.evidence_export._grc_http import grc_request
from complyform.core.generators.evidence_export.base import (
    EvidenceExporter,
    ExportResult,
)

if TYPE_CHECKING:
    from pathlib import Path

DRATA_API_BASE = "https://public-api.drata.com"
DRATA_EVIDENCE_URL = f"{DRATA_API_BASE}/evidence"
DRATA_CONTROLS_URL = f"{DRATA_API_BASE}/controls"


class DrataExporter(EvidenceExporter):
    """Export findings to Drata format with optional API push."""

    FORMAT_ID = "drata"
    TIER_FEATURE = "evidence_export_grc"
    SUPPORTS_API_PUSH = True

    def export_to_file(
        self,
        findings: list[dict[str, object]],
        metadata: dict[str, object],
        output_dir: Path,
        project_label: str | None = None,
    ) -> ExportResult:
        """Generate Drata-formatted JSON evidence file."""
        output_dir.mkdir(parents=True, exist_ok=True)
        scan_id = str(metadata.get("scan_id", "unknown"))

        evidence_items: list[dict[str, object]] = []
        controls_map: dict[str, dict[str, object]] = {}

        for finding in findings:
            resource_address = str(finding.get("resource_address", ""))
            resource_hash = hashlib.sha256(resource_address.encode()).hexdigest()[:8]
            control_id = str(finding.get("control_id", ""))
            framework = str(finding.get("framework", ""))
            status = str(finding.get("status", "FAIL"))

            evidence_items.append(
                {
                    "external_id": f"cf-{framework}-{control_id}-{resource_hash}",
                    "control_id": control_id,
                    "framework": framework,
                    "resource_address": resource_address,
                    "resource_type": str(finding.get("resource_type", "")),
                    "status": "PASSED" if status == "PASS" else "FAILED",
                    "severity": str(finding.get("severity", "MEDIUM")),
                    "description": str(finding.get("finding", "")),
                    "remediation": str(finding.get("remediation", "")),
                }
            )

            ctrl_key = f"{framework}:{control_id}"
            if ctrl_key not in controls_map:
                controls_map[ctrl_key] = {
                    "framework": framework,
                    "control_id": control_id,
                    "pass_count": 0,
                    "fail_count": 0,
                }
            if status == "PASS":
                controls_map[ctrl_key]["pass_count"] = (
                    int(controls_map[ctrl_key]["pass_count"]) + 1  # type: ignore[call-overload]
                )
            else:
                controls_map[ctrl_key]["fail_count"] = (
                    int(controls_map[ctrl_key]["fail_count"]) + 1  # type: ignore[call-overload]
                )

        envelope: dict[str, Any] = {
            "schema_version": 1,
            "platform": "drata",
            "scan_id": scan_id,
            "timestamp": str(metadata.get("timestamp", "")),
            "cloud": str(metadata.get("cloud", "")),
            "project_label": project_label,
            "evidence_count": len(evidence_items),
            "evidence": evidence_items,
            "controls": list(controls_map.values()),
        }

        output_path = output_dir / f"complyform-drata-{scan_id}.json"
        output_path.write_text(json.dumps(envelope, indent=2), encoding="utf-8")

        return ExportResult(
            format=self.FORMAT_ID,
            output_files=[output_path],
            record_count=len(evidence_items),
        )

    async def push_to_api(  # type: ignore[override]
        self,
        findings: list[dict[str, object]],
        metadata: dict[str, object],
        api_config: dict[str, object],
    ) -> ExportResult:
        """Push findings to Drata via Bearer token API."""
        api_key = str(api_config.get("api_key", ""))

        async with httpx.AsyncClient() as client:
            headers = {"Authorization": f"Bearer {api_key}"}

            # File export first
            file_result = self.export_to_file(
                findings,
                metadata,
                output_dir=_default_output_dir(),
            )

            pushed = 0
            errors: list[str] = []

            for evidence in _build_evidence_items(findings, metadata):
                try:
                    await grc_request(
                        client,
                        "POST",
                        DRATA_EVIDENCE_URL,
                        platform="drata",
                        json=evidence,
                        headers=headers,
                    )
                    pushed += 1
                except Exception as exc:
                    errors.append(str(exc))

        if errors and pushed == 0:
            raise ExportAPIPushError("drata", "; ".join(errors[:3]))

        warnings = [f"Failed to push {len(errors)} evidence item(s)"] if errors else []

        return ExportResult(
            format=self.FORMAT_ID,
            output_files=file_result.output_files,
            record_count=pushed,
            api_push_status="partial" if errors else "success",
            api_push_details={
                "pushed": pushed,
                "failed": len(errors),
                "errors": errors[:5],
            },
            warnings=warnings,
        )


def _build_evidence_items(
    findings: list[dict[str, object]],
    metadata: dict[str, object],
) -> list[dict[str, object]]:
    """Build evidence payloads for Drata API."""
    items: list[dict[str, object]] = []
    for finding in findings:
        resource_address = str(finding.get("resource_address", ""))
        resource_hash = hashlib.sha256(resource_address.encode()).hexdigest()[:8]
        control_id = str(finding.get("control_id", ""))
        framework = str(finding.get("framework", ""))
        status = str(finding.get("status", "FAIL"))

        items.append(
            {
                "external_id": f"cf-{framework}-{control_id}-{resource_hash}",
                "control_id": control_id,
                "framework": framework,
                "status": "PASSED" if status == "PASS" else "FAILED",
                "description": str(finding.get("finding", "")),
            }
        )
    return items


def _default_output_dir() -> Path:
    from pathlib import Path

    return Path("./exports")
