"""Shared utilities for renewal-based scouts."""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List, Tuple

from .base import BaseScoutCheck, PreflightResult
from ..crm_provider import Filter, FilterExpr, FilterOp
from ..utils import parse_sf_datetime


@dataclass
class RenewalConfig:
    object_type: str
    renewal_date_field: str
    account_id_field: str
    account_name_field: str


class BaseRenewalCheck(BaseScoutCheck):
    """Shared renewal field resolution and preflight checks."""

    def _resolve_renewal_config(self) -> RenewalConfig:
        crm = (self.provider.name or "salesforce").lower()
        if crm == "hubspot":
            return RenewalConfig(
                object_type="account",
                renewal_date_field="RenewalDate",
                account_id_field="AccountId",
                account_name_field="AccountName",
            )
        if crm == "dynamics":
            return RenewalConfig(
                object_type="contract",
                renewal_date_field="RenewalDate",
                account_id_field="AccountId",
                account_name_field="AccountName",
            )
        return RenewalConfig(
            object_type="contract",
            renewal_date_field="RenewalDate",
            account_id_field="AccountId",
            account_name_field="AccountName",
        )

    def _mapped_field(self, object_type: str, canonical_field: str) -> str:
        crm_key = (self.provider.name or "salesforce").lower()
        overrides = self.definition.field_overrides or {}
        crm_overrides = overrides.get(crm_key) or overrides.get(crm_key.lower()) or {}
        obj_key = object_type.lower()
        obj_overrides = crm_overrides.get(obj_key) or crm_overrides.get(object_type) or {}
        # Allow case-insensitive field keys
        for key in (canonical_field, canonical_field.lower()):
            if key in obj_overrides:
                return str(obj_overrides[key])
        return canonical_field

    def _available_fields(self, object_type: str) -> set[str]:
        try:
            desc = self.provider.describe(object_type)
        except Exception:
            return set()
        if isinstance(desc, dict):
            if "result" in desc:
                fields = desc.get("result", {}).get("fields", []) or []
                names = {str(f.get("name", "")) for f in fields if isinstance(f, dict)}
                return {n for n in names if n}
            if "properties" in desc:
                props = desc.get("properties", []) or []
                names = {str(p.get("name", "")) for p in props if isinstance(p, dict)}
                return {n for n in names if n}
        return set()

    def _filter_fields(self, object_type: str, fields: List[str]) -> List[str]:
        available = {f.lower() for f in self._available_fields(object_type)}
        if not available:
            return fields
        kept: List[str] = []
        for field in fields:
            mapped = self._mapped_field(object_type, field)
            if "." in mapped:
                kept.append(field)
                continue
            if mapped.startswith("_") and mapped.endswith("_value"):
                kept.append(field)
                continue
            if mapped.lower() in available:
                kept.append(field)
            else:
                self._warnings.append(f"Field '{mapped}' not found on {object_type}; skipping.")
        return kept

    def preflight(self) -> PreflightResult:
        cfg = self._resolve_renewal_config()
        if not self.provider.supports_object(cfg.object_type):
            return PreflightResult(ok=False, unsupported=True, reason=f"Unsupported object: {cfg.object_type}")

        mapped_date = self._mapped_field(cfg.object_type, cfg.renewal_date_field)
        available = {f.lower() for f in self._available_fields(cfg.object_type)}
        if available and mapped_date.lower() not in available:
            return PreflightResult(
                ok=False,
                unsupported=True,
                reason=(
                    f"Field '{mapped_date}' not found on {cfg.object_type}. "
                    "Map RenewalDate via field_overrides in scout YAML."
                ),
            )
        return PreflightResult(ok=True)

    def _today_values(self, horizon_days: int | None = None) -> tuple[Any, Any | None]:
        now = datetime.now(timezone.utc)
        today = datetime(year=now.year, month=now.month, day=now.day, tzinfo=timezone.utc)
        if self.provider.name == "hubspot":
            start = int(today.timestamp() * 1000)
            if horizon_days is None:
                return start, None
            end = int((today + timedelta(days=horizon_days)).timestamp() * 1000)
            return start, end
        if self.provider.name == "dynamics":
            if horizon_days is None:
                return today, None
            return today, today + timedelta(days=horizon_days)
        start = today.date().isoformat()
        if horizon_days is None:
            return start, None
        end = (today.date() + timedelta(days=horizon_days)).isoformat()
        return start, end

    def _days_until(self, value: str | None) -> int | None:
        dt = parse_sf_datetime(value)
        if not dt:
            return None
        now = datetime.now(timezone.utc)
        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        return (dt - now).days

    def _days_overdue(self, value: str | None) -> int | None:
        dt = parse_sf_datetime(value)
        if not dt:
            return None
        now = datetime.now(timezone.utc)
        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        return (now - dt).days


__all__ = ["BaseRenewalCheck", "RenewalConfig"]
