"""Contactability hygiene 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 ..domain_utils import extract_registrable_domain, is_free_domain
from ..models import EvidenceRow, RecommendedAction, ScoutFinding


class ContactabilityCheck(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", "Contact"]) or ["Lead", "Contact"]
        sobjects = [str(s).lower() for s in sobjects]
        check_email = bool(self.params.get("check_missing_email", True))
        check_phone = bool(self.params.get("check_missing_phone", False))
        check_personal = bool(self.params.get("check_personal_domain", True))
        check_bounced = bool(self.params.get("check_bounced", True))
        lookback = int(self.params.get("lookback_days", 0))
        exclude = self.params.get("exclude_statuses", []) or []

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

            available = self._describe_fields(obj)
            available_lower = {f.lower() for f in available}
            has_phone = "phone" in available_lower
            has_bounced = "emailbounceddate" in available_lower

            fields = ["Id", "Name", "Email", "OwnerId", "Status", "CreatedDate"]
            if has_phone:
                fields.append("Phone")
            if has_bounced and check_bounced:
                fields.append("EmailBouncedDate")

            filters: List[FilterExpr] = []
            if lookback:
                filters.append(Filter("CreatedDate", FilterOp.LAST_N_DAYS, lookback))
            if exclude and ("status" in available_lower or "statuscode" in available_lower or "hs_lead_status" in available_lower):
                filters.append(Filter("Status", FilterOp.NOT_IN, exclude))

            total_scanned += self.provider.count(obj, filters)
            self._collect_provider_meta()
            rows = self.provider.query(
                obj,
                fields,
                filters,
                order_by="CreatedDate",
                order_dir="DESC",
                limit=self.top_n,
            )
            self._collect_provider_meta()

            for row in rows:
                record_id = row.get("Id", "")
                if not record_id:
                    continue
                email = row.get("Email")
                phone = row.get("Phone") if has_phone else None

                base_fields = {
                    "Name": row.get("Name"),
                    "Email": email,
                    "Phone": phone,
                    "Status": row.get("Status"),
                    "OwnerId": row.get("OwnerId"),
                    "CreatedDate": row.get("CreatedDate"),
                }

                if check_email and not email:
                    evidence.append(
                        EvidenceRow(
                            record_id=record_id,
                            sobject=obj,
                            fields=dict(base_fields),
                            reason="No email address",
                            reason_code="MISSING_EMAIL",
                            severity=self.definition.severity_default,
                        )
                    )

                if check_phone and has_phone and not phone:
                    evidence.append(
                        EvidenceRow(
                            record_id=record_id,
                            sobject=obj,
                            fields=dict(base_fields),
                            reason="No phone number",
                            reason_code="MISSING_PHONE",
                            severity=self.definition.severity_default,
                        )
                    )

                if check_personal and email:
                    domain = extract_registrable_domain(email)
                    if domain and is_free_domain(domain):
                        evidence.append(
                            EvidenceRow(
                                record_id=record_id,
                                sobject=obj,
                                fields={**base_fields, "email_domain": domain},
                                reason=f"Personal email domain: {domain}",
                                reason_code="PERSONAL_DOMAIN",
                                severity=self.definition.severity_default,
                            )
                        )

                if check_bounced and has_bounced and row.get("EmailBouncedDate"):
                    evidence.append(
                        EvidenceRow(
                            record_id=record_id,
                            sobject=obj,
                            fields=dict(base_fields),
                            reason=f"Email bounced on {row.get('EmailBouncedDate')}",
                            reason_code="EMAIL_BOUNCED",
                            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="export_csv",
                label="Export contactability issues",
                risk_level="safe",
            ),
            RecommendedAction(
                action_type="create_task",
                label="Create tasks for contactability issues",
                risk_level="low",
            ),
            RecommendedAction(
                action_type="flag_record",
                label="Flag contactability issues",
                risk_level="low",
            ),
        ]


__all__ = ["ContactabilityCheck"]
