from __future__ import annotations

import json
import re
from datetime import date, datetime
from decimal import Decimal
from pathlib import Path
from typing import Any


def ensure_dir(path: Path) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)


def read_json(path: Path, default: Any = None) -> Any:
    if not path.exists():
        return default
    with path.open("r", encoding="utf-8") as handle:
        return json.load(handle)


def write_json(path: Path, data: Any) -> None:
    """Write JSON atomically so a crash does not corrupt the file."""
    ensure_dir(path)
    tmp = path.with_suffix(path.suffix + ".tmp")
    with tmp.open("w", encoding="utf-8") as handle:
        json.dump(data, handle, ensure_ascii=False, indent=2, default=_json_default)
    tmp.replace(path)


def _json_default(value: Any) -> Any:
    if isinstance(value, Decimal):
        return float(value)
    if isinstance(value, (datetime, date)):
        return value.strftime("%Y-%m-%d")
    raise TypeError(f"Tipo no serializable: {type(value)}")


def pick(payload: Any, *keys: str) -> Any:
    """Return the first matching key in a flat or nested dict."""
    if payload is None:
        return None

    if isinstance(payload, list) and payload:
        return pick(payload[0], *keys)

    if not isinstance(payload, dict):
        return None

    for key in keys:
        if key in payload and payload[key] not in (None, ""):
            return payload[key]

    for nest in ("data", "cliente", "resultado", "result", "payload"):
        inner = payload.get(nest)
        if isinstance(inner, (dict, list)):
            found = pick(inner, *keys)
            if found not in (None, ""):
                return found

    return None


_MONTHS = {
    "jan": 1, "january": 1, "ene": 1, "enero": 1,
    "feb": 2, "february": 2, "febrero": 2,
    "mar": 3, "march": 3, "marzo": 3,
    "apr": 4, "april": 4, "abr": 4, "abril": 4,
    "may": 5, "mayo": 5,
    "jun": 6, "june": 6, "junio": 6,
    "jul": 7, "july": 7, "julio": 7,
    "aug": 8, "august": 8, "ago": 8, "agosto": 8,
    "sep": 9, "sept": 9, "september": 9, "septiembre": 9,
    "oct": 10, "october": 10, "octubre": 10,
    "nov": 11, "november": 11, "noviembre": 11,
    "dec": 12, "december": 12, "dic": 12, "diciembre": 12,
}

_SQLSERVER_DATE = re.compile(
    r"^([A-Za-z]+)\s+(\d{1,2})\s+(\d{4})"
)


def as_date(value: Any) -> str | None:
    if value in (None, ""):
        return None
    if isinstance(value, datetime):
        return value.strftime("%Y-%m-%d")
    if isinstance(value, date):
        return value.isoformat()

    text = str(value).strip()
    if len(text) >= 10 and text[4] == "-" and text[7] == "-":
        return text[:10]

    matched = _SQLSERVER_DATE.match(text)
    if matched:
        month = _MONTHS.get(matched.group(1).lower())
        if month:
            return f"{int(matched.group(3)):04d}-{month:02d}-{int(matched.group(2)):02d}"

    return None


def as_number(value: Any, default: float = 0.0) -> float | int:
    if value in (None, ""):
        return default
    if isinstance(value, Decimal):
        number = float(value)
    else:
        try:
            number = float(value)
        except (TypeError, ValueError):
            return default
    if number.is_integer():
        return int(number)
    return round(number, 2)


def as_int(value: Any, default: int = 0) -> int:
    if value in (None, ""):
        return default
    try:
        return int(value)
    except (TypeError, ValueError):
        return default
