"""Unmatched leads to account check."""

from __future__ import annotations

from typing import Any, Dict, List, Tuple

from .base import BaseScoutCheck
from ..crm_provider import Filter, FilterExpr, FilterOp
from ..domain_utils import extract_domain_root, extract_registrable_domain, is_free_domain
from ..models import EvidenceRow, RecommendedAction
from ..utils import chunked


class UnmatchedLeadsCheck(BaseScoutCheck):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self._account_domains: dict[str, List[str]] = {}
        self._account_root_map: dict[str, List[str]] = {}

    def build_count_filters(self) -> Tuple[str, List[FilterExpr]]:
        lead_field = self.params.get("lead_domain_field", "Email")
        filters: List[FilterExpr] = [
            Filter("IsConverted", FilterOp.EQ, False),
            Filter(str(lead_field), FilterOp.IS_NOT_NULL),
        ]
        return "lead", filters

    def build_sample_request(self) -> Tuple[str, List[str], List[FilterExpr], str | None, str, bool]:
        lead_field = self.params.get("lead_domain_field", "Email")
        fields = ["Id", "Name", "Company", str(lead_field)]
        filters: List[FilterExpr] = [
            Filter("IsConverted", FilterOp.EQ, False),
            Filter(str(lead_field), FilterOp.IS_NOT_NULL),
        ]
        return "lead", fields, filters, "CreatedDate", "DESC", False

    def _fetch_accounts(self, domains: List[str]) -> None:
        account_field = self.params.get("account_domain_field", "Website")
        if not domains:
            return

        for chunk in chunked(domains):
            rows = self.provider.query(
                "account",
                ["Id", "Name", str(account_field)],
                [
                    Filter(str(account_field), FilterOp.IS_NOT_NULL),
                    Filter(str(account_field), FilterOp.IN, list(chunk)),
                ],
                limit=500,
            )
            self._collect_provider_meta()
            for row in rows:
                raw_value = row.get(account_field)
                domain = extract_registrable_domain(raw_value)
                if not domain:
                    continue
                self._account_domains.setdefault(domain, []).append(row.get("Id", ""))
                root = extract_domain_root(domain)
                if root:
                    self._account_root_map.setdefault(root, []).append(row.get("Id", ""))

    def fetch_sample_rows(self) -> List[Dict[str, Any]]:
        obj_type, fields, filters, order_by, order_dir, nulls_first = self.build_sample_request()
        leads = self.provider.query(
            obj_type,
            fields,
            filters,
            order_by=order_by,
            order_dir=order_dir,
            nulls_first=nulls_first,
            limit=self.top_n,
        )
        self._collect_provider_meta()
        lead_field = self.params.get("lead_domain_field", "Email")

        domains: List[str] = []
        for row in leads:
            raw_value = row.get(lead_field)
            domain = extract_registrable_domain(raw_value)
            if not domain:
                continue
            if is_free_domain(domain):
                continue
            domains.append(domain)

        unique_domains = sorted(set(domains))
        self._fetch_accounts(unique_domains)
        return leads

    def evaluate(self, rows: List[Dict[str, Any]]) -> List[EvidenceRow]:
        evidence: List[EvidenceRow] = []
        lead_field = self.params.get("lead_domain_field", "Email")
        max_candidates = int(self.params.get("max_candidates", 3))

        for row in rows:
            record_id = row.get("Id", "")
            if not record_id:
                continue
            raw_value = row.get(lead_field)
            domain = extract_registrable_domain(raw_value)
            if not domain or is_free_domain(domain):
                continue

            matched_accounts = self._account_domains.get(domain, [])
            if matched_accounts:
                continue

            root = extract_domain_root(domain) or ""
            candidates = self._account_root_map.get(root, [])[:max_candidates]
            reason_code = "POSSIBLE_MATCH" if candidates else "NO_ACCOUNT_MATCH"
            reason = (
                "Possible account matches by domain root" if candidates else "No account match for lead domain"
            )
            evidence.append(
                EvidenceRow(
                    record_id=record_id,
                    sobject="lead",
                    fields={
                        "Name": row.get("Name"),
                        "Company": row.get("Company"),
                        "lead_domain": domain,
                        "candidate_account_ids": ",".join(candidates) if candidates else "",
                    },
                    reason=reason,
                    reason_code=reason_code,
                    severity=self.definition.severity_default,
                    related_ids=candidates,
                )
            )

        return evidence

    def _findings_count(self, count: int, evidence: List[EvidenceRow]) -> int:
        if evidence and len(evidence) >= self.top_n:
            self._warnings.append(
                "Evidence capped at top_n; findings_count reflects sample size."
            )
        return len(evidence)

    def _derive_status(self, count: int, evidence: List[EvidenceRow]) -> str:
        return "findings" if evidence else "ok"

    def get_recommended_actions(self) -> List[RecommendedAction]:
        return [
            RecommendedAction(
                action_type="export_csv",
                label="Export unmatched leads to CSV",
                risk_level="safe",
            ),
            RecommendedAction(
                action_type="manual_review",
                label="Review leads for potential account matching",
                risk_level="low",
            ),
        ]


__all__ = ["UnmatchedLeadsCheck"]
