"""Infer employee count ranges using lightweight heuristics."""

from __future__ import annotations

import os
import time
from enum import Enum
from functools import lru_cache
from typing import Any, Dict, Optional

from ._decorators import safe_transform, with_metrics


# Enforce enumeration of employee count ranges
class EmployeeRange(Enum):
    MICRO = "1-10"
    SMALL = "11-50"
    MEDIUM = "51-200"
    LARGE = "201-500"
    ENTERPRISE = "500+"


ALLOWED_RANGES = [r.value for r in EmployeeRange]

# Check if AI features are disabled
AI_DISABLED = os.getenv("FM_AI_DISABLED", "false").lower() == "true"

# Cache configuration (24 hour TTL)
CACHE_TTL = 86400  # 24 hours in seconds
_cache_timestamps = {}


def _normalize(text: Optional[str]) -> str:
    return str(text or "").strip().lower()


def _contains_any(text: str, keywords: tuple[str, ...]) -> bool:
    return any(keyword in text for keyword in keywords)


@lru_cache(maxsize=1024)
def _infer_employee_range_cached(cache_key: str) -> Dict[str, Any]:
    """Cached inference with TTL support."""
    # Check if cache entry is still valid
    if cache_key in _cache_timestamps:
        if time.time() - _cache_timestamps[cache_key] > CACHE_TTL:
            # Expired, remove from cache
            _infer_employee_range_cached.cache_clear()
            _cache_timestamps.clear()

    # Parse cache key back to components
    parts = cache_key.split("|", 2)
    name = parts[0] if len(parts) > 0 else ""
    domain = parts[1] if len(parts) > 1 else ""
    industry = parts[2] if len(parts) > 2 else ""

    result = _infer_employee_range_heuristic(name, domain, industry)
    _cache_timestamps[cache_key] = time.time()
    return result


def _infer_employee_range_heuristic(
    name: str, domain: str, industry: str
) -> Dict[str, Any]:
    """Core heuristic logic for employee count inference."""
    name_l = _normalize(name)
    domain_l = _normalize(domain)
    industry_l = _normalize(industry)

    explanations = []

    # Enterprise indicators
    if _contains_any(name_l, ("enterprise", "global", "international", "worldwide")):
        explanations.append("Contains enterprise/global keywords")
        if _contains_any(
            industry_l, ("manufacturing", "telecommunications", "banking")
        ):
            explanations.append("Industry typically large-scale")
            return {
                "range": EmployeeRange.ENTERPRISE.value,
                "confidence": 0.85,
                "reason": "enterprise_indicators",
                "explain": " + ".join(explanations),
            }
        return {
            "range": EmployeeRange.ENTERPRISE.value,
            "confidence": 0.75,
            "reason": "enterprise_keyword",
            "explain": explanations[0],
        }

    # Startup indicators
    if _contains_any(name_l, ("startup", "labs", "studio", "ventures", "seed")):
        explanations.append("Contains startup/early-stage keywords")
        return {
            "range": EmployeeRange.MICRO.value,
            "confidence": 0.65,
            "reason": "startup_keyword",
            "explain": explanations[0],
        }

    # Small business indicators
    if _contains_any(name_l, ("llc", "consulting", "advisory", "boutique")):
        explanations.append("Business structure suggests small company")
        return {
            "range": EmployeeRange.SMALL.value,
            "confidence": 0.55,
            "reason": "small_business",
            "explain": explanations[0],
        }

    # Industry-based defaults
    industry_defaults = {
        "software": (
            EmployeeRange.MEDIUM.value,
            0.5,
            "Software companies typically mid-sized",
        ),
        "technology": (
            EmployeeRange.MEDIUM.value,
            0.5,
            "Tech companies typically mid-sized",
        ),
        "retail": (
            EmployeeRange.LARGE.value,
            0.45,
            "Retail often requires more employees",
        ),
        "healthcare": (
            EmployeeRange.LARGE.value,
            0.45,
            "Healthcare organizations typically larger",
        ),
        "education": (
            EmployeeRange.MEDIUM.value,
            0.4,
            "Educational institutions vary in size",
        ),
    }

    for keyword, (range_val, conf, explanation) in industry_defaults.items():
        if keyword in industry_l:
            return {
                "range": range_val,
                "confidence": conf,
                "reason": f"{keyword}_industry",
                "explain": explanation,
            }

    # Fallback
    return {
        "range": EmployeeRange.SMALL.value,
        "confidence": 0.35,
        "reason": "fallback",
        "explain": "No specific indicators found, using default",
    }


@safe_transform
@with_metrics
def infer_employee_count(payload: Dict[str, Any]) -> Dict[str, Any]:
    """Infer employee count with caching, bounds enforcement, and explanations."""

    # Extract fields from payload
    if "row" in payload:
        # Row-based operation
        row = payload.get("row", [])
        headers = payload.get("headers", [])
        header_map = {h.lower(): i for i, h in enumerate(headers) if h}

        # Try to extract company, domain, industry from row
        name = ""
        domain = ""
        industry = ""

        for key in ["company", "name", "organization", "account"]:
            if key in header_map and header_map[key] < len(row):
                name = str(row[header_map[key]] or "")
                break

        for key in ["domain", "website", "url"]:
            if key in header_map and header_map[key] < len(row):
                domain = str(row[header_map[key]] or "")
                break

        for key in ["industry", "sector", "vertical"]:
            if key in header_map and header_map[key] < len(row):
                industry = str(row[header_map[key]] or "")
                break
    else:
        # Direct payload
        name = payload.get("name") or payload.get("company") or ""
        domain = payload.get("domain") or payload.get("website") or ""
        industry = payload.get("industry") or payload.get("sector") or ""

    # Check if AI is disabled - use fallback heuristics only
    if AI_DISABLED:
        # Simple size mapping based on limited signals
        if any(term in name.lower() for term in ["inc", "corp", "global"]):
            range_val = EmployeeRange.LARGE.value
            confidence = 0.3
            explanation = "Corporate structure (AI disabled)"
        else:
            range_val = EmployeeRange.SMALL.value
            confidence = 0.2
            explanation = "Default fallback (AI disabled)"

        return {
            "value": [range_val, f"{confidence:.2f}"],
            "meta": {"reason": "ai_disabled", "explain": explanation, "cached": False},
        }

    # Create cache key
    cache_key = f"{name}|{domain}|{industry}"

    # Get result from cache or compute
    result = _infer_employee_range_cached(cache_key)

    # Enforce enumeration bounds
    range_label = (
        result["range"]
        if result["range"] in ALLOWED_RANGES
        else EmployeeRange.SMALL.value
    )
    confidence = max(0.0, min(1.0, result.get("confidence", 0.35)))

    return {
        "value": [range_label, f"{confidence:.2f}"],
        "meta": {
            "reason": result.get("reason", "fallback"),
            "explain": result.get("explain", "No explanation available"),
            "name": name[:50] if name else None,  # Truncate for privacy
            "domain": domain[:50] if domain else None,
            "industry": industry[:50] if industry else None,
            "cached": cache_key in _cache_timestamps,
            "confidence": confidence,
        },
    }
