package client

import (
	"encoding/binary"
	"fmt"
	"log"

	"go-ts/pkg/protocol"

	"gopkg.in/hraban/opus.v2"
)

const (
	// TeamSpeak Codecs
	CodecOpusVoice = 4
	CodecOpusMusic = 5
)

func (c *Client) handleVoice(pkt *protocol.Packet) {
	// Only process Opus packets
	// Parse Voice Header (Server -> Client)
	// VId(2) + CId(2) + Codec(1) + Data

	var data []byte = pkt.Data

	// Decrypt if Encrypted
	if !pkt.Header.FlagUnencrypted() {
		if c.Handshake == nil || len(c.Handshake.SharedIV) == 0 {
			log.Println("Received encrypted voice packet but no SharedIV available. Dropping.")
			return
		}

		crypto := &protocol.CryptoState{
			SharedIV:     c.Handshake.SharedIV,
			SharedMac:    c.Handshake.SharedMac,
			GenerationID: 0,
		}
		// Server->Client = false
		key, nonce := crypto.GenerateKeyNonce(&pkt.Header, false)

		// Meta for Server->Client: PID(2) + PT(1) = 3 bytes
		meta := make([]byte, 3)
		binary.BigEndian.PutUint16(meta[0:2], pkt.Header.PacketID)
		meta[2] = pkt.Header.Type

		var err error
		data, err = protocol.DecryptEAX(key, nonce, meta, pkt.Data, pkt.Header.MAC[:])
		if err != nil {
			// Voice decryption failure is common if keys mismatch or packet loss affects counter?
			// But EAX doesn't depend on counter state, only PID which is in header.
			log.Printf("Voice decryption failed: %v", err)
			return
		}
	}

	if len(data) < 6 {
		// Ignore empty/too small packets (e.g. silence or end)
		return
	}

	// The first 2 bytes are the sequence ID, not the client ID for the decoder key.
	// The client ID is at data[2:4]
	// vid := binary.BigEndian.Uint16(data[0:2]) // Sequence ID
	vid := binary.BigEndian.Uint16(data[2:4]) // Talking Client ID (CORRECT KEY)
	codec := data[4]
	voiceData := data[5:]

	// Only process Opus packets
	if codec != CodecOpusVoice && codec != CodecOpusMusic {
		log.Printf("Received non-Opus voice packet (Codec=%d). Ignoring.", codec)
		return
	}

	channels := 1
	if codec == CodecOpusMusic {
		channels = 2
	}

	// 1. Get or Create Decoder for this VID
	decoder, ok := c.VoiceDecoders[vid]
	if !ok {
		var err error
		decoder, err = opus.NewDecoder(48000, channels)
		if err != nil {
			log.Printf("Failed to create Opus decoder: %v", err)
			return
		}
		c.VoiceDecoders[vid] = decoder
	}

	// 2. Decode Opus to PCM
	// Max frame size for 120ms at 48kHz is 5760 samples
	pcm := make([]int16, 5760*channels)
	n, err := decoder.Decode(voiceData, pcm)
	if err != nil {
		log.Printf("Opus decode error: %v", err)
		return
	}
	pcm = pcm[:n*channels]

	// 3. Emit audio event instead of auto-echo
	c.emitEvent("audio", map[string]any{
		"senderID": vid,
		"codec":    int(codec),
		"pcm":      pcm,
		"channels": channels,
	})
}

// SendVoice sends PCM audio data to the server
// PCM must be 48kHz, 960 samples for 20ms frame (mono)
func (c *Client) SendVoice(pcm []int16) error {
	if c.Conn == nil {
		return fmt.Errorf("not connected")
	}

	channels := 1
	codec := uint8(CodecOpusVoice)

	// Protect shared encoder
	c.VoiceEncoderMu.Lock()
	defer c.VoiceEncoderMu.Unlock()

	// Get or Create Encoder
	if c.VoiceEncoder == nil {
		var err error
		app := opus.AppVoIP
		encoder, err := opus.NewEncoder(48000, channels, app)
		if err != nil {
			return fmt.Errorf("failed to create Opus encoder: %w", err)
		}

		// Optimize Quality
		encoder.SetBitrate(64000) // 64 kbps (High Quality for Voice)
		encoder.SetComplexity(10) // Max Complexity (Best Quality)

		c.VoiceEncoder = encoder
	}

	// Encode PCM to Opus
	encoded := make([]byte, 1024)
	nEnc, err := c.VoiceEncoder.Encode(pcm, encoded)
	if err != nil {
		return fmt.Errorf("opus encode error: %w", err)
	}
	encoded = encoded[:nEnc]

	// Build voice packet (Client -> Server)
	// Payload format: [VId(2)] [Codec(1)] [Data...]
	voiceData := make([]byte, 2+1+len(encoded))

	c.VoicePacketID++ // Increment counter before using it

	// Set VId in Payload to be the Sequence Number (not ClientID)
	binary.BigEndian.PutUint16(voiceData[0:2], c.VoicePacketID)
	voiceData[2] = codec
	copy(voiceData[3:], encoded)

	pkt := protocol.NewPacket(protocol.PacketTypeVoice, voiceData)
	pkt.Header.PacketID = c.VoicePacketID
	pkt.Header.ClientID = c.ClientID

	// Encrypt voice packet
	if c.Handshake != nil && len(c.Handshake.SharedIV) > 0 {
		crypto := &protocol.CryptoState{
			SharedIV:     c.Handshake.SharedIV,
			SharedMac:    c.Handshake.SharedMac,
			GenerationID: 0,
		}
		key, nonce := crypto.GenerateKeyNonce(&pkt.Header, true)

		meta := make([]byte, 5)
		binary.BigEndian.PutUint16(meta[0:2], pkt.Header.PacketID)
		binary.BigEndian.PutUint16(meta[2:4], pkt.Header.ClientID)
		meta[4] = pkt.Header.Type

		encData, mac, err := protocol.EncryptEAX(key, nonce, meta, pkt.Data)
		if err != nil {
			return fmt.Errorf("voice encryption failed: %w", err)
		}
		pkt.Data = encData
		copy(pkt.Header.MAC[:], mac)
	} else {
		if c.Handshake != nil && len(c.Handshake.SharedMac) > 0 {
			copy(pkt.Header.MAC[:], c.Handshake.SharedMac)
		} else {
			pkt.Header.MAC = protocol.HandshakeMac
		}
	}

	return c.Conn.SendPacket(pkt)
}
