import logging
from datetime import datetime, date
from typing import List, Dict, Any, Optional
import sys
import os
import socket
import paramiko
from fastapi import UploadFile
from dotenv import load_dotenv


from cnxpdo import get_connection

# Cargar variables de entorno
script_dir = os.path.dirname(os.path.abspath(__file__))
crm_dir = os.path.dirname(script_dir)
env_path = os.path.join(crm_dir, "configuraciones", ".env")
load_dotenv(env_path)

    # server = os.getenv('ftp_server')
    # username = os.getenv('ftp_user_name')
    # #pem_key_path = os.getenv('ftp_private_key_path')
    # passVal = os.getenv('ftp_user_pass')
    # port = int(os.getenv('ftp_port'))
    # ftp_path_imgs = os.getenv('ftp_path_imgs')

def obtener_noticiascrm(fecha: Optional[str]) -> Dict[str, Any]:
    conexion = None
    cursor = None

    try:
        conexion = get_connection()
        if not conexion:
            raise Exception("No se pudo establecer conexión a la base de datos")

        # Validación de la fecha
        if fecha:
            try:
                datetime.strptime(fecha, "%Y-%m-%d")
            except ValueError:
                raise ValueError("Formato de fecha inválido. Use YYYY-MM-DD")

        # Construcción del query - CAMBIO: fecha -> fecha_noticia
        query = """
            SELECT n.*, en.estado  
            FROM noticias n
            LEFT JOIN estado_noticias en ON n.id_estado = en.id
        """
        params = []

        if fecha:
            # CAMBIO: fecha -> fecha_noticia
            query += " WHERE DATE(fecha_noticia) = STR_TO_DATE(%s, '%%Y-%%m-%%d')"
            params.append(fecha)

        # Ordenar por fecha descendente - CAMBIO: fecha -> fecha_noticia
        query += " ORDER BY fecha_publicacion DESC"

        # Ejecutar consulta
        cursor = conexion.cursor(dictionary=True)
        cursor.execute(query, params)
        datos = cursor.fetchall()

        # Formatear fecha como string - CAMBIO: fecha -> fecha_noticia
        for noticia in datos:
            if isinstance(noticia["fecha_noticia"], (datetime, date)):
                noticia["fecha_noticia"] = noticia["fecha_noticia"].strftime("%Y-%m-%d")

        return {
            'success': 1,
            'message': 'Noticias encontradas',
            "data": datos
        }

    except Exception as e:
        logging.error(f"[ERROR] en obtener_noticias: {str(e)}")
        raise
    finally:
        if cursor:
            cursor.close()
        if conexion:
            conexion.close()

def insertar_noticia(datos: Dict[str, Any]) -> Dict[str, Any]:
    import os
    import socket
    import logging
    from datetime import datetime
    import paramiko

    conexion = None
    cursor = None

    try:
        # ---- DB ----
        conexion = get_connection()
        if not conexion:
            raise Exception("No se pudo establecer conexión a la base de datos")

        # ---- Validaciones de entrada ----
        campos_requeridos = ['principal', 'fecha_noticia', 'titulo', 'link', 'texto_corto', 'texto_largo', 'id_estado', 'adjunto']
        for campo in campos_requeridos:
            if campo not in datos or datos[campo] is None or datos[campo] == '':
                raise ValueError(f"El campo '{campo}' es requerido")

        try:
            datetime.strptime(datos['fecha_noticia'], "%Y-%m-%d")
        except ValueError:
            raise ValueError("Formato de fecha inválido. Use YYYY-MM-DD")

        archivo = datos['adjunto']
        try:
            archivo.file.seek(0)
            file_content = archivo.file.read()
            if not file_content:
                raise ValueError("El archivo está vacío o no se pudo leer.")
            print(f"📁 [DEBUG] Archivo leído correctamente. Tamaño: {len(file_content)} bytes")
        except Exception as e:
            raise ValueError(f"Error al leer el archivo: {e}")

        # ---- Config SFTP (DESTINO) ----
        server = os.getenv('ftp_server')            # p.ej. 3.149.38.169 (DESTINO)
        username = os.getenv('ftp_user_name')       # p.ej. 'ubuntu' o 'ec2-user'
        ftp_path_imgs = os.getenv('ftp_path_imgs')  # p.ej. '/var/www/.../noticias'
        port = 22

        # Ruta de la private key (por defecto, tu .pem)
        key_path = os.getenv('ftp_key_path', '/home/ubuntu/admin-access.pem')
        key_pass = os.getenv('ftp_key_pass') or None  # passphrase si aplica

        print("🔍 [DEBUG] Variables de entorno SFTP:")
        print(f"   - Server: {'✅ OK' if server else '❌ VACÍO'} ({server})")
        print(f"   - Username: {'✅ OK' if username else '❌ VACÍO'} ({username})")
        print(f"   - Remote Folder: {'✅ OK' if ftp_path_imgs else '❌ VACÍO'} ({ftp_path_imgs})")
        print(f"   - Key Path: {'✅ OK' if key_path else '❌ VACÍO'} ({key_path})")

        if not server:
            raise ValueError("Variable de entorno 'ftp_server' no está configurada")
        if not username:
            raise ValueError("Variable de entorno 'ftp_user_name' no está configurada")
        if not ftp_path_imgs:
            raise ValueError("Variable de entorno 'ftp_path_imgs' no está configurada")
        if not os.path.isfile(key_path):
            raise ValueError(f"No se encontró la llave privada en '{key_path}'")

        # ---- Armar nombres de archivo/ruta ----
        file_name = os.path.basename(getattr(archivo, "filename", "archivo.bin"))
        remote_dir = ftp_path_imgs.rstrip("/")
        remote_file_path = f"{remote_dir}/{file_name}"
        print(f"📄 [DEBUG] Nombre del archivo: {file_name}")
        print(f"🎯 [DEBUG] Ruta remota completa: {remote_file_path}")

        # ---- Pre-chequeo de red para 22/tcp (inline) ----
        print(f"🌐 [CHECK] Verificando conectividad TCP a {server}:{port} ...")
        try:
            with socket.create_connection((server, port), timeout=5):
                pass
        except Exception as e:
            raise Exception(f"No hay conectividad TCP al puerto {port} de {server}: {e}")
        print("🌐 [CHECK] OK")

        # ---- Conexión SSH + SFTP vía private key (Opción 2) ----
        client = None
        sftp = None
        try:
            print(f"🔐 [SFTP] Conectando a {server}:{port} como {username} con KEY")
            client = paramiko.SSHClient()
            client.set_missing_host_key_policy(paramiko.AutoAddPolicy())  # en prod, usa known_hosts

            client.connect(
                server,
                port=port,
                username=username,
                key_filename=key_path,         # << usa la private key
                passphrase=key_pass,           # si tu key tiene passphrase
                look_for_keys=False,
                allow_agent=False,
                timeout=15,
                banner_timeout=15,
                auth_timeout=15,
            )

            transport = client.get_transport()
            if transport:
                transport.set_keepalive(30)

            sftp = client.open_sftp()
            print("✅ [SFTP] Sesión abierta")

            # ---- Crear recursivamente el directorio remoto (inline) ----
            # remote_dir = "/a/b/c"; crearlo si no existe
            parts = remote_dir.split("/")
            cur = ""
            for p in parts:
                if not p:
                    continue
                cur = f"{cur}/{p}"
                try:
                    sftp.stat(cur)
                except IOError:
                    sftp.mkdir(cur)

            # ---- Subir archivo desde bytes ----
            print(f"📤 [SFTP] Subiendo {file_name} ...")
            with sftp.open(remote_file_path, "wb") as f:
                f.write(file_content)
            print(f"✅ [SFTP] Archivo subido a: {remote_file_path}")

            imagen_path = file_name

        except paramiko.AuthenticationException as auth_error:
            raise Exception(f"Error de autenticación SFTP (publickey): {auth_error}")
        except paramiko.SSHException as ssh_error:
            raise Exception(f"Error SSH: {ssh_error}")
        except socket.error as socket_error:
            raise Exception(f"Error de red al conectar SFTP: {socket_error}")
        except Exception as e:
            raise Exception(f"Error inesperado al subir archivo: {e} - Tipo: {type(e).__name__}")
        finally:
            if sftp:
                try:
                    sftp.close()
                except Exception:
                    pass
            if client:
                try:
                    client.close()
                except Exception:
                    pass

        # ---- Insert DB ----
        print("📊 [DB] Insertando noticia en base de datos...")
        query = """
            INSERT INTO noticias (principal, fecha_noticia, titulo, link, texto_corto, texto_largo, id_estado, imagen, fecha_publicacion)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, NOW())
        """
        params = [
            datos['principal'],
            datos['fecha_noticia'],
            datos['titulo'],
            datos['link'],
            datos['texto_corto'],
            datos['texto_largo'],
            datos['id_estado'],
            imagen_path
        ]

        cursor = conexion.cursor()
        cursor.execute(query, params)

        # Obtener ID insertado (MySQL/MariaDB: lastrowid; si es PostgreSQL, usa RETURNING)
        noticia_id = getattr(cursor, "lastrowid", None)
        if not noticia_id:
            try:
                cursor.execute("SELECT LAST_INSERT_ID()")
                row = cursor.fetchone()
                if row:
                    noticia_id = row[0]
            except Exception:
                pass

        print(f"✅ [DB] Noticia insertada con ID: {noticia_id}")

        # Lógica adicional propia del negocio (si existe en tu proyecto)
        gestionar_noticia_principal(conexion, noticia_id, datos['principal'])

        conexion.commit()
        print("✅ [DB] Transacción confirmada")

        return {
            'success': 1,
            'message': 'Noticia insertada correctamente',
            'data': {'id': noticia_id, 'insertado': True}
        }

    except Exception as e:
        if conexion:
            try:
                print("🔄 [DB] Ejecutando rollback...")
                conexion.rollback()
            except Exception:
                pass

        error_completo = str(e)
        print(f"💥❌ [FUNCIÓN ERROR] Error completo: {error_completo}")
        logging.error(f"[ERROR] en insertar_noticia: {error_completo}")

        return {
            'success': 0,
            'message': f'Error al insertar noticia: {error_completo}',
            'data': {'error_type': type(e).__name__, 'error_details': error_completo}
        }

    finally:
        if cursor:
            try:
                cursor.close()
            except Exception:
                pass
        if conexion:
            try:
                conexion.close()
            except Exception:
                pass
        print("🔚 [CLEANUP] Recursos liberados")

def actualizar_noticia(id: int, datos: Dict[str, Any]) -> Dict[str, Any]:
    conexion = None
    cursor = None

    try:
        conexion = get_connection()
        if not conexion:
            raise Exception("No se pudo establecer conexión a la base de datos")

        # Validación de campos requeridos (sin incluir adjunto porque es opcional)
        campos_requeridos = ['principal', 'fecha_noticia', 'titulo', 'link', 'texto_corto', 'texto_largo', 'id_estado']
        for campo in campos_requeridos:
            if campo not in datos or datos[campo] is None or datos[campo] == '':
                raise ValueError(f"El campo '{campo}' es requerido")

        # Validación de la fecha
        try:
            datetime.strptime(datos['fecha_noticia'], "%Y-%m-%d")
        except ValueError:
            raise ValueError("Formato de fecha inválido. Use YYYY-MM-DD")
        
        # Obtener la imagen actual antes de procesar
        cursor_temp = conexion.cursor()
        cursor_temp.execute("SELECT imagen FROM noticias WHERE id = %s", (id,))
        resultado = cursor_temp.fetchone()
        cursor_temp.close()
        
        imagen_anterior = resultado[0] if resultado and resultado[0] else None
        
        archivo = datos.get('adjunto')  # UploadFile o None
        imagen_path = imagen_anterior  # Por defecto, mantener la imagen actual

        # Si se proporciona un archivo, procesarlo
        if archivo is not None and archivo.filename:
            try:
                archivo.file.seek(0)
                file_content = archivo.file.read()
                if not file_content:
                    raise ValueError("El archivo está vacío.")
                archivo.file.seek(0)
                
            except Exception as e:
                raise ValueError(f"Error al leer el archivo: {e}")

            # Configuración SFTP
            server = os.getenv('ftp_server')
            username = os.getenv('ftp_user_name')
            passVal = os.getenv('ftp_user_pass')
            port = 22
            ftp_path_imgs = os.getenv('ftp_path_imgs')
            
            file_name = archivo.filename
            remote_file_path = f"{ftp_path_imgs}/{file_name}"

            transport = None
            sftp = None

            try:
                # Conectar al servidor SFTP
                transport = paramiko.Transport((server, port))
                transport.connect(username=username, password=passVal)
                sftp = paramiko.SFTPClient.from_transport(transport)

                # Eliminar imagen anterior si existe y es diferente a la nueva
                if imagen_anterior and imagen_anterior != file_name:
                    try:
                        remote_old_path = f"{ftp_path_imgs}/{imagen_anterior}"
                        sftp.remove(remote_old_path)
                        logging.info(f"Imagen anterior eliminada: {imagen_anterior}")
                    except FileNotFoundError:
                        logging.warning(f"La imagen anterior no se encontró en el servidor: {imagen_anterior}")
                    except Exception as e:
                        logging.warning(f"Error al eliminar imagen anterior: {e}")

                # Subir nueva imagen
                with sftp.file(remote_file_path, 'wb') as remote_file:
                    remote_file.write(file_content)
                    remote_file.flush()
                
                imagen_path = file_name
                logging.info(f"Nueva imagen subida: {file_name}")

            except Exception as e:
                raise Exception(f"Error al subir el archivo al servidor: {e}")
            finally:
                if sftp:
                    sftp.close()
                if transport:
                    transport.close()

        # Query de actualización
        query = """
            UPDATE noticias
            SET principal = %s, fecha_noticia = %s, titulo = %s, link = %s,
                texto_corto = %s, texto_largo = %s, id_estado = %s, imagen = %s
            WHERE id = %s
        """

        # Parámetros para la actualización
        params = [
            datos['principal'],
            datos['fecha_noticia'],
            datos['titulo'],
            datos['link'],
            datos['texto_corto'],
            datos['texto_largo'],
            datos['id_estado'],
            imagen_path,
            id
        ]

        # Ejecutar actualización
        cursor = conexion.cursor()
        cursor.execute(query, params)

        # Verificar si se actualizó algún registro
        if cursor.rowcount == 0:
            raise ValueError("No se encontró la noticia para actualizar")

        # **NUEVA LÓGICA: Gestionar noticia principal**
        gestionar_noticia_principal(conexion, id, datos['principal'])
        
        # Confirmar todas las transacciones
        conexion.commit()

        return {
            'success': 1,
            'message': 'Noticia actualizada correctamente',
            'data': {
                'id': id,
                'insertado': True,
                'imagen': imagen_path if imagen_path else None,
                'url': f"{os.getenv('ftp_path_imgs')}/{imagen_path}" if imagen_path else None
            }
        }

    except Exception as e:
        if conexion:
            conexion.rollback()
        logging.error(f"[ERROR] en actualizar_noticia: {str(e)}")
        return {
            'success': 0,
            'message': f'Error al actualizar noticia: {str(e)}',
            'data': None
        }
    finally:
        if cursor:
            cursor.close()
        if conexion:
            conexion.close()
            
            
            
            
            
def eliminarNoticia(id_noticia):
    conexionBD = None
    cursor = None
    
    try:
        conexionBD = get_connection()
        if conexionBD is None:
            return {"success": False, "message": "Error de conexión"}
        
        cursor = conexionBD.cursor()
        
        # Primero obtener la información de la imagen antes de eliminar el registro
        query_select = "SELECT imagen FROM noticias WHERE id = %s"
        cursor.execute(query_select, (id_noticia,))
        resultado = cursor.fetchone()
        
        if not resultado:
            return {"success": False, "message": "Noticia no encontrada"}
        
        imagen_nombre = resultado[0] if resultado[0] else None
        
        # Eliminar imagen del servidor SFTP si existe
        if imagen_nombre:
            try:
                # Configuración SFTP
                server = os.getenv('ftp_server')
                username = os.getenv('ftp_user_name')
                passVal = os.getenv('ftp_user_pass')
                port = 22
                ftp_path_imgs = os.getenv('ftp_path_imgs')
                
                remote_file_path = f"{ftp_path_imgs}/{imagen_nombre}"
                
                transport = None
                sftp = None
                
                try:
                    # Conectar al servidor SFTP
                    transport = paramiko.Transport((server, port))
                    transport.connect(username=username, password=passVal)
                    sftp = paramiko.SFTPClient.from_transport(transport)
                    
                    # Eliminar archivo del servidor
                    sftp.remove(remote_file_path)
                    logging.info(f"Imagen eliminada del servidor: {imagen_nombre}")
                    
                except FileNotFoundError:
                    logging.warning(f"La imagen no se encontró en el servidor: {imagen_nombre}")
                    # Continuamos con la eliminación del registro aunque la imagen no exista
                except Exception as e:
                    logging.warning(f"Error al eliminar imagen del servidor: {e}")
                    # Continuamos con la eliminación del registro aunque falle la eliminación de la imagen
                finally:
                    if sftp:
                        sftp.close()
                    if transport:
                        transport.close()
                        
            except Exception as e:
                logging.error(f"Error en conexión SFTP: {e}")
                # Continuamos con la eliminación del registro
        
        # Eliminar registro de la base de datos
        query_delete = "DELETE FROM noticias WHERE id = %s"
        cursor.execute(query_delete, (id_noticia,))
        conexionBD.commit()
        
        if cursor.rowcount > 0:
            return {"success": True, "message": "Noticia e imagen eliminadas correctamente"}
        else:
            return {"success": False, "message": "Error al eliminar la noticia"}
        
    except Exception as e:
        if conexionBD:
            conexionBD.rollback()
        logging.error(f"[ERROR] en eliminarNoticia: {str(e)}")
        return {"success": False, "message": f"Error: {str(e)}"}
    
    finally:
        if cursor:
            cursor.close()
        if conexionBD:
            conexionBD.close()


def gestionar_noticia_principal(conexion, nueva_noticia_id: int, es_principal: int) -> None:

    cursor = None
    try:
        cursor = conexion.cursor()
        
        if es_principal == 1:
            # Desactivar todas las noticias principales excepto la actual
            query_desactivar = """
                UPDATE noticias 
                SET principal = 0 
                WHERE principal = 1 AND id != %s
            """
            cursor.execute(query_desactivar, (nueva_noticia_id,))
            
            filas_afectadas = cursor.rowcount
            if filas_afectadas > 0:
                logging.info(f"Se desactivaron {filas_afectadas} noticias principales anteriores")
        
    except Exception as e:
        logging.error(f"Error en gestionar_noticia_principal: {str(e)}")
        raise
    finally:
        if cursor:
            cursor.close()

