"""Automation failure impact monitor (recommend-only)."""

from __future__ import annotations

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

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


class AutomationFailureImpactCheck(BaseScoutCheck):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self._supported_sources: List[Tuple[str, str]] = []
        self._source_fields: Dict[str, List[str]] = {}

    def preflight(self) -> PreflightResult:
        include_apex = bool(self.params.get("include_apex", True))
        include_flow = bool(self.params.get("include_flow", True))
        sources = []
        if include_apex:
            sources.append(("AsyncApexJob", "apex"))
        if include_flow:
            sources.append(("FlowInterview", "flow"))

        supported: List[Tuple[str, str]] = []
        for sobject, label in sources:
            if not self.provider.supports_object(sobject):
                continue
            try:
                desc = self.provider.describe(sobject)
                available = self._extract_field_names(desc)
                required = self._required_fields_for_source(label)
                missing = sorted(field for field in required if field not in available)
                if missing:
                    self._warnings.append(
                        f"{sobject} missing required fields; skipping source. ({', '.join(missing)})"
                    )
                    continue
                fields = list(required)
                for optional in self._optional_fields_for_source(label):
                    if optional in available and optional not in fields:
                        fields.append(optional)
                self._source_fields[sobject.lower()] = fields
                supported.append((sobject, label))
            except Exception as exc:
                self._warnings.append(f"{sobject}: {exc}")

        if not supported:
            return PreflightResult(ok=False, unsupported=True, reason="No supported automation objects.")

        self._supported_sources = supported
        return PreflightResult(ok=True)

    def execute(self) -> ScoutFinding:
        start = time.perf_counter()
        try:
            preflight = self.preflight()
            if preflight.unsupported:
                return self._unsupported_finding(preflight.reason or "Unsupported")

            evidence: List[EvidenceRow] = []
            for sobject, label in self._supported_sources:
                rows = self._query_source(sobject, label)
                evidence.extend(rows)

            status = "findings" if evidence 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=len(evidence),
                findings_count=len(evidence),
                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,
            )
        except Exception as exc:
            duration_ms = int((time.perf_counter() - start) * 1000)
            return self._error_finding(str(exc), duration_ms=duration_ms)

    def _query_source(self, sobject: str, label: str) -> List[EvidenceRow]:
        lookback_days = int(self.params.get("lookback_days", 7) or 7)
        limit = int(self.params.get("max_records", self.top_n) or self.top_n)
        filters: List[FilterExpr] = [Filter("CreatedDate", FilterOp.LAST_N_DAYS, lookback_days)]
        fields: List[str] = list(self._source_fields.get(sobject.lower(), ["Id", "CreatedDate"]))

        if sobject.lower() == "asyncapexjob":
            filters.append(Filter("Status", FilterOp.IN, ["Failed", "Aborted"]))
            job_types = self.params.get("apex_job_types") or []
            if job_types:
                filters.append(Filter("JobType", FilterOp.IN, job_types))
        elif sobject.lower() == "flowinterview":
            filters.append(Filter("InterviewStatus", FilterOp.EQ, "Error"))

        rows = self.provider.query(sobject, fields, filters, order_by="CreatedDate", order_dir="DESC", limit=limit)
        self._collect_provider_meta()
        evidence: List[EvidenceRow] = []
        for row in rows:
            record_id = row.get("Id", "")
            if not record_id:
                continue
            reason_code, reason = self._format_reason(label, row)
            fields_payload = dict(row)
            fields_payload["failure_type"] = label
            evidence.append(
                EvidenceRow(
                    record_id=record_id,
                    sobject=sobject,
                    fields=fields_payload,
                    reason=reason,
                    reason_code=reason_code,
                    severity=self.definition.severity_default,
                )
            )
        return evidence

    def _format_reason(self, label: str, row: Dict[str, Any]) -> Tuple[str, str]:
        if label == "apex":
            status = row.get("Status") or "Failed"
            detail = row.get("ExtendedStatus") or "Apex job failed"
            return "APEX_FAILURE", f"{status}: {detail}"
        status = row.get("InterviewStatus") or "Error"
        flow = row.get("FlowApiName") or row.get("FlowVersionView") or "Flow"
        return "FLOW_FAILURE", f"{flow} interview {status}"

    def _extract_field_names(self, desc: Dict[str, Any]) -> set[str]:
        if "result" in desc and isinstance(desc.get("result"), dict):
            fields = desc.get("result", {}).get("fields", []) or []
            return {f.get("name") for f in fields if isinstance(f, dict) and f.get("name")}
        fields = desc.get("fields", []) if isinstance(desc, dict) else []
        if isinstance(fields, list):
            return {f.get("name") for f in fields if isinstance(f, dict) and f.get("name")}
        return set()

    def _required_fields_for_source(self, label: str) -> List[str]:
        if label == "apex":
            return ["Id", "CreatedDate", "Status"]
        return ["Id", "CreatedDate", "InterviewStatus"]

    def _optional_fields_for_source(self, label: str) -> List[str]:
        if label == "apex":
            return ["JobType", "CompletedDate", "ExtendedStatus", "ApexClassId"]
        return ["FlowApiName", "FlowVersionView"]

    def build_count_filters(self) -> Tuple[str, List[FilterExpr]]:
        return "AsyncApexJob", []

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

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

    def get_recommended_actions(self) -> List[RecommendedAction]:
        details: Dict[str, Any] = {
            "subject": "Automation failure follow-up",
        }
        owner_id = self.params.get("task_owner_id")
        if owner_id:
            details["owner_id"] = owner_id
        return [
            RecommendedAction(
                action_type="create_task",
                label="Create tasks for automation failures",
                risk_level="low",
                details=details,
            )
        ]


__all__ = ["AutomationFailureImpactCheck"]
