"""
T217: Almacenamiento Perenne de Logs con Tracking
Sistema de logs persistente en /logs con tracking de eventos
"""

import os
import json
import gzip
import shutil
from datetime import datetime, timedelta
from pathlib import Path
from typing import Dict, List, Any, Optional
from threading import Lock
import logging
import logging.handlers


class PersistentLogManager:
    """Gestor de logs persistentes con rotación y compresión."""
    
    LOG_LEVELS = {
        'DEBUG': 10,
        'INFO': 20,
        'WARNING': 30,
        'ERROR': 40,
        'CRITICAL': 50,
        'MCP': 25,  # Nivel especial para eventos MCP
        'GENERATION': 26,  # Nivel especial para generaciones
        'PERFORMANCE': 27  # Nivel especial para performance
    }
    
    def __init__(self, base_dir: str = None, max_days: int = 30):
        self.base_dir = base_dir or os.path.join(
            os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
            'logs'
        )
        self.max_days = max_days
        self.lock = Lock()
        
        # Crear estructura de directorios
        self._create_log_structure()
        
        # Inicializar loggers
        self._init_loggers()
        
    def _create_log_structure(self):
        """Crea la estructura de directorios de logs."""
        subdirs = ['events', 'errors', 'performance', 'generations', 'archive']
        for subdir in subdirs:
            os.makedirs(os.path.join(self.base_dir, subdir), exist_ok=True)
    
    def _init_loggers(self):
        """Inicializa loggers configurados."""
        self.loggers = {}
        
        # Logger principal
        self.main_logger = self._create_logger(
            'abletonmcp_main',
            os.path.join(self.base_dir, 'events', 'main.log'),
            level=logging.INFO
        )
        
        # Logger de errores
        self.error_logger = self._create_logger(
            'abletonmcp_errors',
            os.path.join(self.base_dir, 'errors', 'errors.log'),
            level=logging.ERROR
        )
        
        # Logger de performance
        self.perf_logger = self._create_logger(
            'abletonmcp_performance',
            os.path.join(self.base_dir, 'performance', 'performance.log'),
            level=logging.INFO
        )
        
        # Logger de generaciones
        self.gen_logger = self._create_logger(
            'abletonmcp_generations',
            os.path.join(self.base_dir, 'generations', 'generations.log'),
            level=logging.INFO
        )
    
    def _create_logger(self, name: str, filepath: str, level: int) -> logging.Logger:
        """Crea un logger configurado."""
        logger = logging.getLogger(name)
        logger.setLevel(level)
        
        # Evitar duplicación de handlers
        if not logger.handlers:
            handler = logging.handlers.RotatingFileHandler(
                filepath,
                maxBytes=10*1024*1024,  # 10MB
                backupCount=5
            )
            formatter = logging.Formatter(
                '%(asctime)s | %(levelname)s | %(name)s | %(message)s',
                datefmt='%Y-%m-%d %H:%M:%S'
            )
            handler.setFormatter(formatter)
            logger.addHandler(handler)
        
        return logger
    
    def log_event(self, category: str, message: str, 
                  level: str = 'INFO', metadata: Dict = None):
        """
        Registra un evento con tracking.
        
        Args:
            category: Categoría del evento (mcp, generation, performance, etc.)
            message: Mensaje del evento
            level: Nivel de severidad
            metadata: Datos adicionales para tracking
        """
        with self.lock:
            timestamp = datetime.now().isoformat()
            
            entry = {
                'timestamp': timestamp,
                'category': category,
                'level': level,
                'message': message,
                'metadata': metadata or {}
            }
            
            # Elegir logger según categoría
            if category == 'error' or level in ['ERROR', 'CRITICAL']:
                self.error_logger.error(f"[{category}] {message}")
                self._save_structured_log(entry, 'errors')
            elif category == 'performance':
                self.perf_logger.info(f"[{category}] {message}")
                self._save_structured_log(entry, 'performance')
            elif category == 'generation':
                self.gen_logger.info(f"[{category}] {message}")
                self._save_structured_log(entry, 'generations')
            else:
                self.main_logger.info(f"[{category}] {message}")
                self._save_structured_log(entry, 'events')
    
    def _save_structured_log(self, entry: Dict[str, Any], subdir: str):
        """Guarda log estructurado en JSON."""
        date_str = datetime.now().strftime('%Y%m%d')
        filepath = os.path.join(self.base_dir, subdir, f'{date_str}.jsonl')
        
        with open(filepath, 'a', encoding='utf-8') as f:
            f.write(json.dumps(entry, ensure_ascii=False) + '\n')
    
    def get_logs(self, category: str = None, 
                 start_date: str = None,
                 end_date: str = None,
                 level: str = None,
                 limit: int = 100) -> List[Dict[str, Any]]:
        """
        Recupera logs con filtros.
        
        Args:
            category: Filtrar por categoría
            start_date: Fecha inicial (YYYY-MM-DD)
            end_date: Fecha final (YYYY-MM-DD)
            level: Filtrar por nivel
            limit: Máximo de registros
            
        Returns:
            Lista de entradas de log
        """
        results = []
        subdir = category or 'events'
        log_dir = os.path.join(self.base_dir, subdir)
        
        if not os.path.exists(log_dir):
            return results
        
        # Determinar rango de fechas
        if start_date:
            start_dt = datetime.strptime(start_date, '%Y-%m-%d')
        else:
            start_dt = datetime.now() - timedelta(days=7)
            
        if end_date:
            end_dt = datetime.strptime(end_date, '%Y-%m-%d')
        else:
            end_dt = datetime.now()
        
        # Buscar archivos en rango
        current_dt = end_dt
        while current_dt >= start_dt and len(results) < limit:
            date_str = current_dt.strftime('%Y%m%d')
            filepath = os.path.join(log_dir, f'{date_str}.jsonl')
            
            if os.path.exists(filepath):
                with open(filepath, 'r', encoding='utf-8') as f:
                    for line in f:
                        try:
                            entry = json.loads(line.strip())
                            if level and entry.get('level') != level:
                                continue
                            results.append(entry)
                            if len(results) >= limit:
                                break
                        except json.JSONDecodeError:
                            continue
            
            current_dt -= timedelta(days=1)
        
        return results
    
    def archive_old_logs(self):
        """Archiva logs antiguos comprimiéndolos."""
        archive_dir = os.path.join(self.base_dir, 'archive')
        cutoff_date = datetime.now() - timedelta(days=self.max_days)
        
        for subdir in ['events', 'errors', 'performance', 'generations']:
            log_dir = os.path.join(self.base_dir, subdir)
            if not os.path.exists(log_dir):
                continue
                
            for filename in os.listdir(log_dir):
                if not filename.endswith('.jsonl'):
                    continue
                    
                # Extraer fecha del nombre
                try:
                    date_str = filename.replace('.jsonl', '')
                    file_date = datetime.strptime(date_str, '%Y%m%d')
                    
                    if file_date < cutoff_date:
                        source = os.path.join(log_dir, filename)
                        archive_name = f"{subdir}_{date_str}.jsonl.gz"
                        dest = os.path.join(archive_dir, archive_name)
                        
                        # Comprimir y mover
                        with open(source, 'rb') as f_in:
                            with gzip.open(dest, 'wb') as f_out:
                                shutil.copyfileobj(f_in, f_out)
                        
                        os.remove(source)
                        self.log_event('maintenance', f'Archived {filename}', 'INFO')
                except ValueError:
                    continue
    
    def get_log_stats(self) -> Dict[str, Any]:
        """Obtiene estadísticas de los logs."""
        stats = {
            'total_events': 0,
            'total_errors': 0,
            'total_generations': 0,
            'by_category': {},
            'by_level': {},
            'oldest_log': None,
            'newest_log': None
        }
        
        for subdir in ['events', 'errors', 'performance', 'generations']:
            log_dir = os.path.join(self.base_dir, subdir)
            if not os.path.exists(log_dir):
                continue
                
            for filename in os.listdir(log_dir):
                if not filename.endswith('.jsonl'):
                    continue
                    
                filepath = os.path.join(log_dir, filename)
                with open(filepath, 'r', encoding='utf-8') as f:
                    for line in f:
                        try:
                            entry = json.loads(line.strip())
                            category = entry.get('category', 'unknown')
                            level = entry.get('level', 'INFO')
                            
                            stats['by_category'][category] = stats['by_category'].get(category, 0) + 1
                            stats['by_level'][level] = stats['by_level'].get(level, 0) + 1
                            
                            if level in ['ERROR', 'CRITICAL']:
                                stats['total_errors'] += 1
                            if category == 'generation':
                                stats['total_generations'] += 1
                                
                            stats['total_events'] += 1
                        except:
                            continue
        
        return stats


# Instancia global
_log_manager = None

def get_log_manager() -> PersistentLogManager:
    """Obtiene la instancia global del gestor de logs."""
    global _log_manager
    if _log_manager is None:
        _log_manager = PersistentLogManager()
    return _log_manager


def log_event(category: str, message: str, level: str = 'INFO', metadata: Dict = None):
    """Función pública para registrar eventos."""
    manager = get_log_manager()
    manager.log_event(category, message, level, metadata)


def get_logs(category: str = None, start_date: str = None, 
             end_date: str = None, level: str = None, limit: int = 100):
    """Función pública para recuperar logs."""
    manager = get_log_manager()
    return manager.get_logs(category, start_date, end_date, level, limit)


if __name__ == '__main__':
    # Test del sistema de logs
    log_event('test', 'Sistema de logs inicializado', 'INFO')
    log_event('generation', 'Track generado: ID=12345', 'INFO', {'genre': 'techno', 'bpm': 128})
    log_event('performance', 'Latencia medida: 15ms', 'INFO')
    log_event('error', 'Error de conexión', 'ERROR', {'error_code': 500})
    
    print("Log stats:", get_log_manager().get_log_stats())
    print("Recent logs:", get_logs(limit=5))
