"""Inactive owner records check."""

from __future__ import annotations

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 chunked


class InactiveOwnersCheck(BaseScoutCheck):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self._flag_field = str(self.params.get("flag_field") or "Needs_Review__c")
        self._flag_field_supported: bool | None = None

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

    def build_sample_request(self) -> Tuple[str, List[str], List[FilterExpr], str | None, str, bool]:
        return "user", ["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", "Contact", "Account", "Opportunity"]) or []
        sobjects = [str(s).lower() for s in sobjects]
        max_inactive = int(self.params.get("max_inactive_users", 500))

        inactive_users = self._get_inactive_users(max_inactive)
        if not inactive_users:
            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=0,
                findings_count=0,
                evidence=[],
                recommended_actions=self.get_recommended_actions(),
                soql_queries=self._soql_queries,
                duration_ms=duration_ms,
                parameters_used=self.params,
                status="ok",
                warnings=self._warnings,
            )

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

            for chunk in chunked(inactive_ids, size=200):
                filters = [Filter("OwnerId", FilterOp.IN, list(chunk))]
                total_scanned += self.provider.count(obj, filters)
                self._collect_provider_meta()

                if len(evidence) >= self.top_n:
                    continue
                rows = self.provider.query(
                    obj,
                    ["Id", "Name", "OwnerId", "CreatedDate", "LastActivityDate"],
                    filters,
                    order_by="CreatedDate",
                    order_dir="DESC",
                    limit=self.top_n - len(evidence),
                )
                self._collect_provider_meta()

                for row in rows:
                    record_id = row.get("Id", "")
                    if not record_id:
                        continue
                    owner_id = row.get("OwnerId")
                    owner_name = inactive_users.get(owner_id, "")
                    evidence.append(
                        EvidenceRow(
                            record_id=record_id,
                            sobject=obj,
                            fields={
                                "Name": row.get("Name"),
                                "OwnerId": owner_id,
                                "owner_name": owner_name,
                                "CreatedDate": row.get("CreatedDate"),
                                "LastActivityDate": row.get("LastActivityDate"),
                            },
                            reason=f"Record owned by inactive user {owner_name or owner_id}",
                            reason_code="INACTIVE_OWNER",
                            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 _get_inactive_users(self, max_inactive: int) -> Dict[str, str]:
        if self.provider.name == "hubspot":
            return self._get_inactive_hubspot_owners(max_inactive)
        rows = self.provider.query(
            "user",
            ["Id", "Name", "Email", "IsActive"],
            [Filter("IsActive", FilterOp.EQ, False)],
            limit=max_inactive,
        )
        self._collect_provider_meta()
        result: Dict[str, str] = {}
        for row in rows:
            user_id = row.get("Id")
            if not user_id:
                continue
            name = row.get("Name") or row.get("Email") or ""
            result[str(user_id)] = str(name)
        return result

    def _get_inactive_hubspot_owners(self, max_inactive: int) -> Dict[str, str]:
        if not hasattr(self.provider, "list_owners"):
            return {}
        owners = self.provider.list_owners(include_archived=True)  # type: ignore[attr-defined]
        result: Dict[str, str] = {}
        for owner in owners:
            if owner.get("archived") is not True:
                continue
            owner_id = owner.get("id")
            if not owner_id:
                continue
            email = owner.get("email") or owner.get("emailAddress") or ""
            result[str(owner_id)] = str(email)
            if len(result) >= max_inactive:
                break
        return result

    def get_recommended_actions(self) -> List[RecommendedAction]:
        actions: List[RecommendedAction] = [
            RecommendedAction(
                action_type="export_csv",
                label="Export inactive owner records to CSV",
                risk_level="safe",
            ),
            RecommendedAction(
                action_type="create_task",
                label="Create tasks to reassign inactive owner records",
                risk_level="low",
            ),
        ]
        if self._flag_field_supported_check():
            actions.append(
                RecommendedAction(
                    action_type="flag_record",
                    label="Flag inactive owner records",
                    risk_level="low",
                    details={"field": self._flag_field},
                )
            )
        if self._has_reassignment_config():
            actions.append(
                RecommendedAction(
                    action_type="reassign_owner",
                    label="Reassign to default owner/queue",
                    risk_level="medium",
                    details={
                        "requires_config": True,
                        "config_keys": ["default_owner_id", "default_queue_id"],
                    },
                )
            )
        return actions

    def _has_reassignment_config(self) -> bool:
        cfg = self.params.get("reassignment_config") or {}
        if not isinstance(cfg, dict):
            return False
        configured = False
        for key, value in cfg.items():
            if not isinstance(value, dict):
                continue
            if value.get("default_owner_id") or value.get("default_queue_id"):
                configured = True
                break
        return configured

    def _flag_field_supported_check(self) -> bool:
        if self._flag_field_supported is not None:
            return self._flag_field_supported
        field = self._flag_field
        sobjects = self.params.get("sobjects", ["Lead", "Contact", "Account", "Opportunity"]) or []
        for obj in sobjects:
            if not self.provider.supports_object(obj):
                continue
            try:
                desc = self.provider.describe(obj)
                available = self._extract_field_names(desc)
                if field in available:
                    self._flag_field_supported = True
                    return True
            except Exception:
                continue
        self._flag_field_supported = False
        return False

    def _extract_field_names(self, desc: Dict[str, Any]) -> set[str]:
        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 set()


__all__ = ["InactiveOwnersCheck"]
