"""Backlog SLA breaches check."""

from __future__ import annotations

import math
import time
from typing import Any, Dict, List, Tuple

from .base import BaseScoutCheck
from ..crm_provider import Filter, FilterExpr, FilterOp
from ..models import EvidenceRow, RecommendedAction, ScoutFinding
from ..utils import hours_since


class BacklogSlaBreachesCheck(BaseScoutCheck):
    def build_count_filters(self) -> Tuple[str, List[FilterExpr]]:
        return "lead", []

    def build_sample_request(self) -> Tuple[str, List[str], List[FilterExpr], str | None, str, bool]:
        return "lead", ["Id"], [], None, "ASC", False

    def evaluate(self, rows: List[Dict[str, Any]]) -> List[EvidenceRow]:
        return []

    def execute(self) -> ScoutFinding:  # type: ignore[override]
        start = time.perf_counter()
        evidence: List[EvidenceRow] = []
        total_scanned = 0

        sobjects = self.params.get("sobjects", ["Lead"]) or ["Lead"]
        sobjects = [str(s).lower() for s in sobjects]
        sla_hours = float(self.params.get("sla_hours", 24))
        inactive_hours = float(self.params.get("inactive_hours", 48))
        initial_statuses = [
            str(s) for s in (self.params.get("initial_statuses", []) or ["New", "Open", "Unqualified"])
        ]
        exclude_statuses = [str(s) for s in (self.params.get("exclude_statuses", []) or ["Closed", "Converted"])]
        if self.provider.name == "hubspot" and not self.params.get("initial_statuses"):
            self._warnings.append(
                "HubSpot statuses are org-specific; set initial_statuses to match your portal."
            )

        sla_days = max(1, math.ceil(sla_hours / 24.0))

        for obj in sobjects:
            if not self.provider.supports_object(obj):
                self._warnings.append(f"Unsupported object: {obj}")
                continue

            # Verify status field exists
            available = {f.lower() for f in self._describe_fields(obj)}
            if available and "status" not in available and "hs_lead_status" not in available and "statuscode" not in available:
                self._warnings.append(f"Status field not found on {obj}; skipping.")
                continue

            filters: List[FilterExpr] = [
                Filter("Status", FilterOp.IN, initial_statuses),
                Filter("Status", FilterOp.NOT_IN, exclude_statuses),
                Filter("CreatedDate", FilterOp.NOT_LAST_N_DAYS, sla_days),
            ]

            count = self.provider.count(obj, filters)
            self._collect_provider_meta()
            total_scanned += count

            rows = self.provider.query(
                obj,
                ["Id", "Name", "Status", "OwnerId", "CreatedDate", "LastActivityDate"],
                filters,
                order_by="CreatedDate",
                order_dir="ASC",
                limit=self.top_n,
            )
            self._collect_provider_meta()

            for row in rows:
                record_id = row.get("Id", "")
                if not record_id:
                    continue
                created_hours = hours_since(row.get("CreatedDate"))
                inactive_hours_val = hours_since(row.get("LastActivityDate"))
                status = row.get("Status")

                base_fields = {
                    "Name": row.get("Name"),
                    "Status": status,
                    "OwnerId": row.get("OwnerId"),
                    "CreatedDate": row.get("CreatedDate"),
                    "LastActivityDate": row.get("LastActivityDate"),
                    "hours_since_creation": created_hours,
                    "hours_since_activity": inactive_hours_val,
                }

                if created_hours is not None and created_hours >= sla_hours:
                    evidence.append(
                        EvidenceRow(
                            record_id=record_id,
                            sobject=obj,
                            fields=dict(base_fields),
                            reason=f"In {status} for {int(created_hours)}h (SLA: {int(sla_hours)}h)",
                            reason_code="CREATION_SLA_BREACH",
                            severity=self.definition.severity_default,
                        )
                    )

                if inactive_hours_val is None or inactive_hours_val >= inactive_hours:
                    evidence.append(
                        EvidenceRow(
                            record_id=record_id,
                            sobject=obj,
                            fields=dict(base_fields),
                            reason=f"No activity in {int(inactive_hours_val or inactive_hours)}h (SLA: {int(inactive_hours)}h)",
                            reason_code="INACTIVITY_SLA_BREACH",
                            severity=self.definition.severity_default,
                        )
                    )

        total_findings = len(evidence)
        if len(evidence) > self.top_n:
            evidence = evidence[: self.top_n]

        status = "findings" if total_findings > 0 else "ok"
        duration_ms = int((time.perf_counter() - start) * 1000)
        return ScoutFinding(
            scout_name=self.definition.name,
            display_name=self.definition.display_name,
            description=self.definition.description,
            category=self.definition.category,
            severity=self.definition.severity_default,
            total_records_scanned=total_scanned,
            findings_count=total_findings,
            evidence=evidence,
            recommended_actions=self.get_recommended_actions(),
            soql_queries=self._soql_queries,
            duration_ms=duration_ms,
            parameters_used=self.params,
            status=status,
            warnings=self._warnings,
        )

    def _describe_fields(self, object_type: str) -> List[str]:
        try:
            desc = self.provider.describe(object_type)
        except Exception:
            return []
        if "result" in desc:
            fields = desc.get("result", {}).get("fields", []) or []
            return [f.get("name") for f in fields if isinstance(f, dict) and f.get("name")]
        if "properties" in desc:
            props = desc.get("properties", []) or []
            return [p.get("name") for p in props if isinstance(p, dict) and p.get("name")]
        return []

    def get_recommended_actions(self) -> List[RecommendedAction]:
        return [
            RecommendedAction(
                action_type="create_task",
                label="Create SLA breach follow-up tasks",
                risk_level="low",
            ),
            RecommendedAction(
                action_type="flag_record",
                label="Flag SLA breaches",
                risk_level="low",
            ),
            RecommendedAction(
                action_type="export_csv",
                label="Export SLA breaches to CSV",
                risk_level="safe",
            ),
        ]


__all__ = ["BacklogSlaBreachesCheck"]
