#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
TF5 Mixer AI Agent - Controllo del mixer Yamaha TF5 tramite linguaggio naturale
usando Google Gemini con function calling.
"""
import socket
import sys
import os
import json
import time
from pathlib import Path
from typing import List, Optional
from google import genai
from google.genai import types
from dotenv import load_dotenv
from mixer_controller import TF5MixerController

load_dotenv() 

from config import *


class TF5AIAgent:
    """Agente AI per controllare il mixer TF5 con linguaggio naturale."""
    
    def __init__(self, mixer_host=DEFAULT_HOST, mixer_port=DEFAULT_PORT):
        self.controller = TF5MixerController(mixer_host, mixer_port)
        
        # Configura il client Gemini
        api_key = os.getenv("GEMINI_API_KEY")
        if not api_key:
            raise ValueError("GEMINI_API_KEY non trovata nelle variabili d'ambiente")
        
        self.client = genai.Client(api_key=api_key)
        
        # Configura gli strumenti con automatic function calling
        self.config = types.GenerateContentConfig(
            tools=[
                self.controller.recall_scene,
                self.controller.set_channel_level,
                self.controller.set_channel_on_off,
                self.controller.set_channel_pan,
                self.controller.set_mix_level,
                self.controller.set_mix_on_off,
                self.controller.mute_multiple_channels,
                self.controller.unmute_multiple_channels,
                self.controller.get_channel_info,
                self.controller.get_mix_info,
                self.controller.search_channels_by_name,
                self.controller.search_mixes_by_name,
                self.controller.get_all_channels_summary,
                self.controller.get_all_mixes_summary,
                self.controller.refresh_cache,
            ],
            temperature=0,
        )
        
        # Messaggio di sistema per dare contesto all'AI
        self.system_instruction = """Sei un assistente per il controllo del mixer audio Yamaha TF5.
Parli in modo semplice e diretto, come un tecnico del suono esperto che aiuta i musicisti sul palco.

Il mixer ha:
- 40 canali (microfoni, strumenti, ecc.)
- 20 mix/aux (monitor, effetti, ecc.)
- Ogni canale ha volume (da silenzio a +10 dB), acceso/spento, e bilanciamento sinistra/destra
- Scene memorizzate nei banchi A e B (da 0 a 99)

IMPORTANTE - Sistema di Cache:
- Le info sui canali e mix sono salvate per 60 minuti per non sovraccaricare il mixer
- Usa search_channels_by_name e search_mixes_by_name per cercare velocemente
- Usa get_all_channels_summary e get_all_mixes_summary per vedere tutto
- Quando modifichi un canale/mix, la cache viene aggiornata automaticamente
- Quando si carica una scena, i dati vengono invalidati e aggiornati alla prossima richiesta
- Puoi fare refresh_cache solo se l'utente lo chiede esplicitamente

Come interpretare le richieste:

VOLUME/LIVELLO:
- "alza/abbassa/aumenta/diminuisci" → cambia il volume
- "più/meno forte/volume" → cambia il volume
- "al massimo" → +10 dB
- "un po' più alto" → +3 dB circa
- "metti a zero" o "unity" → 0 dB
- "abbassa di poco" → -3 dB
- "metti basso" → -20 dB
- "silenzio/muto" → spegni il canale

ON/OFF:
- "accendi/attiva/apri" → canale/mix ON
- "spegni/muta/chiudi/stacca" → canale/mix OFF
- "muto" può significare sia spegnere che abbassare molto

BILANCIAMENTO (PAN):
- "a sinistra/left" → pan -63
- "a destra/right" → pan +63
- "al centro" → pan 0
- "un po' a sinistra" → pan -30 circa

IDENTIFICAZIONE CANALI E MIX:
- Accetta sia numeri ("canale 5", "mix 3") che nomi ("il microfono del cantante", "monitor palco")
- Se non trovi un canale/mix per nome, cerca usando search_channels_by_name o search_mixes_by_name
- "il mio mic/microfono" → cerca tra i canali chi è sul palco
- "le chitarre/i vox/le tastiere" → cerca per strumento
- "tutti i mic/tutte le chitarre" → cerca e gestisci multipli
- "il monitor" / "l'aux 2" → cerca tra i mix

SCENE:
- "carica/richiama/vai alla scena X" → recall_scene
- Accetta "A5", "scena A 5", "la cinque del banco A", ecc.

GRUPPI DI CANALI:
- "i canali dal 3 al 7" → canali 3,4,5,6,7
- "spegni tutto tranne..." → muta tutti gli altri
- "solo i microfoni" → attiva solo quelli, spegni il resto

CASI PARTICOLARI:
- Se la richiesta è ambigua, chiedi chiarimenti in modo colloquiale
- Se serve cercare un canale/mix, usa prima la cache (search_channels_by_name / search_mixes_by_name)
- Conferma sempre cosa hai fatto con un messaggio breve e chiaro
- Usa emoji occasionalmente per rendere le risposte più amichevoli (✅ ❌ 🎤 🎸 🔊)
- Se qualcosa non funziona, spiega il problema in modo semplice

ESEMPI DI INTERPRETAZIONE:
"alza il mio microfono" → cerca canale per nome, aumenta volume di 3-5 dB
"abbassa un po' le chitarre" → cerca canali chitarra, riduci di 3-5 dB
"muto tutto" → spegni tutti i 40 canali
"solo voce" → cerca canali voce, accendi quelli e spegni gli altri
"alza il monitor 2" → cerca mix 2, aumenta volume
"carica la scena del soundcheck" → cerca nel nome o chiedi numero scena
"troppo forte, abbassa" → riduci di 5-8 dB
"spegni questo canale" → se non specifica numero, chiedi quale

Rispondi sempre in modo:
- Diretto e colloquiale
- Senza troppi tecnicismi
- Confermando chiaramente l'azione eseguita
- Suggerendo alternative se qualcosa non è possibile

Ricorda: chi ti parla è spesso sul palco, con le mani occupate da uno strumento. 
Devi essere veloce, chiaro e capire anche richieste approssimative.
"""
    
    def __enter__(self):
        """Context manager entry."""
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """Context manager exit."""
        self.controller.close()
    
    def close(self):
        """Chiude le connessioni."""
        self.controller.close()
    
    def chat(self, user_message: str) -> str:
        """Invia un messaggio all'agente e riceve la risposta.
        
        Args:
            user_message: Il messaggio dell'utente in linguaggio naturale
        
        Returns:
            La risposta dell'agente
        """
        try:
            full_prompt = f"{self.system_instruction}\n\nUtente: {user_message}"
            
            response = self.client.models.generate_content(
                model="gemini-2.5-pro",
                contents=full_prompt,
                config=self.config,
            )
            
            return response.text
        except Exception as e:
            return f"Errore nell'elaborazione della richiesta: {e}"
    
    def interactive_mode(self):
        """Avvia una sessione interattiva con l'agente."""
        print("=" * 70)
        print("TF5 Mixer AI Agent - Controllo tramite linguaggio naturale")
        print("=" * 70)
        
        # Mostra stato cache
        cache_age = time.time() - self.controller._cache.get("timestamp", 0)
        if self.controller._is_cache_valid():
            channels_count = len(self.controller._cache.get("channels", {}))
            mixes_count = len(self.controller._cache.get("mixes", {}))
            print(f"\n💾 Cache disponibile (età: {int(cache_age)}s)")
            print(f"   📊 {channels_count} canali, {mixes_count} mix")
        else:
            print("\n💾 Cache non disponibile, verrà creata al primo utilizzo")
        
        print("\nEsempi di comandi:")
        print("  - 'Alza il canale 5 a -10 dB'")
        print("  - 'Spegni i canali dal 1 al 5'")
        print("  - 'Richiama la scena A10'")
        print("  - 'Imposta il pan del canale 3 a sinistra'")
        print("  - 'Muta i canali 2, 4, 6 e 8'")
        print("  - 'Alza il mix 3 di 5 dB'")
        print("  - 'Quali canali sono associati ai vox?'")
        print("  - 'Mostrami lo stato del canale 12'")
        print("  - 'Cerca i monitor'")
        print("  - 'Aggiorna i dati dal mixer'")
        print("\nDigita 'esci' o 'quit' per terminare\n")
        
        while True:
            try:
                user_input = input("\n🎛️  Tu: ").strip()
                
                if user_input.lower() in ['esci', 'quit', 'exit', 'q']:
                    print("\n👋 Arrivederci!")
                    break
                
                if not user_input:
                    continue
                
                print("\n🤖 Agent: ", end="", flush=True)
                response = self.chat(user_input)
                print(response)
                
            except KeyboardInterrupt:
                print("\n\n👋 Arrivederci!")
                break
            except Exception as e:
                print(f"\n❌ Errore: {e}")


def main():
    """Funzione principale."""
    import argparse
    
    parser = argparse.ArgumentParser(
        description='TF5 Mixer AI Agent - Controllo tramite linguaggio naturale'
    )
    parser.add_argument('--host', default=DEFAULT_HOST, help=f'IP del mixer (default: {DEFAULT_HOST})')
    parser.add_argument('--port', type=int, default=DEFAULT_PORT, help=f'Porta (default: {DEFAULT_PORT})')
    parser.add_argument('--message', '-m', help='Invia un singolo comando invece di avviare la modalità interattiva')
    parser.add_argument('--refresh-cache', action='store_true', help='Forza l\'aggiornamento della cache all\'avvio')
    
    args = parser.parse_args()
    
    # Verifica che la API key sia impostata
    if not os.getenv("GEMINI_API_KEY"):
        print("❌ Errore: GEMINI_API_KEY non trovata nelle variabili d'ambiente")
        print("\nPer impostare la chiave API:")
        print("  export GEMINI_API_KEY='la-tua-chiave-api'")
        print("\nOttieni una chiave API gratuita su: https://aistudio.google.com/apikey")
        sys.exit(1)
    
    try:
        with TF5AIAgent(args.host, args.port) as agent:
            if args.refresh_cache:
                agent.controller.refresh_cache()
            
            if args.message:
                print(f"🤖 Risposta: {agent.chat(args.message)}")
            else:
                agent.interactive_mode()
    
    except Exception as e:
        print(f"❌ Errore fatale: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()