"""Salesforce CRM provider for Scout checks."""

from __future__ import annotations

from typing import Any, Dict, List

from g_gremlin import sfdc as sfdc_helpers

from ..crm_provider import Filter, FilterExpr, FilterOp, OrGroup
from ..field_mapping import FieldMapper


class SFDCProvider:
    name = "salesforce"

    def __init__(self, org_alias: str | None = None, field_overrides: Dict[str, Dict[str, str]] | None = None) -> None:
        self.org_alias = org_alias
        self.mapper = FieldMapper("salesforce", overrides=field_overrides)
        self._warnings: List[str] = []
        self._queries: List[str] = []

    def count(self, object_type: str, filters: List[FilterExpr]) -> int:
        native_object = self.mapper.native_object(object_type)
        if not native_object:
            return 0
        where_clause = self._build_where(object_type, filters)
        query = f"SELECT COUNT() FROM {native_object}"
        if where_clause:
            query += f" WHERE {where_clause}"
        self._queries.append(query)
        rows = sfdc_helpers.run_soql(query, org_alias=self.org_alias)
        return self._parse_count_rows(rows)

    def query(
        self,
        object_type: str,
        fields: List[str],
        filters: List[FilterExpr],
        order_by: str | None = None,
        order_dir: str = "ASC",
        nulls_first: bool = False,
        limit: int = 100,
    ) -> List[Dict[str, Any]]:
        native_object = self.mapper.native_object(object_type)
        if not native_object:
            return []
        native_fields = self.mapper.to_native_fields(object_type, fields)
        select_clause = ", ".join(native_fields)
        where_clause = self._build_where(object_type, filters)
        query = f"SELECT {select_clause} FROM {native_object}"
        if where_clause:
            query += f" WHERE {where_clause}"
        if order_by:
            native_order = self.mapper.to_native_field(object_type, order_by)
            query += f" ORDER BY {native_order} {order_dir}"
            if nulls_first:
                query += " NULLS FIRST"
        if limit:
            query += f" LIMIT {int(limit)}"
        self._queries.append(query)
        rows = sfdc_helpers.run_soql(query, org_alias=self.org_alias)
        return [self.mapper.from_native_row(object_type, row) for row in rows]

    def describe(self, object_type: str) -> Dict[str, Any]:
        native_object = self.mapper.native_object(object_type) or object_type
        return sfdc_helpers.describe_sobject(native_object, org_alias=self.org_alias)

    def update_record(self, object_type: str, record_id: str, values: Dict[str, Any]) -> Dict[str, Any]:
        native_object = self.mapper.native_object(object_type) or object_type
        native_values = self._map_values(object_type, values)
        return sfdc_helpers.update_record(native_object, record_id, native_values, org_alias=self.org_alias)

    def create_record(self, object_type: str, values: Dict[str, Any]) -> Dict[str, Any]:
        native_object = self.mapper.native_object(object_type) or object_type
        native_values = self._map_values(object_type, values)
        return sfdc_helpers.create_record(native_object, native_values, org_alias=self.org_alias)

    def supports_object(self, object_type: str) -> bool:
        return self.mapper.native_object(object_type) is not None

    def drain_warnings(self) -> List[str]:
        warnings = list(self._warnings)
        self._warnings.clear()
        return warnings

    def drain_queries(self) -> List[str]:
        queries = list(self._queries)
        self._queries.clear()
        return queries

    def _build_where(self, object_type: str, filters: List[FilterExpr]) -> str:
        clauses: List[str] = []
        for item in filters:
            if isinstance(item, OrGroup):
                inner = [self._filter_to_soql(object_type, f) for f in item.filters if f is not None]
                if inner:
                    clauses.append(f"({' OR '.join(inner)})")
            else:
                clause = self._filter_to_soql(object_type, item)
                if clause:
                    clauses.append(clause)
        return " AND ".join(clauses)

    def _filter_to_soql(self, object_type: str, flt: Filter) -> str:
        field = self.mapper.to_native_field(object_type, flt.field)
        op = flt.op
        value = flt.value

        if op == FilterOp.IS_NULL:
            return f"{field} = NULL"
        if op == FilterOp.IS_NOT_NULL:
            return f"{field} != NULL"
        if op == FilterOp.BEFORE_TODAY:
            return f"{field} < TODAY"
        if op == FilterOp.LAST_N_DAYS:
            return f"{field} >= LAST_N_DAYS:{int(value)}"
        if op == FilterOp.NOT_LAST_N_DAYS:
            return f"{field} < LAST_N_DAYS:{int(value)}"

        if op in (FilterOp.IN, FilterOp.NOT_IN):
            values = value or []
            if not values:
                return "Id = NULL" if op == FilterOp.IN else "Id != NULL"
            escaped = ", ".join(self._format_value(v) for v in values)
            op_token = "IN" if op == FilterOp.IN else "NOT IN"
            return f"{field} {op_token} ({escaped})"

        op_map = {
            FilterOp.EQ: "=",
            FilterOp.NEQ: "!=",
            FilterOp.LT: "<",
            FilterOp.LTE: "<=",
            FilterOp.GT: ">",
            FilterOp.GTE: ">=",
        }
        if op in op_map:
            if value is None:
                return f"{field} = NULL" if op == FilterOp.EQ else f"{field} != NULL"
            return f"{field} {op_map[op]} {self._format_value(value)}"

        raise ValueError(f"Unsupported filter op: {op}")

    def _format_value(self, value: Any) -> str:
        if isinstance(value, bool):
            return "true" if value else "false"
        if isinstance(value, (int, float)):
            return str(value)
        escaped = sfdc_helpers.escape_soql_value(value)
        return f"'{escaped}'"

    def _map_values(self, object_type: str, values: Dict[str, Any]) -> Dict[str, Any]:
        mapped: Dict[str, Any] = {}
        for key, value in values.items():
            mapped_key = self.mapper.to_native_field(object_type, key)
            mapped[str(mapped_key)] = value
        return mapped

    def _parse_count_rows(self, rows: List[Dict[str, str]]) -> int:
        if not rows:
            return 0
        row = rows[0]
        for key in ("expr0", "count", "total", "totalcount", "cnt"):
            if key in row:
                try:
                    return int(float(row[key]))
                except Exception:
                    return 0
        try:
            return int(float(next(iter(row.values()))))
        except Exception:
            return 0


__all__ = ["SFDCProvider"]
