package client

import (
	"log"
	"time"

	"go-ts/pkg/protocol"
	"go-ts/pkg/transport"

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

type Channel struct {
	ID       uint64
	ParentID uint64
	Name     string
	Order    uint64
}

type Client struct {
	Conn      *transport.TS3Conn
	Handshake *HandshakeState

	Nickname string
	ClientID uint16

	// Counters
	PacketIDCounterC2S uint16
	VoicePacketID      uint16

	// State
	Connected bool

	// Fragment reassembly
	FragmentBuffer     []byte
	FragmentStartPktID uint16
	FragmentCompressed bool
	Fragmenting        bool

	// Server Data
	Channels map[uint64]*Channel

	// Audio
	VoiceDecoders map[uint16]*opus.Decoder // Map VID (sender ID) to decoder
	VoiceEncoder  *opus.Encoder            // Encoder for outgoing audio
}

func NewClient(nickname string) *Client {
	return &Client{
		Nickname:           nickname,
		PacketIDCounterC2S: 1,
		VoicePacketID:      1,
		Channels:           make(map[uint64]*Channel),
		VoiceDecoders:      make(map[uint16]*opus.Decoder),
	}
}

func (c *Client) Connect(address string) error {
	conn, err := transport.NewTS3Conn(address)
	if err != nil {
		return err
	}
	c.Conn = conn
	log.Printf("Connected to UDP. Starting Handshake...")

	// Initialize Handshake State
	hs, err := NewHandshakeState(c.Conn)
	if err != nil {
		return err
	}
	c.Handshake = hs

	// Improve Identity Security Level to 8 (Standard Requirement)
	c.Handshake.ImproveSecurityLevel(8)

	// Send Init1
	if err := c.Handshake.SendPacket0(); err != nil {
		return err
	}

	// Listen Loop
	pktChan := c.Conn.PacketChan()
	ticker := time.NewTicker(3 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case pkt := <-pktChan:
			if err := c.handlePacket(pkt); err != nil {
				log.Printf("Error handling packet: %v", err)
			}
		case <-ticker.C:
			ping := protocol.NewPacket(protocol.PacketTypePing, nil)
			c.PacketIDCounterC2S++
			ping.Header.PacketID = c.PacketIDCounterC2S
			ping.Header.ClientID = c.ClientID
			// Must NOT have NewProtocol (0x20) flag for Pings/Pongs
			ping.Header.Type = uint8(protocol.PacketTypePing) | protocol.PacketFlagUnencrypted

			// Use SharedMac if available, otherwise zeros (as per ts3j InitPacketTransformation)
			if c.Handshake != nil && len(c.Handshake.SharedMac) > 0 {
				copy(ping.Header.MAC[:], c.Handshake.SharedMac)
			} else {
				// Initialize Header.MAC with zeros
				for i := 0; i < 8; i++ {
					ping.Header.MAC[i] = 0
				}
			}

			log.Printf("Sending KeepAlive Ping (PID=%d)", ping.Header.PacketID)
			c.Conn.SendPacket(ping)
		}
	}
}
