package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"go-ts/pkg/ts3client"
	"go-ts/pkg/xai"
)

// UserInfo tracks connected users (no individual sessions)
type UserInfo struct {
	ClientID uint16
	Nickname string
}

// Bot manages the TeamSpeak connection and single global xAI session
type Bot struct {
	ts3      *ts3client.Client
	apiKey   string
	voice    string
	prompt   string
	greeting string

	selfID    uint16
	users     map[uint16]*UserInfo
	usersMu   sync.RWMutex
	startTime time.Time

	// Global xAI Session (one for all users)
	globalXAI *xai.Client
	globalMu  sync.Mutex

	// Input audio mixing (multiple users → one stream)
	inputBuffer   []int16
	inputMu       sync.Mutex
	lastInputTime time.Time

	// Output audio (xAI response → TeamSpeak)
	outputQueue  chan []int16
	outputBuffer []int16
	outputMu     sync.Mutex

	done chan struct{}
}

func main() {
	serverAddr := flag.String("server", "127.0.0.1:9987", "TeamSpeak 3 Server Address")
	nickname := flag.String("nickname", "GrokBot", "Bot nickname")
	voice := flag.String("voice", xai.VoiceAra, "xAI voice (Ara, Rex, Sal, Eve, Leo)")
	greeting := flag.String("greeting", "", "Greeting message when users join (empty to disable)")
	room := flag.String("room", "", "Channel name to join after connecting (empty = stay in default)")
	flag.Parse()

	apiKey := os.Getenv("XAI_API_KEY")
	if apiKey == "" {
		log.Fatal("XAI_API_KEY environment variable not set")
	}

	log.Println("=== xAI Voice Bot for TeamSpeak (Unified Session) ===")
	log.Printf("Server: %s", *serverAddr)
	log.Printf("Nickname: %s", *nickname)
	log.Printf("Voice: %s", *voice)

	bot := &Bot{
		apiKey:      apiKey,
		voice:       *voice,
		prompt:      "Eres Grok, un asistente de voz amigable y útil en un canal de TeamSpeak. Puedes escuchar a múltiples personas hablando. Responde de forma concisa y natural. Si varias personas hablan, trata de entender el contexto de la conversación grupal.",
		greeting:    *greeting,
		users:       make(map[uint16]*UserInfo),
		startTime:   time.Now(),
		inputBuffer: make([]int16, 0, 960*50), // ~1 second buffer
		outputQueue: make(chan []int16, 500),  // ~10 seconds of audio
		done:        make(chan struct{}),
	}

	// Create TeamSpeak client
	bot.ts3 = ts3client.New(*serverAddr, ts3client.Config{
		Nickname: *nickname,
	})

	// Register event handlers
	bot.ts3.On(ts3client.EventConnected, func(e *ts3client.ConnectedEvent) {
		bot.selfID = e.ClientID
		log.Printf("✓ Conectado a TeamSpeak! ClientID=%d, Server=%s", e.ClientID, e.ServerName)

		// Initialize global xAI session after connecting
		go func() {
			if err := bot.initGlobalSession(); err != nil {
				log.Printf("[Global] Error iniciando sesión xAI: %v", err)
			}
		}()
	})

	bot.ts3.On(ts3client.EventChannelList, func(e *ts3client.ChannelListEvent) {
		log.Printf("✓ %d canales disponibles", len(e.Channels))

		// Join specified room if provided
		if *room != "" {
			go func() {
				// Small delay to ensure connection is fully established
				time.Sleep(500 * time.Millisecond)
				ch := bot.ts3.GetChannelByName(*room)
				if ch != nil {
					log.Printf("[Room] Uniéndose al canal: %s (ID=%d)", ch.Name, ch.ID)
					if err := bot.ts3.JoinChannel(ch.ID); err != nil {
						log.Printf("[Room] Error al unirse al canal: %v", err)
					} else {
						log.Printf("[Room] ✓ Unido al canal: %s", ch.Name)
					}
				} else {
					log.Printf("[Room] ⚠ Canal no encontrado: %s", *room)
				}
			}()
		}
	})

	bot.ts3.On(ts3client.EventClientEnter, func(e *ts3client.ClientEnterEvent) {
		// Don't track ourselves
		if e.ClientID == bot.selfID {
			log.Printf("   (Soy yo, ignorando)")
			return
		}

		log.Printf("→ Usuario entró: %s (ID=%d)", e.Nickname, e.ClientID)

		bot.usersMu.Lock()
		bot.users[e.ClientID] = &UserInfo{
			ClientID: e.ClientID,
			Nickname: e.Nickname,
		}
		bot.usersMu.Unlock()

		// Notify xAI about new user (if past startup grace period)
		if bot.greeting != "" && time.Since(bot.startTime) > 3*time.Second {
			bot.globalMu.Lock()
			if bot.globalXAI != nil && bot.globalXAI.IsConnected() {
				msg := fmt.Sprintf("%s. El usuario %s acaba de unirse al canal.", bot.greeting, e.Nickname)
				if err := bot.globalXAI.SendText(msg); err != nil {
					log.Printf("[Global] Error enviando notificación: %v", err)
				}
			}
			bot.globalMu.Unlock()
		}
	})

	bot.ts3.On(ts3client.EventClientLeft, func(e *ts3client.ClientLeftEvent) {
		bot.usersMu.Lock()
		if user, ok := bot.users[e.ClientID]; ok {
			log.Printf("← Usuario salió: %s (ID=%d, %s)", user.Nickname, e.ClientID, e.Reason)
			delete(bot.users, e.ClientID)
		}
		bot.usersMu.Unlock()
	})

	// Audio handler: Mix ALL incoming audio into unified buffer
	bot.ts3.On(ts3client.EventAudio, func(e *ts3client.AudioEvent) {
		bot.handleInputAudio(e.SenderID, e.PCM)
	})

	bot.ts3.On(ts3client.EventError, func(e *ts3client.ErrorEvent) {
		if e.ID != "0" {
			log.Printf("! Error del servidor: [%s] %s", e.ID, e.Message)
		}
	})

	// Handle shutdown
	shutdownDone := make(chan struct{})
	go func() {
		sigChan := make(chan os.Signal, 1)
		signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
		<-sigChan
		log.Println("Cerrando...")

		// Signal all goroutines to stop
		close(bot.done)

		// Close global xAI session
		bot.globalMu.Lock()
		if bot.globalXAI != nil {
			bot.globalXAI.Close()
		}
		bot.globalMu.Unlock()

		// Wait for goroutines
		time.Sleep(200 * time.Millisecond)

		// Disconnect from TeamSpeak
		bot.ts3.Disconnect()

		close(shutdownDone)
	}()

	// Start input sender (sends mixed audio to xAI)
	go bot.runInputSender()

	// Start output mixer (sends xAI audio to TeamSpeak)
	go bot.runOutputSender()

	// Connect to TeamSpeak
	if err := bot.ts3.Connect(); err != nil {
		select {
		case <-shutdownDone:
			log.Println("Conexión cerrada por shutdown")
		default:
			log.Fatalf("Error de conexión: %v", err)
		}
	}

	log.Println("Esperando confirmación final de shutdown...")
	<-shutdownDone
	log.Println("Shutdown completado. Saliendo.")
	os.Exit(0)
}

// initGlobalSession creates the single xAI session for all users
func (b *Bot) initGlobalSession() error {
	log.Println("[Global] Iniciando sesión xAI global...")

	xaiClient := xai.New(b.apiKey)

	// Handle output audio from xAI → buffer for TeamSpeak
	xaiClient.OnAudio(func(pcm []int16) {
		b.outputMu.Lock()
		b.outputBuffer = append(b.outputBuffer, pcm...)

		// Queue complete 960-sample frames
		for len(b.outputBuffer) >= 960 {
			frame := make([]int16, 960)
			copy(frame, b.outputBuffer[:960])
			b.outputBuffer = b.outputBuffer[960:]

			select {
			case b.outputQueue <- frame:
			default:
				// Queue full, drop oldest
			}
		}
		b.outputMu.Unlock()
	})

	// Log transcripts
	xaiClient.OnTranscript(func(text string) {
		log.Printf("[Grok] %s", text)
	})

	// Connect to xAI
	if err := xaiClient.Connect(); err != nil {
		return fmt.Errorf("connect: %w", err)
	}

	// Configure the session
	if err := xaiClient.ConfigureSession(b.voice, b.prompt); err != nil {
		xaiClient.Close()
		return fmt.Errorf("configure: %w", err)
	}

	b.globalMu.Lock()
	b.globalXAI = xaiClient
	b.globalMu.Unlock()

	log.Println("[Global] ✓ Sesión xAI global activa")
	return nil
}

// handleInputAudio mixes incoming audio from any user into the unified buffer
func (b *Bot) handleInputAudio(senderID uint16, pcm []int16) {
	b.inputMu.Lock()
	defer b.inputMu.Unlock()

	// Extend buffer if needed
	neededLen := len(pcm)
	currentLen := len(b.inputBuffer)

	if currentLen < neededLen {
		// Extend with zeros
		b.inputBuffer = append(b.inputBuffer, make([]int16, neededLen-currentLen)...)
	}

	// Mix (add with clipping protection)
	for i, sample := range pcm {
		val := int32(b.inputBuffer[i]) + int32(sample)
		if val > 32767 {
			val = 32767
		}
		if val < -32768 {
			val = -32768
		}
		b.inputBuffer[i] = int16(val)
	}

	b.lastInputTime = time.Now()
}

// runInputSender sends buffered audio to xAI every 20ms
func (b *Bot) runInputSender() {
	ticker := time.NewTicker(20 * time.Millisecond)
	defer ticker.Stop()

	for {
		select {
		case <-b.done:
			return
		case <-ticker.C:
			b.inputMu.Lock()
			if len(b.inputBuffer) >= 960 {
				// Extract one frame
				frame := make([]int16, 960)
				copy(frame, b.inputBuffer[:960])
				// Shift buffer (remove consumed samples)
				b.inputBuffer = b.inputBuffer[960:]
				b.inputMu.Unlock()

				// Send to global xAI session
				b.globalMu.Lock()
				if b.globalXAI != nil && b.globalXAI.IsConnected() {
					b.globalXAI.SendAudio(frame)
				}
				b.globalMu.Unlock()
			} else {
				b.inputMu.Unlock()
			}
		}
	}
}

// runOutputSender sends xAI audio responses to TeamSpeak with proper timing
func (b *Bot) runOutputSender() {
	ticker := time.NewTicker(20 * time.Millisecond)
	defer ticker.Stop()

	for {
		select {
		case <-b.done:
			return
		case <-ticker.C:
			select {
			case frame := <-b.outputQueue:
				if err := b.ts3.SendAudio(frame); err != nil {
					log.Printf("[Output] Error sending audio: %v", err)
				}
			default:
				// No audio to send
			}
		}
	}
}
