"""
audio_fingerprint.py - Sistema de fingerprint de samples
T033-T039: Wild Card, Section Casting, Fingerprint
"""
import hashlib
import json
import logging
from typing import Dict, Any, List, Optional, Set
from pathlib import Path
from collections import defaultdict

logger = logging.getLogger("AudioFingerprint")


class SampleFingerprint:
    """
    T033-T039: Sistema de fingerprint para identificación única de samples.
    Permite tracking, matching y deduplicación.
    """

    def __init__(self, file_path: str):
        self.file_path = Path(file_path)
        self.hash = None
        self.metadata = {}
        self._generate()

    def _generate(self):
        """Genera fingerprint del archivo."""
        if not self.file_path.exists():
            self.hash = None
            return

        # Hash basado en nombre y tamaño (rápido)
        stat = self.file_path.stat()
        content = f"{self.file_path.name}_{stat.st_size}_{stat.st_mtime}"
        self.hash = hashlib.md5(content.encode()).hexdigest()

        # Metadata adicional
        self.metadata = {
            'name': self.file_path.stem,
            'size': stat.st_size,
            'modified': stat.st_mtime,
            'extension': self.file_path.suffix,
        }

    def to_dict(self) -> Dict[str, Any]:
        return {
            'hash': self.hash,
            'path': str(self.file_path),
            'metadata': self.metadata
        }


class FingerprintDatabase:
    """Base de datos de fingerprints para tracking."""

    def __init__(self, db_path: Optional[str] = None):
        self.db_path = Path(db_path) if db_path else Path.home() / ".abletonmcp_ai" / "fingerprints.json"
        self.db_path.parent.mkdir(parents=True, exist_ok=True)
        self._fingerprints: Dict[str, Dict] = {}
        self._load()

    def _load(self):
        """Carga base de datos existente."""
        if self.db_path.exists():
            try:
                with open(self.db_path, 'r', encoding='utf-8') as f:
                    self._fingerprints = json.load(f)
                logger.info(f"Loaded {len(self._fingerprints)} fingerprints")
            except Exception as e:
                logger.warning(f"Could not load fingerprints: {e}")
                self._fingerprints = {}

    def _save(self):
        """Guarda base de datos."""
        with open(self.db_path, 'w', encoding='utf-8') as f:
            json.dump(self._fingerprints, f, indent=2)

    def add(self, sample_path: str) -> Optional[str]:
        """Agrega sample a la base de datos."""
        fp = SampleFingerprint(sample_path)
        if fp.hash:
            self._fingerprints[fp.hash] = fp.to_dict()
            self._save()
            return fp.hash
        return None

    def find_duplicates(self) -> List[List[str]]:
        """Encuentra samples duplicados por hash."""
        hash_to_paths = defaultdict(list)
        for hash_val, data in self._fingerprints.items():
            hash_to_paths[hash_val].append(data['path'])

        # Retornar grupos con más de 1 archivo
        return [paths for paths in hash_to_paths.values() if len(paths) > 1]

    def find_by_name(self, name_pattern: str) -> List[Dict]:
        """Busca por nombre."""
        results = []
        for data in self._fingerprints.values():
            if name_pattern.lower() in data['metadata']['name'].lower():
                results.append(data)
        return results


class WildCardMatcher:
    """
    T033-T034: Wild Card system para matching flexible.
    """

    WILD_PATTERNS = {
        'any_drum': ['*kick*', '*snare*', '*clap*', '*hat*', '*perc*'],
        'any_bass': ['*bass*', '*sub*', '*808*', '*low*'],
        'any_synth': ['*synth*', '*pad*', '*lead*', '*chord*', '*arp*'],
        'any_vocal': ['*vocal*', '*vox*', '*voice*', '*chant*'],
        'any_fx': ['*riser*', '*downlifter*', '*impact*', '*fx*'],
    }

    @classmethod
    def get_wildcard_query(cls, category: str) -> List[str]:
        """Retorna patrones wildcard para una categoría."""
        return cls.WILD_PATTERNS.get(category.lower(), [f'*{category}*'])


class SectionCastingEngine:
    """
    T035-T037: Section Casting - asignación de roles por sección.
    """

    SECTION_ROLES = {
        'intro': {
            'primary': ['atmos', 'pad', 'texture'],
            'secondary': ['kick', 'bass'],
            'avoid': ['lead', 'full_drums']
        },
        'build': {
            'primary': ['snare_roll', 'riser', 'perc'],
            'secondary': ['bass', 'pad'],
            'avoid': ['full_atmos']
        },
        'drop': {
            'primary': ['kick', 'bass', 'lead', 'full_drums'],
            'secondary': ['synth', 'pad'],
            'avoid': ['atmos', 'break_atmos']
        },
        'break': {
            'primary': ['pad', 'atmos', 'vocal', 'pluck'],
            'secondary': ['light_perc'],
            'avoid': ['heavy_kick', 'full_bass']
        },
        'outro': {
            'primary': ['pad', 'atmos', 'texture'],
            'secondary': ['kick'],
            'avoid': ['lead', 'full_drums', 'heavy_bass']
        }
    }

    def get_roles_for_section(self, section_kind: str) -> Dict[str, List[str]]:
        """Retorna roles recomendados para una sección."""
        return self.SECTION_ROLES.get(section_kind.lower(), {
            'primary': [], 'secondary': [], 'avoid': []
        })

    def filter_samples_for_section(self, samples: List[Dict], section_kind: str) -> List[Dict]:
        """Filtra samples apropiados para una sección."""
        roles = self.get_roles_for_section(section_kind)
        primary = set(roles['primary'])

        filtered = []
        for sample in samples:
            sample_type = sample.get('type', '').lower()
            if any(p in sample_type for p in primary):
                sample['section_priority'] = 'primary'
                filtered.append(sample)
            elif not any(a in sample_type for a in roles['avoid']):
                sample['section_priority'] = 'secondary'
                filtered.append(sample)

        return sorted(filtered, key=lambda x: x.get('section_priority', '') != 'primary')


class SampleFamilyTracker:
    """
    T038-T039: Tracking de familias de samples.
    """

    def __init__(self):
        self.families: Dict[str, Set[str]] = defaultdict(set)
        self.usage_count: Dict[str, int] = defaultdict(int)

    def register_family(self, family_name: str, sample_path: str):
        """Registra un sample como parte de una familia."""
        self.families[family_name].add(sample_path)

    def record_usage(self, family_name: str):
        """Registra uso de una familia."""
        self.usage_count[family_name] += 1

    def get_least_used_family(self, families: List[str]) -> str:
        """Retorna la familia menos usada."""
        if not families:
            return ''
        return min(families, key=lambda f: self.usage_count.get(f, 0))

    def get_family_diversity_score(self) -> float:
        """Calcula score de diversidad (0-1)."""
        if not self.usage_count:
            return 1.0
        total = sum(self.usage_count.values())
        unique = len(self.usage_count)
        # Más familias usadas = mejor diversidad
        return min(1.0, unique / max(1, total / 3))


# Instancias globales
_fingerprint_db: Optional[FingerprintDatabase] = None
_family_tracker: Optional[SampleFamilyTracker] = None


def get_fingerprint_db() -> FingerprintDatabase:
    """Obtiene instancia global de fingerprint database."""
    global _fingerprint_db
    if _fingerprint_db is None:
        _fingerprint_db = FingerprintDatabase()
    return _fingerprint_db


def get_family_tracker() -> SampleFamilyTracker:
    """Obtiene instancia global de family tracker."""
    global _family_tracker
    if _family_tracker is None:
        _family_tracker = SampleFamilyTracker()
    return _family_tracker
