"""
T229: Library Daemon
Daemon de escaneo background de librería de samples
"""

import os
import json
import time
import threading
from datetime import datetime
from typing import Dict, List, Any, Optional
from pathlib import Path


class LibraryDaemon:
    """
    Daemon de escaneo background de librería.
    
    T229: Escaneo continuo de la librería en background.
    """
    
    DEFAULT_SCAN_INTERVAL = 300  # 5 minutos
    
    def __init__(self, library_path: str = None, 
                 scan_interval: int = DEFAULT_SCAN_INTERVAL):
        self.library_path = library_path or self._get_default_library_path()
        self.scan_interval = scan_interval
        self.index_file = os.path.join(
            os.path.dirname(os.path.dirname(__file__)),
            'logs', 'library_index.json'
        )
        self.running = False
        self.daemon_thread: Optional[threading.Thread] = None
        self.index = {
            'last_scan': None,
            'total_files': 0,
            'files': {},
            'categories': {}
        }
        
    def _get_default_library_path(self) -> str:
        """Obtiene ruta por defecto de la librería."""
        return os.path.join(
            os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))),
            'librerias', 'all_tracks'
        )
    
    def start(self) -> Dict[str, Any]:
        """Inicia el daemon de escaneo."""
        if self.running:
            return {'status': 'already_running'}
        
        # Cargar índice existente
        self._load_index()
        
        self.running = True
        self.daemon_thread = threading.Thread(target=self._scan_loop, daemon=True)
        self.daemon_thread.start()
        
        return {
            'status': 'started',
            'library_path': self.library_path,
            'scan_interval': self.scan_interval,
            'initial_file_count': self.index['total_files'],
            'timestamp': datetime.now().isoformat()
        }
    
    def stop(self) -> Dict[str, Any]:
        """Detiene el daemon."""
        if not self.running:
            return {'status': 'not_running'}
        
        self.running = False
        if self.daemon_thread:
            self.daemon_thread.join(timeout=10)
        
        # Guardar índice
        self._save_index()
        
        return {
            'status': 'stopped',
            'timestamp': datetime.now().isoformat(),
            'files_indexed': self.index['total_files']
        }
    
    def _scan_loop(self):
        """Bucle de escaneo."""
        while self.running:
            try:
                self._perform_scan()
                time.sleep(self.scan_interval)
            except Exception as e:
                self._log_error(f"Scan error: {e}")
                time.sleep(self.scan_interval)
    
    def _perform_scan(self):
        """Realiza escaneo de la librería."""
        if not os.path.exists(self.library_path):
            return
        
        new_files = 0
        modified_files = 0
        
        for root, dirs, files in os.walk(self.library_path):
            # Ignorar carpetas ocultas
            dirs[:] = [d for d in dirs if not d.startswith('.')]
            
            for filename in files:
                if not filename.lower().endswith(('.wav', '.aif', '.aiff', '.mp3', '.flac')):
                    continue
                
                filepath = os.path.join(root, filename)
                rel_path = os.path.relpath(filepath, self.library_path)
                
                # Obtener estadísticas del archivo
                try:
                    stat = os.stat(filepath)
                    mtime = stat.st_mtime
                    size = stat.st_size
                    
                    # Verificar si es nuevo o modificado
                    if rel_path not in self.index['files']:
                        self._index_file(rel_path, filepath, mtime, size)
                        new_files += 1
                    elif self.index['files'][rel_path]['mtime'] != mtime:
                        self._update_file(rel_path, mtime, size)
                        modified_files += 1
                        
                except Exception as e:
                    self._log_error(f"Error indexing {filepath}: {e}")
        
        # Actualizar timestamp
        self.index['last_scan'] = datetime.now().isoformat()
        
        if new_files > 0 or modified_files > 0:
            self._log_info(f"Scan complete: {new_files} new, {modified_files} modified")
            self._save_index()
    
    def _index_file(self, rel_path: str, full_path: str, 
                   mtime: float, size: int):
        """Indexa un archivo nuevo."""
        # Determinar categoría
        category = self._categorize_file(rel_path)
        
        self.index['files'][rel_path] = {
            'path': full_path,
            'mtime': mtime,
            'size': size,
            'category': category,
            'indexed_at': datetime.now().isoformat()
        }
        
        # Actualizar categorías
        if category not in self.index['categories']:
            self.index['categories'][category] = []
        self.index['categories'][category].append(rel_path)
        
        self.index['total_files'] = len(self.index['files'])
    
    def _update_file(self, rel_path: str, mtime: float, size: int):
        """Actualiza índice de archivo modificado."""
        self.index['files'][rel_path]['mtime'] = mtime
        self.index['files'][rel_path]['size'] = size
        self.index['files'][rel_path]['updated_at'] = datetime.now().isoformat()
    
    def _categorize_file(self, rel_path: str) -> str:
        """Categoriza archivo por nombre y ruta."""
        path_lower = rel_path.lower()
        filename = os.path.basename(path_lower)
        
        if 'kick' in path_lower or 'bd' in filename:
            return 'kick'
        elif 'snare' in path_lower or 'sd' in filename:
            return 'snare'
        elif 'hat' in path_lower or 'hh' in filename or 'cym' in filename:
            return 'hats'
        elif 'bass' in path_lower:
            return 'bass'
        elif 'synth' in path_lower or 'lead' in path_lower or 'pad' in path_lower:
            return 'synth'
        elif 'vocal' in path_lower or 'vox' in filename:
            return 'vocal'
        elif 'perc' in path_lower:
            return 'percussion'
        elif 'fx' in path_lower or 'effect' in path_lower:
            return 'fx'
        elif 'loop' in path_lower:
            return 'loop'
        else:
            return 'other'
    
    def _load_index(self):
        """Carga índice desde archivo."""
        if os.path.exists(self.index_file):
            try:
                with open(self.index_file, 'r') as f:
                    self.index = json.load(f)
            except Exception as e:
                self._log_error(f"Error loading index: {e}")
    
    def _save_index(self):
        """Guarda índice a archivo."""
        os.makedirs(os.path.dirname(self.index_file), exist_ok=True)
        try:
            with open(self.index_file, 'w') as f:
                json.dump(self.index, f, indent=2)
        except Exception as e:
            self._log_error(f"Error saving index: {e}")
    
    def _log_info(self, message: str):
        """Registra información."""
        try:
            from ..logs.persistent_logs import log_event
            log_event('library_daemon', message, 'INFO')
        except:
            pass
    
    def _log_error(self, message: str):
        """Registra error."""
        try:
            from ..logs.persistent_logs import log_event
            log_event('library_daemon', message, 'ERROR')
        except:
            pass
    
    def get_library_stats(self) -> Dict[str, Any]:
        """Obtiene estadísticas de la librería."""
        return {
            'total_files': self.index['total_files'],
            'last_scan': self.index['last_scan'],
            'categories': {
                cat: len(files) for cat, files in self.index['categories'].items()
            },
            'library_path': self.library_path,
            'daemon_status': 'running' if self.running else 'stopped'
        }
    
    def search_files(self, query: str, category: str = None) -> List[Dict]:
        """Busca archivos en el índice."""
        results = []
        query_lower = query.lower()
        
        for rel_path, info in self.index['files'].items():
            if category and info.get('category') != category:
                continue
            
            if query_lower in rel_path.lower():
                results.append({
                    'path': rel_path,
                    'full_path': info['path'],
                    'category': info['category'],
                    'size': info['size']
                })
        
        return results


# Instancia global
_daemon_instance: Optional[LibraryDaemon] = None

def scan_sample_library(analyze_audio: bool = False) -> Dict[str, Any]:
    """
    T229: Escanea librería de samples.
    
    Args:
        analyze_audio: Analizar contenido de audio (más lento pero más preciso)
        
    Returns:
        Estadísticas del escaneo
    """
    global _daemon_instance
    
    if _daemon_instance is None:
        _daemon_instance = LibraryDaemon()
    
    # Si no está corriendo, iniciar
    if not _daemon_instance.running:
        _daemon_instance.start()
    
    return _daemon_instance.get_library_stats()


def get_sample_library_stats() -> Dict[str, Any]:
    """Obtiene estadísticas detalladas de la librería."""
    global _daemon_instance
    
    if _daemon_instance is None:
        _daemon_instance = LibraryDaemon()
        _daemon_instance._load_index()
    
    return _daemon_instance.get_library_stats()


if __name__ == '__main__':
    # Test del daemon
    result = scan_sample_library()
    print(json.dumps(result, indent=2))
