"""Deterministic CRM field mapping suggestions for discovery."""

from __future__ import annotations

from typing import Iterable

from g_gremlin.autopilot.discovery import FieldMappingSuggestion, ObjectProfile


class DiscoveryFieldMapper:
    """Generate deterministic field mapping suggestions for locked runtime targets."""

    _DNC_STANDARD_FIELDS: tuple[str, ...] = ("HasOptedOutOfEmail", "DoNotCall", "HasOptedOutOfFax")
    _DNC_CUSTOM_TOKENS: tuple[str, ...] = ("opt", "suppress", "dnc", "donotcontact", "unsubscribe")
    _STAGE_TOKENS: tuple[str, ...] = ("stage", "status", "phase")
    _LEAD_DOMAIN_TOKENS: tuple[str, ...] = ("domain", "website")
    _ACCOUNT_DOMAIN_TOKENS: tuple[str, ...] = ("domain", "url", "website")
    _DEAL_CUSTOM_TOKENS: tuple[str, ...] = ("amount", "price", "revenue", "date", "close")

    def __init__(self, object_profiles: list[ObjectProfile], crm: str = "salesforce") -> None:
        self.object_profiles = object_profiles
        self.crm = (crm or "salesforce").lower()

    def suggest(self) -> list[FieldMappingSuggestion]:
        if self.crm != "salesforce":
            return []

        ordered = []
        ordered.extend(self._suggest_dnc_suppression())
        ordered.extend(self._suggest_stage_field())
        ordered.extend(self._suggest_lead_domain())
        ordered.extend(self._suggest_account_domain())
        ordered.extend(self._suggest_dealdesk_reconciliation())
        return self._dedupe(ordered)

    def _suggest_dnc_suppression(self) -> list[FieldMappingSuggestion]:
        suggestions: list[FieldMappingSuggestion] = []
        fields = self._collect_fields(("Lead", "Contact"))
        lower_to_original = {field.lower(): field for field in fields}

        for standard_field in self._DNC_STANDARD_FIELDS:
            matched = lower_to_original.get(standard_field.lower())
            if not matched:
                continue
            suggestions.append(
                FieldMappingSuggestion(
                    target="do_not_contact.suppression_fields",
                    source_field=matched,
                    confidence=1.0,
                    reason=f"Standard Salesforce DNC field matches do_not_contact.suppression_fields default ({matched})",
                    needs_confirmation=False,
                )
            )

        for field_name in fields:
            lowered = field_name.lower()
            if not lowered.endswith("__c"):
                continue
            if not any(token in lowered for token in self._DNC_CUSTOM_TOKENS):
                continue
            suggestions.append(
                FieldMappingSuggestion(
                    target="do_not_contact.custom_suppression_field",
                    source_field=field_name,
                    confidence=0.7,
                    reason=(
                        "Custom Salesforce field name contains suppression token "
                        f"for do_not_contact.custom_suppression_field ({field_name})"
                    ),
                    needs_confirmation=True,
                )
            )
        return suggestions

    def _suggest_stage_field(self) -> list[FieldMappingSuggestion]:
        suggestions: list[FieldMappingSuggestion] = []
        fields = self._fields_for("Opportunity")
        lower_to_original = {field.lower(): field for field in fields}

        standard = lower_to_original.get("stagename")
        if standard:
            suggestions.append(
                FieldMappingSuggestion(
                    target="required_fields_by_stage.stage_field",
                    source_field=standard,
                    confidence=1.0,
                    reason="Standard Salesforce stage field matches required_fields_by_stage.stage_field default",
                    needs_confirmation=False,
                )
            )

        for field_name in fields:
            lowered = field_name.lower()
            if not any(token in lowered for token in self._STAGE_TOKENS):
                continue
            suggestions.append(
                FieldMappingSuggestion(
                    target="required_fields_by_stage.stage_field",
                    source_field=field_name,
                    confidence=0.6,
                    reason=(
                        "Field name contains stage/status/phase token for "
                        f"required_fields_by_stage.stage_field ({field_name})"
                    ),
                    needs_confirmation=True,
                )
            )
        return suggestions

    def _suggest_lead_domain(self) -> list[FieldMappingSuggestion]:
        suggestions: list[FieldMappingSuggestion] = []
        fields = self._fields_for("Lead")
        lower_to_original = {field.lower(): field for field in fields}

        standard = lower_to_original.get("email")
        if standard:
            suggestions.append(
                FieldMappingSuggestion(
                    target="unmatched_leads.lead_domain_field",
                    source_field=standard,
                    confidence=1.0,
                    reason="Standard Salesforce Email field matches unmatched_leads.lead_domain_field default",
                    needs_confirmation=False,
                )
            )

        for field_name in fields:
            lowered = field_name.lower()
            if not lowered.endswith("__c"):
                continue
            if not any(token in lowered for token in self._LEAD_DOMAIN_TOKENS):
                continue
            suggestions.append(
                FieldMappingSuggestion(
                    target="unmatched_leads.lead_domain_field",
                    source_field=field_name,
                    confidence=0.6,
                    reason=(
                        "Custom Lead field name contains domain/website token for "
                        f"unmatched_leads.lead_domain_field ({field_name})"
                    ),
                    needs_confirmation=True,
                )
            )
        return suggestions

    def _suggest_account_domain(self) -> list[FieldMappingSuggestion]:
        suggestions: list[FieldMappingSuggestion] = []
        fields = self._fields_for("Account")
        lower_to_original = {field.lower(): field for field in fields}

        standard = lower_to_original.get("website")
        if standard:
            suggestions.append(
                FieldMappingSuggestion(
                    target="unmatched_leads.account_domain_field",
                    source_field=standard,
                    confidence=1.0,
                    reason="Standard Salesforce Website field matches unmatched_leads.account_domain_field default",
                    needs_confirmation=False,
                )
            )

        for field_name in fields:
            lowered = field_name.lower()
            if not lowered.endswith("__c"):
                continue
            if not any(token in lowered for token in self._ACCOUNT_DOMAIN_TOKENS):
                continue
            suggestions.append(
                FieldMappingSuggestion(
                    target="unmatched_leads.account_domain_field",
                    source_field=field_name,
                    confidence=0.6,
                    reason=(
                        "Custom Account field name contains domain/url/website token for "
                        f"unmatched_leads.account_domain_field ({field_name})"
                    ),
                    needs_confirmation=True,
                )
            )
        return suggestions

    def _suggest_dealdesk_reconciliation(self) -> list[FieldMappingSuggestion]:
        suggestions: list[FieldMappingSuggestion] = []
        fields = self._fields_for("Opportunity")
        lower_to_original = {field.lower(): field for field in fields}

        for standard_name in ("Amount", "CloseDate"):
            matched = lower_to_original.get(standard_name.lower())
            if not matched:
                continue
            suggestions.append(
                FieldMappingSuggestion(
                    target="deal_desk.field_reconciliation_rules[].crm_field",
                    source_field=matched,
                    confidence=0.8,
                    reason=(
                        "Standard Opportunity reconciliation field candidate for "
                        f"deal_desk.field_reconciliation_rules[].crm_field ({matched})"
                    ),
                    needs_confirmation=True,
                )
            )

        for field_name in fields:
            lowered = field_name.lower()
            if not lowered.endswith("__c"):
                continue
            if not any(token in lowered for token in self._DEAL_CUSTOM_TOKENS):
                continue
            suggestions.append(
                FieldMappingSuggestion(
                    target="deal_desk.field_reconciliation_rules[].crm_field",
                    source_field=field_name,
                    confidence=0.5,
                    reason=(
                        "Custom Opportunity field name contains reconciliation token for "
                        f"deal_desk.field_reconciliation_rules[].crm_field ({field_name})"
                    ),
                    needs_confirmation=True,
                )
            )
        return suggestions

    def _collect_fields(self, sobjects: Iterable[str]) -> list[str]:
        collected: list[str] = []
        seen: set[str] = set()
        for sobject in sobjects:
            for field_name in self._fields_for(sobject):
                lowered = field_name.lower()
                if lowered in seen:
                    continue
                collected.append(field_name)
                seen.add(lowered)
        return collected

    def _fields_for(self, sobject: str) -> list[str]:
        for profile in self.object_profiles:
            if profile.sobject.lower() == sobject.lower():
                return list(profile.key_fields)
        return []

    @staticmethod
    def _dedupe(suggestions: list[FieldMappingSuggestion]) -> list[FieldMappingSuggestion]:
        deduped: list[FieldMappingSuggestion] = []
        seen: set[tuple[str, str]] = set()
        for suggestion in suggestions:
            key = (suggestion.target.lower(), suggestion.source_field.lower())
            if key in seen:
                continue
            deduped.append(suggestion)
            seen.add(key)
        return deduped


__all__ = ["DiscoveryFieldMapper"]
