from __future__ import annotations

import csv
import logging
import os
import re
from io import StringIO
from typing import Any, Dict, List, Optional

from .cache import HybridCache
from .salesforce_gateway import SalesforceGateway

logger = logging.getLogger(__name__)


FIELD_HINTS = {
    "strict_id": r"(?i)(npi|duns|provider_?id|tax_?id|ein|d_?u_?n_?s)",
    "domain": r"(?i)(website|url|domain|web_?addr)",
    "alias": r"(?i)(alias|dba|trade_?name|aka|doing_?business_?as)",
    "hierarchy": r"(?i)(parent|ultimate_?parent)",
}

_HINT_REGEX = {key: re.compile(pattern) for key, pattern in FIELD_HINTS.items()}

_PROFILE_TTL = int(os.getenv("SFDC_FIELD_PROFILE_TTL", "3600"))
_MIN_FILL_RATE = float(os.getenv("SFDC_FIELD_PROFILE_MIN_FILL", "0.10"))
_SAMPLE_SIZE = int(os.getenv("SFDC_FIELD_PROFILE_SAMPLE", "2000"))

_PROFILE_CACHE = HybridCache(namespace="sfdc_field_profile", lru_size=256, ttl=_PROFILE_TTL)


def _infer_role(field_name: str) -> Optional[str]:
    if not field_name:
        return None
    for role, rx in _HINT_REGEX.items():
        if rx.search(field_name):
            return role
    return None


def _parse_bulk_records(resp: Any) -> List[Dict[str, Any]]:
    if isinstance(resp, str):
        reader = csv.DictReader(StringIO(resp))
        return list(reader)
    if isinstance(resp, list):
        return resp
    if isinstance(resp, dict) and resp.get("records"):
        return resp["records"]
    return []


def _parse_count_value(resp: Any) -> Optional[int]:
    records = _parse_bulk_records(resp)
    if not records:
        return None
    row = records[0] or {}
    for key in ("expr0", "count", "total", "Total"):
        if key in row:
            try:
                return int(float(row.get(key)))
            except Exception:
                return None
    for val in row.values():
        try:
            return int(float(val))
        except Exception:
            continue
    return None


def _is_non_empty(value: Any) -> bool:
    if value is None:
        return False
    if isinstance(value, str):
        return bool(value.strip())
    return True


async def get_sfdc_field_profile(gateway: SalesforceGateway) -> Dict[str, Any]:
    tenant_id = str(getattr(gateway, "tenant_id", "unknown") or "unknown")
    cache_key = f"sfdc_field_profile:{tenant_id}"
    cached = await _PROFILE_CACHE.get(cache_key)
    if cached:
        return cached

    profile = await _build_profile(gateway)
    await _PROFILE_CACHE.set(cache_key, profile)
    return profile


async def _build_profile(gateway: SalesforceGateway) -> Dict[str, Any]:
    desc = await gateway.describe("Account")
    fields = desc.get("fields", []) if isinstance(desc, dict) else []
    candidates: List[Dict[str, str]] = []

    for field in fields:
        name = field.get("name") if isinstance(field, dict) else None
        if not name:
            continue
        if field.get("queryable") is False:
            continue
        role = _infer_role(name)
        if role:
            candidates.append({"name": name, "role": role})

    query_fields = ["Id", "Name"] + [c["name"] for c in candidates]
    # de-dupe while preserving order
    deduped_fields: List[str] = []
    seen = set()
    for f in query_fields:
        if f in seen:
            continue
        seen.add(f)
        deduped_fields.append(f)

    soql = (
        f"SELECT {', '.join(deduped_fields)} FROM Account "
        f"WHERE IsDeleted = false LIMIT {_SAMPLE_SIZE}"
    )

    try:
        sample_resp = await gateway.bulk_query(soql, "Account")
        records = _parse_bulk_records(sample_resp)
    except Exception as exc:
        logger.debug("SFDC profiler bulk_query failed: %s", exc)
        records = []

    sample_count = len(records)
    counts: Dict[str, int] = {c["name"]: 0 for c in candidates}
    if records:
        for row in records:
            for c in candidates:
                name = c["name"]
                if _is_non_empty(row.get(name)):
                    counts[name] += 1

    field_meta: Dict[str, Dict[str, Any]] = {}
    for c in candidates:
        name = c["name"]
        fill_rate = (counts.get(name, 0) / sample_count) if sample_count else 0.0
        if fill_rate >= _MIN_FILL_RATE:
            field_meta[name] = {"fill_rate": round(fill_rate, 4), "role": c["role"]}

    blocking_fields = [
        name
        for name, meta in field_meta.items()
        if meta.get("role") in {"domain", "strict_id"}
    ]
    match_fields = [
        name for name, meta in field_meta.items() if meta.get("role") == "alias"
    ]

    total_accounts = None
    try:
        count_resp = await gateway.bulk_query(
            "SELECT COUNT() FROM Account WHERE IsDeleted = false", "Account"
        )
        total_accounts = _parse_count_value(count_resp)
    except Exception as exc:
        logger.debug("SFDC profiler count query failed: %s", exc)

    profile = {
        "org_id": getattr(gateway, "org_id", None) or None,
        "tenant_id": getattr(gateway, "tenant_id", None) or None,
        "total_accounts": total_accounts or sample_count,
        "fields": field_meta,
        "blocking_fields": blocking_fields,
        "match_fields": match_fields,
        "query": soql,
    }
    return profile
