"""Close packet artifact builder for Deal Desk."""

from __future__ import annotations

import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Iterable

from g_gremlin.scout.models import EvidenceRow, ScoutFinding, ScoutScanResult

from .config import DealDeskConfig


def _utc_now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


def _safe_filename(value: str) -> str:
    safe = []
    for ch in value:
        if ch.isalnum() or ch in ("-", "_"):
            safe.append(ch)
    return "".join(safe) or "unknown"


def _artifact_url(path: Path, base_url: str | None, artifacts_root: Path) -> str | None:
    if not base_url:
        return None
    try:
        rel = path.resolve().relative_to(artifacts_root.resolve())
    except Exception:
        return None
    return f"{base_url.rstrip('/')}/{rel.as_posix()}"


class ClosePacketBuilder:
    def __init__(self, config: DealDeskConfig) -> None:
        self.config = config

    def build_packet(
        self,
        *,
        finding: ScoutFinding,
        row: EvidenceRow,
        run_id: str,
        generated_at: str | None = None,
    ) -> dict[str, Any]:
        fields = row.fields or {}
        check_results = fields.get("check_results", []) or []
        missing_fields = fields.get("missing_fields") or _missing_from_checks(check_results)
        proposed_diffs = fields.get("proposed_diffs", {}) or {}
        computed_suggestions = fields.get("computed_suggestions", {}) or _suggestions_from_diffs(
            proposed_diffs
        )
        overall_status = _overall_status(check_results)
        renewal_eligible = bool(fields.get("renewal_eligible"))
        renewal_proposed = fields.get("renewal_proposed")
        if renewal_proposed is None:
            renewal_proposed = fields.get("renewal_preview") or {}

        actions_pending = []
        if proposed_diffs:
            actions_pending.append("sfdc.record.update")
        if renewal_eligible:
            actions_pending.append("dealdesk.renewal.create_shell")

        payload = {
            "$schema": "ClosePacket/v1",
            "generated_at": generated_at or _utc_now_iso(),
            "run_id": run_id,
            "opportunity_id": row.record_id,
            "opportunity_name": fields.get("Name") or fields.get("opportunity_name"),
            "amount": fields.get("Amount"),
            "close_date": fields.get("CloseDate"),
            "band": fields.get("band"),
            "checklist": check_results,
            "overall_status": overall_status,
            "missing_fields": missing_fields or [],
            "computed_suggestions": computed_suggestions,
            "proposed_diffs": _strip_diff_formulas(proposed_diffs),
            "renewal_eligible": bool(renewal_eligible),
            "renewal_proposed": renewal_proposed or None,
            "actions_pending": actions_pending,
            "warnings": list(finding.warnings or []),
        }
        return payload

    def build_markdown(self, packet: dict[str, Any]) -> str:
        lines: list[str] = []
        name = packet.get("opportunity_name") or packet.get("opportunity_id") or "Opportunity"
        lines.append(f"# Close Packet — {name}")
        lines.append("")
        lines.append(f"Run ID: {packet.get('run_id')}")
        lines.append(f"Generated: {packet.get('generated_at')}")
        lines.append(f"Opportunity ID: {packet.get('opportunity_id')}")
        lines.append(f"Amount: {packet.get('amount')}")
        lines.append(f"Close Date: {packet.get('close_date')}")
        lines.append(f"Band: {packet.get('band')}")
        lines.append(f"Overall Status: {packet.get('overall_status')}")
        lines.append("")

        lines.append("## Checklist")
        checklist = packet.get("checklist") or []
        if checklist:
            lines.append("| Code | Status | Detail | Severity |")
            lines.append("| --- | --- | --- | --- |")
            for item in checklist:
                lines.append(
                    f"| {item.get('code')} | {item.get('status')} | {item.get('detail')} | {item.get('severity')} |"
                )
        else:
            lines.append("No checks recorded.")
        lines.append("")

        lines.append("## Missing Fields")
        missing = packet.get("missing_fields") or []
        if missing:
            for field in missing:
                lines.append(f"- {field}")
        else:
            lines.append("None.")
        lines.append("")

        lines.append("## Proposed Fixes")
        proposed = packet.get("computed_suggestions") or {}
        if proposed:
            for field, detail in proposed.items():
                value = detail.get("value") if isinstance(detail, dict) else detail
                formula = detail.get("formula") if isinstance(detail, dict) else None
                line = f"- {field}: {value}"
                if formula:
                    line = f"{line} ({formula})"
                lines.append(line)
        else:
            lines.append("None.")
        lines.append("")

        lines.append("## Renewal Preview")
        renewal = packet.get("renewal_proposed") or {}
        if renewal:
            for key, value in renewal.items():
                lines.append(f"- {key}: {value}")
        else:
            lines.append("None.")
        lines.append("")

        warnings = packet.get("warnings") or []
        if warnings:
            lines.append("## Warnings")
            for warning in warnings:
                lines.append(f"- {warning}")
            lines.append("")

        return "\n".join(lines).strip() + "\n"


def _overall_status(check_results: Iterable[dict[str, Any]]) -> str:
    for item in check_results:
        if str(item.get("status")).lower() == "fail":
            return "fail"
    return "pass"


def _missing_from_checks(check_results: Iterable[dict[str, Any]]) -> list[str]:
    missing = []
    for item in check_results:
        code = str(item.get("code") or "")
        if code.startswith("DD_MISSING_FIELD:"):
            missing.append(code.split(":", 1)[1])
    return missing


def _suggestions_from_diffs(proposed_diffs: dict[str, Any]) -> dict[str, Any]:
    suggestions: dict[str, Any] = {}
    for field, detail in proposed_diffs.items():
        if isinstance(detail, dict):
            value = detail.get("after")
            formula = detail.get("formula")
            inputs = detail.get("inputs")
            suggestions[field] = {
                "value": value,
                "formula": formula,
                "inputs": inputs,
            }
        else:
            suggestions[field] = {"value": detail}
    return suggestions


def _strip_diff_formulas(proposed_diffs: dict[str, Any]) -> dict[str, Any]:
    cleaned: dict[str, Any] = {}
    for field, detail in proposed_diffs.items():
        if isinstance(detail, dict):
            cleaned[field] = {
                "before": detail.get("before"),
                "after": detail.get("after"),
            }
        else:
            cleaned[field] = {"after": detail}
    return cleaned


def write_close_packets(
    scan_result: ScoutScanResult,
    config: DealDeskConfig,
    state_dir: Path,
    *,
    artifact_base_url: str | None = None,
) -> Path:
    run_id = scan_result.run_id
    artifacts_root = Path(state_dir) / "artifacts"
    output_dir = artifacts_root / run_id / "close_packets"
    output_dir.mkdir(parents=True, exist_ok=True)

    builder = ClosePacketBuilder(config)
    packets_index: dict[str, Any] = {
        "run_id": run_id,
        "generated_at": _utc_now_iso(),
        "close_packets": [],
    }

    for finding in scan_result.findings:
        if not finding.scout_name.startswith("dealdesk"):
            continue
        for row in finding.evidence:
            packet = builder.build_packet(finding=finding, row=row, run_id=run_id)
            name = row.record_id or row.fields.get("Name") or "packet"
            slug = _safe_filename(str(name))
            json_path = output_dir / f"{slug}.json"
            md_path = output_dir / f"{slug}.md"
            with json_path.open("w", encoding="utf-8") as handle:
                json.dump(packet, handle, indent=2)
            md_text = builder.build_markdown(packet)
            md_path.write_text(md_text, encoding="utf-8")

            packets_index["close_packets"].append(
                {
                    "opportunity_id": row.record_id,
                    "opportunity_name": row.fields.get("Name"),
                    "overall_status": packet.get("overall_status"),
                    "json_path": str(json_path),
                    "markdown_path": str(md_path),
                    "json_url": _artifact_url(json_path, artifact_base_url, artifacts_root),
                    "markdown_url": _artifact_url(md_path, artifact_base_url, artifacts_root),
                }
            )

    index_path = output_dir / "close_packets_index.json"
    packets_index["index_url"] = _artifact_url(index_path, artifact_base_url, artifacts_root)
    with index_path.open("w", encoding="utf-8") as handle:
        json.dump(packets_index, handle, indent=2)

    return index_path


__all__ = ["ClosePacketBuilder", "write_close_packets"]
