import json
import csv
import os
import re
from datetime import datetime
from typing import Union, List, Dict, Any, Optional


def _only_digits(value: Any) -> str:
    return re.sub(r"\D+", "", str(value or ""))


def _find_first(obj: Any, keys: List[str]) -> Optional[Any]:
    """
    Procura recursivamente (dict/list) pelo primeiro campo existente em `keys`.
    Retorna o valor encontrado ou None.
    """
    if isinstance(obj, dict):
        for k in keys:
            if k in obj and obj[k] not in (None, ""):
                return obj[k]
        for v in obj.values():
            found = _find_first(v, keys)
            if found not in (None, ""):
                return found
    elif isinstance(obj, list):
        for it in obj:
            found = _find_first(it, keys)
            if found not in (None, ""):
                return found
    return None


def _parse_date_time(date_str: Any, time_str: Any) -> Optional[datetime]:
    date_s = str(date_str).strip()
    time_s = str(time_str).strip()

    # Normaliza hora
    if re.fullmatch(r"\d{6}", time_s):  # HHMMSS
        time_s = f"{time_s[0:2]}:{time_s[2:4]}:{time_s[4:6]}"
    elif re.fullmatch(r"\d{4}", time_s):  # HHMM
        time_s = f"{time_s[0:2]}:{time_s[2:4]}:00"
    elif re.fullmatch(r"\d{2}:\d{2}$", time_s):  # HH:MM
        time_s = f"{time_s}:00"

    # Normaliza data (suporta 2026-01-26, 26/01/2026, 20260126)
    parsed_date = None
    for fmt in ("%Y-%m-%d", "%d/%m/%Y", "%Y%m%d"):
        try:
            parsed_date = datetime.strptime(date_s, fmt).date()
            break
        except ValueError:
            pass

    if not parsed_date:
        return None

    try:
        parsed_time = datetime.strptime(time_s, "%H:%M:%S").time()
    except ValueError:
        return None

    return datetime.combine(parsed_date, parsed_time)


def _parse_iso_datetime(value: Any) -> Optional[datetime]:
    if value in (None, ""):
        return None
    s = str(value).strip()
    try:
        # suporta "Z" no final
        dt = datetime.fromisoformat(s.replace("Z", "+00:00"))
        # remove timezone para ficar consistente no filename
        return dt.replace(tzinfo=None)
    except ValueError:
        return None


def _extract_success_purchase(payload: Any) -> Dict[str, Any]:
    """
    Encontra o dict success_purchase (quando existir) no payload.
    """
    if isinstance(payload, dict):
        sp = payload.get("success_purchase")
        return sp if isinstance(sp, dict) else {}
    if isinstance(payload, list) and payload and isinstance(payload[0], dict):
        sp = payload[0].get("success_purchase")
        return sp if isinstance(sp, dict) else {}
    return {}


def _build_filename(payload: Any, uuid: Any) -> str:
    # Telefone: tenta vários campos comuns (success_purchase / formatted_call / etc.)
    phone_val = _find_first(
        payload,
        ["NUM_TELEFONE", "NUM_ACESSO", "telefone", "phone", "phone_number"],
    )
    phone = _only_digits(phone_val) or _only_digits(uuid) or "unknown"

    # Data/hora: tenta DATA + HORA primeiro
    date_val = _find_first(payload, ["DATA", "data", "date"])
    time_val = _find_first(payload, ["HORA", "hora", "time"])

    dt = None
    if date_val and time_val:
        dt = _parse_date_time(date_val, time_val)

    # Fallback: tenta dataISO / timestamp ISO
    if not dt:
        dt = _parse_iso_datetime(
            _find_first(payload, ["dataISO", "DATA_ISO", "timestamp", "datetime"])
        )

    # Último fallback: agora
    if not dt:
        dt = datetime.now()

    stamp = dt.strftime("%Y%m%dT%H%M%S")
    return f"{phone}_{stamp}.csv"


def json_to_csv(
    json_input: Union[str, List[Dict[str, Any]], Dict[str, Any]],
    uuid,
    encoding: str = "utf-8",
) -> None:
    # 1) Carrega o JSON (arquivo ou objeto em memória)
    if isinstance(json_input, str):
        with open(json_input, "r", encoding=encoding) as f:
            payload = json.load(f)
    else:
        payload = json_input

    print("Json de retorno", payload)

    # 2) Valida success_purchase (mantendo sua regra)
    success_purchase = _extract_success_purchase(payload)
    if not success_purchase:
        return

    # 3) Nome do arquivo: telefone_data
    fname = _build_filename(payload, uuid)

    # Base da pasta vinda da env, padrão ./recordings
    base_dir = os.getenv("EXPORT_DIR", "./recordings")

    # 4) Salva direto em ./recordings (sem subpasta do telefone)
    os.makedirs(base_dir, exist_ok=True)
    path = os.path.join(base_dir, fname)

    # 5) Normaliza os dados para lista de dicts (para achatar)
    data = payload
    if isinstance(data, dict):
        data = [data]
    elif not isinstance(data, list):
        raise ValueError("O JSON deve ser uma lista de objetos ou um único objeto.")

    if not data:
        raise ValueError("JSON está vazio, não há dados para escrever no CSV.")

    # 6) Achata cada linha
    linhas_flat: List[Dict[str, Any]] = []
    fieldnames_set = set()

    for item in data:
        if not isinstance(item, dict):
            raise ValueError("Cada item da lista JSON deve ser um objeto (dict).")

        flat_row: Dict[str, Any] = {}

        for key, value in item.items():
            if isinstance(value, dict):
                for subkey, subvalue in value.items():
                    flat_row[subkey] = subvalue
            else:
                flat_row[key] = value

        linhas_flat.append(flat_row)
        fieldnames_set.update(flat_row.keys())

    # Opcional: ordena as colunas para ficar estável
    fieldnames = sorted(fieldnames_set)

    # 7) Escreve CSV
    with open(path, "w", encoding=encoding, newline="") as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        writer.writeheader()
        for row in linhas_flat:
            writer.writerow(row)

    print(f"CSV gerado com sucesso em: {path}")
