#!/usr/bin/env python3
"""
monitor_sincronizacion.py - Monitor de sincronización entre sistemas
Verifica que los estados entre panel, asterisk y AGI estén sincronizados
"""

import sys
sys.path.insert(0, '/etc/centralita-tarot/db_wrapper')
from python_helper import get_asterisk_connection, get_panel_connection
import time
from datetime import datetime
import argparse

class SyncMonitor:
    def __init__(self, verbose=False):
        self.verbose = verbose
        self.conn_panel = None
        self.conn_asterisk = None
        self.connect_databases()
    
    def connect_databases(self):
        """Conecta a ambas bases de datos"""
        try:
            self.conn_panel = MySQLdb.connect(**DB_CONFIG, db='panel_tarot')
            self.conn_asterisk = MySQLdb.connect(**DB_CONFIG, db='asterisk')
            if self.verbose:
                print("✓ Conexiones a BD establecidas")
        except Exception as e:
            print(f"✗ Error conectando a BD: {e}")
            sys.exit(1)
    
    def check_tarotista_sync(self):
        """Verifica sincronización de estados de tarotistas"""
        discrepancias = []
        
        try:
            # Obtener estados del panel
            cursor_panel = self.conn_panel.cursor(MySQLdb.cursors.DictCursor)
            cursor_panel.execute("""
                SELECT extension, nombre, estado_actual, ultima_actualizacion
                FROM tarotistas 
                WHERE activo = 1
                ORDER BY extension
            """)
            tarotistas_panel = {row['extension']: row for row in cursor_panel.fetchall()}
            
            # Obtener estados de asterisk
            cursor_asterisk = self.conn_asterisk.cursor(MySQLdb.cursors.DictCursor)
            cursor_asterisk.execute("""
                SELECT usuario as extension, nombre, 
                       estado_login, estado_ocupacion, dnd_status
                FROM agentes
                ORDER BY usuario
            """)
            
            for agente in cursor_asterisk.fetchall():
                extension = agente['extension']
                
                # Determinar estado esperado en panel
                if agente['estado_login'] == 0:
                    estado_esperado = 'offline'
                elif agente['dnd_status'] == 1:
                    estado_esperado = 'pausa'
                elif agente['estado_ocupacion'] == 1:
                    estado_esperado = 'ocupado'
                else:
                    estado_esperado = 'disponible'
                
                # Comparar con panel
                if extension in tarotistas_panel:
                    estado_panel = tarotistas_panel[extension]['estado_actual']
                    if estado_panel != estado_esperado:
                        discrepancias.append({
                            'extension': extension,
                            'nombre': agente['nombre'],
                            'estado_asterisk': estado_esperado,
                            'estado_panel': estado_panel,
                            'ultima_actualizacion': tarotistas_panel[extension]['ultima_actualizacion']
                        })
            
            return discrepancias
            
        except Exception as e:
            print(f"✗ Error verificando sincronización: {e}")
            return []
    
    def check_active_calls(self):
        """Verifica consistencia de llamadas activas"""
        try:
            cursor_ast = self.conn_asterisk.cursor()
            
            # Llamadas en active_calls
            cursor_ast.execute("SELECT COUNT(*) FROM active_calls")
            active_calls_count = cursor_ast.fetchone()[0]
            
            # Agentes ocupados
            cursor_ast.execute("SELECT COUNT(*) FROM agentes WHERE estado_ocupacion = 1")
            busy_agents_count = cursor_ast.fetchone()[0]
            
            # Llamadas en agent_calls sin end_time
            cursor_ast.execute("SELECT COUNT(*) FROM agent_calls WHERE end_time IS NULL")
            ongoing_calls_count = cursor_ast.fetchone()[0]
            
            return {
                'active_calls': active_calls_count,
                'busy_agents': busy_agents_count,
                'ongoing_calls': ongoing_calls_count,
                'consistent': (active_calls_count == busy_agents_count == ongoing_calls_count)
            }
            
        except Exception as e:
            print(f"✗ Error verificando llamadas: {e}")
            return None
    
    def check_orphaned_calls(self):
        """Busca llamadas huérfanas"""
        orphaned = []
        
        try:
            cursor = self.conn_asterisk.cursor(MySQLdb.cursors.DictCursor)
            
            # Buscar llamadas sin agente ocupado correspondiente
            cursor.execute("""
                SELECT ac.uniqueid, ac.agent_user, ac.start_time
                FROM agent_calls ac
                LEFT JOIN agentes a ON ac.agent_user = a.usuario
                WHERE ac.end_time IS NULL 
                AND (a.estado_ocupacion = 0 OR a.usuario IS NULL)
            """)
            
            orphaned = cursor.fetchall()
            
        except Exception as e:
            print(f"✗ Error buscando llamadas huérfanas: {e}")
        
        return orphaned
    
    def fix_discrepancies(self, discrepancias):
        """Intenta corregir discrepancias encontradas"""
        if not discrepancias:
            return
        
        print(f"\n⚠ Encontradas {len(discrepancias)} discrepancias")
        
        for disc in discrepancias:
            print(f"\nExtensión {disc['extension']} ({disc['nombre']}):")
            print(f"  Estado en Asterisk: {disc['estado_asterisk']}")
            print(f"  Estado en Panel: {disc['estado_panel']}")
            print(f"  Última actualización: {disc['ultima_actualizacion']}")
            
            if input("  ¿Corregir? (s/n): ").lower() == 's':
                try:
                    cursor = self.conn_panel.cursor()
                    cursor.execute("""
                        UPDATE tarotistas 
                        SET estado_actual = %s, ultima_actualizacion = NOW()
                        WHERE extension = %s
                    """, (disc['estado_asterisk'], disc['extension']))
                    self.conn_panel.commit()
                    print(f"  ✓ Corregido")
                except Exception as e:
                    print(f"  ✗ Error: {e}")
                    self.conn_panel.rollback()
    
    def continuous_monitor(self, interval=60):
        """Monitor continuo con intervalo especificado"""
        print(f"Monitor iniciado. Verificando cada {interval} segundos...")
        print("Presiona Ctrl+C para detener\n")
        
        try:
            while True:
                timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                print(f"\n{'='*60}")
                print(f"Verificación: {timestamp}")
                print('='*60)
                
                # Verificar sincronización
                discrepancias = self.check_tarotista_sync()
                if discrepancias:
                    print(f"\n⚠ DISCREPANCIAS ENCONTRADAS: {len(discrepancias)}")
                    for disc in discrepancias[:5]:  # Mostrar máx 5
                        print(f"  - {disc['extension']}: {disc['estado_asterisk']} != {disc['estado_panel']}")
                    if len(discrepancias) > 5:
                        print(f"  ... y {len(discrepancias)-5} más")
                else:
                    print("✓ Estados sincronizados correctamente")
                
                # Verificar llamadas
                call_stats = self.check_active_calls()
                if call_stats:
                    print(f"\nLlamadas activas: {call_stats['active_calls']}")
                    print(f"Agentes ocupados: {call_stats['busy_agents']}")
                    print(f"Llamadas en curso: {call_stats['ongoing_calls']}")
                    
                    if not call_stats['consistent']:
                        print("⚠ INCONSISTENCIA EN CONTADORES DE LLAMADAS")
                
                # Verificar huérfanas
                orphaned = self.check_orphaned_calls()
                if orphaned:
                    print(f"\n⚠ LLAMADAS HUÉRFANAS: {len(orphaned)}")
                    for call in orphaned[:3]:
                        print(f"  - {call['uniqueid']} (agente: {call['agent_user']})")
                
                time.sleep(interval)
                
        except KeyboardInterrupt:
            print("\n\nMonitor detenido")
    
    def generate_report(self):
        """Genera reporte detallado de sincronización"""
        print("\nGENERANDO REPORTE DE SINCRONIZACIÓN...")
        print("="*60)
        
        # Estados
        discrepancias = self.check_tarotista_sync()
        print(f"\n1. SINCRONIZACIÓN DE ESTADOS:")
        print(f"   Total tarotistas verificadas: {len(discrepancias) if discrepancias else 'OK'}")
        print(f"   Discrepancias encontradas: {len(discrepancias)}")
        
        if discrepancias:
            print("\n   Detalle de discrepancias:")
            for disc in discrepancias:
                print(f"   - {disc['extension']} ({disc['nombre']})")
                print(f"     Asterisk: {disc['estado_asterisk']}")
                print(f"     Panel: {disc['estado_panel']}")
        
        # Llamadas
        call_stats = self.check_active_calls()
        print(f"\n2. CONSISTENCIA DE LLAMADAS:")
        if call_stats:
            print(f"   Llamadas activas (active_calls): {call_stats['active_calls']}")
            print(f"   Agentes ocupados: {call_stats['busy_agents']}")
            print(f"   Llamadas en curso (agent_calls): {call_stats['ongoing_calls']}")
            print(f"   Estado: {'✓ CONSISTENTE' if call_stats['consistent'] else '✗ INCONSISTENTE'}")
        
        # Huérfanas
        orphaned = self.check_orphaned_calls()
        print(f"\n3. LLAMADAS HUÉRFANAS:")
        print(f"   Encontradas: {len(orphaned)}")
        if orphaned:
            for call in orphaned:
                print(f"   - UniqueID: {call['uniqueid']}")
                print(f"     Agente: {call['agent_user']}")
                print(f"     Inicio: {call['start_time']}")
        
        print("\n" + "="*60)
        print(f"Reporte generado: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

def main():
    parser = argparse.ArgumentParser(description='Monitor de sincronización Panel-Asterisk')
    parser.add_argument('-c', '--continuous', action='store_true', 
                       help='Monitor continuo')
    parser.add_argument('-i', '--interval', type=int, default=60,
                       help='Intervalo en segundos para monitor continuo (default: 60)')
    parser.add_argument('-f', '--fix', action='store_true',
                       help='Intentar corregir discrepancias')
    parser.add_argument('-r', '--report', action='store_true',
                       help='Generar reporte completo')
    parser.add_argument('-v', '--verbose', action='store_true',
                       help='Salida detallada')
    
    args = parser.parse_args()
    
    monitor = SyncMonitor(verbose=args.verbose)
    
    if args.continuous:
        monitor.continuous_monitor(args.interval)
    elif args.report:
        monitor.generate_report()
    else:
        # Verificación única
        discrepancias = monitor.check_tarotista_sync()
        
        if discrepancias:
            print(f"⚠ Encontradas {len(discrepancias)} discrepancias:")
            for disc in discrepancias:
                print(f"  {disc['extension']}: {disc['estado_asterisk']} != {disc['estado_panel']}")
            
            if args.fix:
                monitor.fix_discrepancies(discrepancias)
        else:
            print("✓ Todos los estados están sincronizados")
        
        # Verificar llamadas
        call_stats = monitor.check_active_calls()
        if call_stats and not call_stats['consistent']:
            print(f"\n⚠ Inconsistencia en llamadas:")
            print(f"  Active calls: {call_stats['active_calls']}")
            print(f"  Busy agents: {call_stats['busy_agents']}")
            print(f"  Ongoing calls: {call_stats['ongoing_calls']}")

if __name__ == "__main__":
    main()