import os
import json
import time
import logging
from datetime import datetime, timedelta
import pymysql
import requests
from dotenv import load_dotenv
import re
load_dotenv()

DB_HOST = os.getenv('DB_HOST')
DB_USER = os.getenv('DB_USER')
DB_PASSWORD = os.getenv('DB_PASSWORD')
DB_NAME = os.getenv('DB_NAME')


DB_CONFIG = {
    'host': DB_HOST,
    'user': DB_USER,
    'password': DB_PASSWORD,
    'database': DB_NAME,
    'cursorclass': pymysql.cursors.DictCursor
}




# Datos de la API GCI
GCI_APIKEY = os.getenv("GCI_APIKEY")
GCI_LOGIN_URL = os.getenv("GCI_LOGIN_URL")  # p.e.: "https://api-gci-rest-pruebas.integracionplanok.io/api/login?apikey=..."
# Ajusta tu 'username' y 'password' a lo que necesites
GCI_USERNAME = os.getenv("GCI_USERNAME")  # Ej: 76769507 (sin comillas)
GCI_PASSWORD = os.getenv("GCI_PASSWORD")

# Archivo donde guardaremos el token
TOKEN_CACHE_FILE = "gci_token_cache.json"

def _obtener_nuevo_token():
    if not GCI_USERNAME:
        raise ValueError("No se definió GCI_USERNAME en .env o está vacío.")
    
    payload = {
        "username": int(GCI_USERNAME),  # si tu API requiere int
        "password": GCI_PASSWORD
    }
    response = requests.post(GCI_LOGIN_URL, json=payload, timeout=10)
    response.raise_for_status()
    data = response.json()
    # La API devuelve, por ejemplo, {"token": "...", "usuario": {...}, "idInmobiliaria": 716 }
    bearer_token = data.get("token")
    if not bearer_token:
        raise ValueError("No se encontró 'token' en la respuesta de GCI.")
    return bearer_token

def get_cached_token():
    """
    Devuelve un token válido para la API GCI.
    - Si existe un token en el archivo y se guardó hace menos de 20 horas, lo reutiliza.
    - Si no, solicita un token nuevo y lo guarda con la fecha de adquisición.
    """
    # 1) Ver si existe el archivo de caché
    if os.path.exists(TOKEN_CACHE_FILE):
        try:
            with open(TOKEN_CACHE_FILE, "r") as f:
                cache_data = json.load(f)
                token = cache_data.get("token")
                time_fetched_str = cache_data.get("time_fetched")

            if token and time_fetched_str:
                time_fetched = datetime.fromisoformat(time_fetched_str)  # "YYYY-MM-DDTHH:MM:SS..."
                now = datetime.now()
                elapsed = now - time_fetched
                if elapsed < timedelta(hours=20):
                    # Todavía no han pasado 20 horas, reutilizamos
                    logging.info("Reutilizando token GCI desde caché (menos de 20h).")
                    return token
                else:
                    logging.info("Token GCI en caché superó las 20h. Solicitar token nuevo.")
            else:
                logging.info("Archivo de caché mal formado o sin datos, se solicitará un token nuevo.")
        except Exception as e:
            logging.warning(f"Error leyendo caché de token GCI: {e}. Se solicitará un token nuevo.")
    else:
        logging.info("No existe archivo de caché de token GCI, se solicitará un token nuevo.")

    # 2) Solicitar un token nuevo
    new_token = _obtener_nuevo_token()

    # 3) Guardar en caché
    cache_data = {
        "token": new_token,
        "time_fetched": datetime.now().isoformat()  # Para saber la fecha/hora exacta
    }
    try:
        with open(TOKEN_CACHE_FILE, "w") as f:
            json.dump(cache_data, f)
        logging.info("Nuevo token GCI guardado en caché.")
    except Exception as e:
        logging.warning(f"No se pudo guardar el nuevo token en caché: {e}")

    return new_token

def patch_lead_gci(id_centralizador):
    """
    Envía un PATCH a /api/leads con id_centralizador y fecha_real_contacto = ahora.
    Reutiliza el token de GCI almacenado (si está en vigencia).
    """
    token = get_cached_token()  # 1) Obtiene el token (desde caché si no expiró)
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    fecha_real_contacto = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
    payload = {
        "id_centralizador": id_centralizador,
        "fecha_real_contacto": fecha_real_contacto
    }
    # Haces la petición
    url = "https://api-gci-rest.integracionplanok.io/api/leads" 
    response = requests.patch(url, headers=headers, json=payload)
    response.raise_for_status()
    print("PATCH exitoso", response.text)

def _split_rut_if_needed(identificador_personal, digito_verificador):
    """
    Si el DV no viene (None, '', 'null'), intenta separarlo desde identificador_personal.
    Ejemplos válidos: '12.345.678-9', '12345678-9', '12.345.678-k'
    """
    # DV faltante
    if digito_verificador is None or str(digito_verificador).strip().lower() in ("", "null"):
        s = str(identificador_personal or "").strip()
        # limpiar puntos y espacios
        s = s.replace(".", "").replace(" ", "")
        if "-" in s:
            cuerpo, dv = s.rsplit("-", 1)  # usar el último guion por si acaso
            identificador_personal = cuerpo
            digito_verificador = dv.upper()  # normaliza 'k' -> 'K'
    return identificador_personal, digito_verificador
    
def crear_cliente_natural(identificador_personal, digito_verificador, nombre,
                          apellido_paterno, apellido_materno, celular, email):
    """
    Crea un cliente NATURAL en la API GCI.
    Retorna el 'id' que la API nos devuelve en la respuesta.
    """
    # <<< NUEVO: normalización/separación de RUT >>>
    identificador_personal, digito_verificador = _split_rut_if_needed(
        identificador_personal, digito_verificador
    )

    token = get_cached_token()  # Reutiliza token caché o lo pide si no existe

    url = "https://api-gci-rest.integracionplanok.io/api/clientes/naturales"
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {token}"
    }
    payload = {
        "identificadorPersonal": identificador_personal,  # ej. 16470531 (sin puntos ni guion)
        "digitoVerificador": digito_verificador,          # ej. "5"
        "nombre": nombre,
        "apellidoPaterno": apellido_paterno,
        "apellidoMaterno": apellido_materno,
        "celular": celular,
        "email": email
    }

    try:
        response = requests.post(url, headers=headers, json=payload, timeout=10)
        response.raise_for_status()  # Lanza excepción si status >= 400
        data = response.json()
        # La respuesta tiene "id": 27613, ...
        cliente_id = data.get("id")
        if not cliente_id:
            logging.warning(f"No se encontró 'id' en la respuesta: {data}")
        else:
            logging.info(f"Cliente natural creado con ID={cliente_id}")
        return cliente_id
    except requests.exceptions.RequestException as e:
        logging.error(f"Error al crear cliente natural: {e}")
        raise

def buscar_cliente_natural(identificador_personal):
    token = get_cached_token()
    base_url = "https://api-gci-rest.integracionplanok.io/api/clientes/naturales"
    url = f"{base_url}?identificador-personal={identificador_personal}"
    headers = {"Authorization": f"Bearer {token}"}

    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()  # <--- si es 404, lanza HTTPError
        data = response.json()

        # Verificamos si 'data' es dict o lista
        if isinstance(data, dict):
            cliente_id = data.get("id")
            if cliente_id:
                logging.info(f"Cliente natural encontrado con ID={cliente_id}")
                return cliente_id
            else:
                logging.warning(f"No se encontró 'id' en la respuesta: {data}")
                return None

        elif isinstance(data, list):
            if not data:
                logging.warning("Lista vacía. No se encontró cliente.")
                return None
            primer_obj = data[0]
            cliente_id = primer_obj.get("id")
            if cliente_id:
                logging.info(f"Cliente natural encontrado (lista) con ID={cliente_id}")
                return cliente_id
            else:
                logging.warning(f"No se encontró 'id' en el primer elemento: {primer_obj}")
                return None

        else:
            logging.warning(f"Tipo de respuesta no esperado: {type(data)} -> {data}")
            return None

    except requests.exceptions.HTTPError as e:
        # <-- Si GCI devuelve 404 (Not Found), lo interpretamos como "cliente no existe"
        if e.response.status_code == 404:
            logging.info(f"Cliente con identificador {identificador_personal} no existe en GCI.")
            return None
        else:
            logging.error(f"Error al buscar cliente natural: {e}")
            raise  # Re-lanzamos si no es 404

    except requests.exceptions.RequestException as e:
        logging.error(f"Error al buscar cliente natural (otra excepción): {e}")
        raise

def obtener_id_medio_llegada(proyecto_id, nombre_medio):
    """
    Busca el ID de un medio de llegada en un proyecto específico.
    Retorna el ID si lo encuentra, de lo contrario, devuelve None.
    """
    token = get_cached_token()
    url = f"https://api-gci-rest.integracionplanok.io/api/proyectos/{proyecto_id}/mediosDeLlegada"
    
    headers = {
        'Authorization': f'Bearer {token}'
    }

    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        datos = response.json()

        for medio in datos:
            if medio["nombre"].strip().lower() == nombre_medio.strip().lower():
                return medio["id"]  # Devuelve solo el ID

        return None  # Si no se encontró el medio de llegada
    else:
        logging.error(f"Error al consultar la API: {response.status_code}")
        return None

    
    
def _convertir_fecha_a_iso8601(fecha_str):
    """
    Convierte una fecha en formato dd-mm-yyyy a:
    YYYY-MM-DDT00:00:00.000Z
    - Si 'fecha_str' viene vacío o None, retorna None.
    """
    if not fecha_str:
        return None
    try:
        # Parseamos la fecha en formato dd-mm-YYYY
        fecha_obj = datetime.strptime(fecha_str, "%d-%m-%Y")
        # Formateamos con tiempo "00:00:00.000Z"
        fecha_iso = fecha_obj.strftime("%Y-%m-%dT00:00:00.000Z")
        return fecha_iso
    except ValueError as e:
        logging.warning(f"Error al convertir fecha '{fecha_str}': {e}")
        return None

def buscar_id_medio_desde_db(id_proyecto):
    """
    Busca en la tabla Medio_Proyecto el Id_Medio correspondiente al id_proyecto dado.
    Devuelve el Id_Medio si lo encuentra, o None si no hay coincidencias.
    """
    try:
        connection = pymysql.connect(**DB_CONFIG)
        with connection:
            with connection.cursor() as cursor:
                sql = "SELECT Id_Medio FROM Medio_Proyecto WHERE Id_proyecto = %s LIMIT 1"
                cursor.execute(sql, (id_proyecto,))
                resultado = cursor.fetchone()
                if resultado:
                    return resultado["Id_Medio"]
                else:
                    logging.warning(f"No se encontró Id_Medio para el proyecto {id_proyecto}.")
                    return None
    except pymysql.MySQLError as e:
        logging.error(f"Error al consultar la base de datos: {str(e)}")
        return None

def obtener_rut_vendedor_por_proyecto(proyecto_id):
    """
    Busca en la tabla Vendedores el rut_ejecutivo_sin_digito asociado a un proyecto_id.
    Si hay más de uno, devuelve el primero. Si no hay coincidencias, retorna None.
    """
    try:
        connection = pymysql.connect(**DB_CONFIG)
        with connection:
            with connection.cursor() as cursor:
                sql = """
                    SELECT rut_ejecutivo_sin_digito
                    FROM Vendedores
                    WHERE proyecto_id = %s
                    LIMIT 1
                """
                cursor.execute(sql, (proyecto_id,))
                resultado = cursor.fetchone()
                if resultado and resultado.get("rut_ejecutivo_sin_digito"):
                    return str(resultado["rut_ejecutivo_sin_digito"]).strip()
                else:
                    logging.warning(f"No se encontró rut_ejecutivo_sin_digito para proyecto {proyecto_id}.")
                    return None
    except pymysql.MySQLError as e:
        logging.error(f"Error al consultar tabla Vendedores: {e}")
        return None


def crear_seguimiento(
    id_cliente,
    id_proyecto,
    id_medio,
    id_tipo_contacto,
    id_expectativa,
    comentario,
    fecha_recontacto_str,
    mantener_ejecutivo=True,
    rutvendedor=None
):
    """
    Envía un POST a /api/seguimientos con los campos requeridos por PlanOK.
    Incluye hora_recontacto e id_tipo según esquema oficial.
    Si falla el primer intento, se reintenta con id_medio obtenido desde la DB.
    """

    def _enviar(payload):
        logging.info(f"Enviando seguimiento a GCI con payload:\n{json.dumps(payload, ensure_ascii=False, indent=2)}")
        response = requests.post(url, headers=headers, json=payload, timeout=10)
        response.raise_for_status()
        return response.json()

    # Obtener token desde caché
    token = get_cached_token()
    url = "https://api-gci-rest.integracionplanok.io/api/seguimientos"

    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {token}"
    }

    # --- Normalizar comentario ---
    if not comentario or str(comentario).strip().lower() in ("", "none", "null"):
        comentario = "Gestión ConiLead, cliente no contestó."

    # --- Construcción base del payload ---
    payload = {
        "id_cliente": int(id_cliente or 0),
        "id_proyecto": int(id_proyecto or 0),
        "id_medio": int(id_medio or 0),
        "id_tipo_contacto": int(id_tipo_contacto or 0),
        "id_expectativa": int(id_expectativa or 0),
        "comentario": comentario,
        "fecha_recontacto": fecha_recontacto_str,
        "hora_recontacto": datetime.utcnow().strftime("%H:%M:%S"),  # nuevo campo obligatorio
        "mantenerEjecutivo": True,
        "id_tipo": 0  # campo obligatorio aunque sea 0
    }

    try:
        rut_int = int(str(rutvendedor).strip())
        if rut_int > 1000000:
            payload["rut_ejecutivo"] = rut_int
    except (TypeError, ValueError):
        pass  # 👈 No se agrega la clave si no es válido

    try:
        data = _enviar(payload)

    except requests.exceptions.HTTPError as e:
        if hasattr(e, "response") and e.response is not None:
            logging.warning(f"Primer intento fallido: {e} - Respuesta: {e.response.text}")
        else:
            logging.warning(f"Primer intento fallido: {e}")

        # --- Segundo intento con id_medio desde DB ---
        nuevo_id_medio = buscar_id_medio_desde_db(id_proyecto)
        if nuevo_id_medio:
            logging.info(f"Reintentando con nuevo id_medio obtenido desde la DB: {nuevo_id_medio}")
            payload["id_medio"] = int(nuevo_id_medio)
            payload["mantenerEjecutivo"] = True
            try:
                data = _enviar(payload)
            except Exception as e2:
                if hasattr(e2, "response") and e2.response is not None:
                    logging.error(f"Segundo intento también fallido: {e2} - Respuesta: {e2.response.text}")
                else:
                    logging.error(f"Segundo intento también fallido: {e2}")
                return None
        else:
            logging.warning(f"No se pudo obtener nuevo id_medio desde la DB para el proyecto {id_proyecto}")
            return None

    except Exception as e:
        logging.error(f"Error inesperado al crear seguimiento: {e}", exc_info=True)
        return None

    # --- Procesar respuesta ---
    if isinstance(data, dict):
        seguimiento_id = data.get("id") or data.get("id_seguimiento")
        if seguimiento_id:
            logging.info(f"✅ Seguimiento creado con ID={seguimiento_id}")
            return seguimiento_id
        else:
            logging.warning(f"Seguimiento creado, pero sin 'id': {data}")
            return None

    elif isinstance(data, list):
        if not data:
            logging.warning("Respuesta vacía (lista) al crear seguimiento.")
            return None
        primer_elem = data[0]
        if isinstance(primer_elem, int):
            logging.info(f"✅ Seguimiento creado (lista con int) con ID={primer_elem}")
            return primer_elem
        elif isinstance(primer_elem, dict):
            seguimiento_id = primer_elem.get("id")
            if seguimiento_id:
                logging.info(f"✅ Seguimiento creado (array dict) con ID={seguimiento_id}")
                return seguimiento_id
            else:
                logging.warning(f"Elemento sin 'id': {primer_elem}")
                return None
        else:
            logging.warning(f"Elemento inesperado en lista: {primer_elem}")
            return None

    elif isinstance(data, int):
        logging.info(f"✅ La API devolvió un entero como ID de seguimiento: {data}")
        return data

    else:
        logging.warning(f"Respuesta inesperada: {data} (tipo={type(data)})")
        return None

