"""Pushed deals check."""

from __future__ import annotations

import time
from typing import Dict, List, Tuple

from g_gremlin import sfdc as sfdc_helpers

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


class PushedDealsCheck(BaseScoutCheck):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self._history_available = True

    def preflight(self) -> PreflightResult:
        if self.provider.name != "salesforce" or not self.provider.supports_object("deal_history"):
            self._history_available = False
            self._warnings.append("Deal history unsupported; using fallback path.")
            return PreflightResult(ok=True)
        try:
            query = "SELECT COUNT() FROM OpportunityFieldHistory WHERE Field = 'CloseDate'"
            self._soql_queries.append(query)
            rows = sfdc_helpers.run_soql(query, org_alias=self.org_alias)
            count = self._parse_count_rows(rows)
            if count == 0:
                self._history_available = False
                self._warnings.append(
                    "CloseDate history not detected; using fallback (open opps past CloseDate)."
                )
        except Exception as exc:
            self._history_available = False
            self._warnings.append(f"CloseDate history unavailable; using fallback. ({exc})")
        return PreflightResult(ok=True)

    def build_count_filters(self) -> Tuple[str, List[FilterExpr]]:
        filters: List[FilterExpr] = [
            Filter("IsClosed", FilterOp.EQ, False),
            Filter("CloseDate", FilterOp.BEFORE_TODAY),
        ]
        return "opportunity", filters

    def build_sample_request(self) -> Tuple[str, List[str], List[FilterExpr], str | None, str, bool]:
        filters: List[FilterExpr] = [
            Filter("IsClosed", FilterOp.EQ, False),
            Filter("CloseDate", FilterOp.BEFORE_TODAY),
        ]
        fields = ["Id", "Name", "StageName", "CloseDate", "Amount", "OwnerId"]
        return "opportunity", fields, filters, "CloseDate", "ASC", False

    def execute(self) -> ScoutFinding:  # type: ignore[override]
        if self.provider.name == "salesforce" and self._history_available:
            return self._execute_history_mode()
        return super().execute()

    def _execute_history_mode(self) -> ScoutFinding:
        start = time.perf_counter()
        push_threshold = int(self.params.get("push_threshold", 2))
        lookback_days = int(self.params.get("lookback_days", 365))

        history_query = (
            "SELECT OpportunityId, COUNT(Id) "
            "FROM OpportunityFieldHistory "
            "WHERE Field = 'CloseDate' "
            f"AND CreatedDate >= LAST_N_DAYS:{lookback_days} "
            "GROUP BY OpportunityId "
            f"HAVING COUNT(Id) >= {push_threshold}"
        )
        self._soql_queries.append(history_query)
        raw_history_rows = sfdc_helpers.run_soql(history_query, org_alias=self.org_alias)

        history_rows: List[Dict[str, str]] = []
        for row in raw_history_rows:
            opp_id = row.get("OpportunityId") or row.get("Id")
            if not opp_id:
                continue
            count_val = row.get("PushCount") or row.get("expr0") or row.get("count")
            try:
                push_count = int(float(count_val)) if count_val is not None else 0
            except Exception:
                push_count = 0
            history_rows.append(
                {
                    "OpportunityId": str(opp_id),
                    "PushCount": str(push_count),
                }
            )

        if not history_rows:
            duration_ms = int((time.perf_counter() - start) * 1000)
            return self._build_result([], 0, duration_ms)

        history_rows.sort(
            key=lambda row: int(float(row.get("PushCount") or 0)),
            reverse=True,
        )
        count = len(history_rows)
        history_rows = history_rows[: self.top_n]

        opp_ids: List[str] = []
        push_counts: Dict[str, int] = {}
        for row in history_rows:
            opp_id = row.get("OpportunityId") or row.get("Id")
            if not opp_id:
                continue
            opp_ids.append(opp_id)
            count_val = row.get("PushCount") or row.get("expr0") or row.get("count")
            try:
                push_counts[opp_id] = int(float(count_val)) if count_val is not None else 0
            except Exception:
                push_counts[opp_id] = 0

        if not opp_ids:
            duration_ms = int((time.perf_counter() - start) * 1000)
            return self._build_result([], count, duration_ms)

        escaped = ", ".join(f"'{sfdc_helpers.escape_soql_value(i)}'" for i in opp_ids)
        opp_query = (
            "SELECT Id, Name, StageName, CloseDate, Amount, OwnerId "
            "FROM Opportunity "
            "WHERE IsClosed = false "
            f"AND Id IN ({escaped})"
        )
        self._soql_queries.append(opp_query)
        opp_rows = sfdc_helpers.run_soql(opp_query, org_alias=self.org_alias)
        for row in opp_rows:
            opp_id = row.get("Id")
            if opp_id in push_counts:
                row["PushCount"] = str(push_counts[opp_id])
        evidence = self.evaluate(opp_rows)
        duration_ms = int((time.perf_counter() - start) * 1000)
        return self._build_result(evidence, count, duration_ms)

    def _build_result(self, evidence: List[EvidenceRow], count: int, duration_ms: int) -> ScoutFinding:
        status = "findings" if count > 0 else "ok"
        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=count,
            findings_count=count,
            evidence=evidence[: self.top_n],
            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 evaluate(self, rows: List[Dict[str, str]]) -> List[EvidenceRow]:
        evidence: List[EvidenceRow] = []

        if not self._history_available:
            for row in rows:
                record_id = row.get("Id", "")
                if not record_id:
                    continue
                evidence.append(
                    EvidenceRow(
                        record_id=record_id,
                        sobject="opportunity",
                        fields={
                            "Name": row.get("Name"),
                            "StageName": row.get("StageName"),
                            "CloseDate": row.get("CloseDate"),
                            "Amount": row.get("Amount"),
                            "OwnerId": row.get("OwnerId"),
                        },
                        reason="CloseDate is in the past while opportunity is open",
                        reason_code="PAST_CLOSE_DATE",
                        severity=self.definition.severity_default,
                    )
                )
            return evidence

        for row in rows:
            record_id = row.get("Id", "")
            if not record_id:
                continue
            push_count = row.get("PushCount") or "0"
            evidence.append(
                EvidenceRow(
                    record_id=record_id,
                    sobject="opportunity",
                    fields={
                        "Name": row.get("Name"),
                        "StageName": row.get("StageName"),
                        "CloseDate": row.get("CloseDate"),
                        "Amount": row.get("Amount"),
                        "OwnerId": row.get("OwnerId"),
                        "push_count": push_count,
                    },
                    reason=f"CloseDate pushed {push_count} times",
                    reason_code="PUSHED_N_TIMES",
                    severity=self.definition.severity_default,
                )
            )

        return evidence

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


__all__ = ["PushedDealsCheck"]
