package ts3client

import (
	"fmt"
	"log"
	"strings"

	"go-ts/pkg/protocol"
)

// =============================================================================
// Channel Methods
// =============================================================================

// GetChannels returns all known channels
func (c *Client) GetChannels() []*Channel {
	c.channelsMu.RLock()
	defer c.channelsMu.RUnlock()

	channels := make([]*Channel, 0, len(c.channels))
	for _, ch := range c.channels {
		channels = append(channels, ch)
	}
	return channels
}

// GetChannel returns a channel by ID
func (c *Client) GetChannel(id uint64) *Channel {
	c.channelsMu.RLock()
	defer c.channelsMu.RUnlock()
	return c.channels[id]
}

// GetChannelByName returns the first channel matching the given name (case-insensitive substring match)
func (c *Client) GetChannelByName(name string) *Channel {
	c.channelsMu.RLock()
	defer c.channelsMu.RUnlock()

	// Debug: log all available channels
	log.Printf("[GetChannelByName] Searching for: %q, Available channels (%d):", name, len(c.channels))
	for id, ch := range c.channels {
		log.Printf("[GetChannelByName]   - [%d] %q", id, ch.Name)
	}

	// First try exact match
	for _, ch := range c.channels {
		if ch.Name == name {
			return ch
		}
	}

	// Then try case-insensitive contains
	nameLower := strings.ToLower(name)
	for _, ch := range c.channels {
		if strings.Contains(strings.ToLower(ch.Name), nameLower) {
			return ch
		}
	}
	return nil
}

// GetCurrentChannel returns the client's current channel
func (c *Client) GetCurrentChannel() *Channel {
	if c.selfInfo == nil {
		return nil
	}
	return c.GetChannel(c.selfInfo.ChannelID)
}

// JoinChannel moves the client to the specified channel
func (c *Client) JoinChannel(channelID uint64) error {
	return c.JoinChannelWithPassword(channelID, "")
}

// JoinChannelWithPassword moves the client to a password-protected channel
func (c *Client) JoinChannelWithPassword(channelID uint64, password string) error {
	if c.internal == nil || c.selfInfo == nil {
		return fmt.Errorf("not connected")
	}

	cmd := protocol.NewCommand("clientmove")
	cmd.AddParam("clid", fmt.Sprintf("%d", c.selfInfo.ClientID))
	cmd.AddParam("cid", fmt.Sprintf("%d", channelID))
	if password != "" {
		cmd.AddParam("cpw", password)
	}

	err := c.internal.SendCommand(cmd)
	if err == nil && c.selfInfo != nil {
		c.selfInfo.ChannelID = channelID
	}
	return err
}

// =============================================================================
// Message Methods
// =============================================================================

// SendChannelMessage sends a message to the current channel
func (c *Client) SendChannelMessage(message string) error {
	if c.internal == nil {
		return fmt.Errorf("not connected")
	}

	cmd := protocol.NewCommand("sendtextmessage")
	cmd.AddParam("targetmode", "2") // Channel
	cmd.AddParam("msg", message)

	return c.internal.SendCommand(cmd)
}

// SendPrivateMessage sends a private message to a specific client
func (c *Client) SendPrivateMessage(clientID uint16, message string) error {
	if c.internal == nil {
		return fmt.Errorf("not connected")
	}

	cmd := protocol.NewCommand("sendtextmessage")
	cmd.AddParam("targetmode", "1") // Private
	cmd.AddParam("target", fmt.Sprintf("%d", clientID))
	cmd.AddParam("msg", message)

	return c.internal.SendCommand(cmd)
}

// SendServerMessage sends a message to the entire server
func (c *Client) SendServerMessage(message string) error {
	if c.internal == nil {
		return fmt.Errorf("not connected")
	}

	cmd := protocol.NewCommand("sendtextmessage")
	cmd.AddParam("targetmode", "3") // Server
	cmd.AddParam("msg", message)

	return c.internal.SendCommand(cmd)
}

// =============================================================================
// Audio Methods
// =============================================================================

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

	return c.internal.SendVoice(pcm)
}

// SetInputMuted mutes or unmutes the microphone
func (c *Client) SetInputMuted(muted bool) error {
	if c.internal == nil {
		return fmt.Errorf("not connected")
	}

	val := "0"
	if muted {
		val = "1"
	}

	cmd := protocol.NewCommand("clientupdate")
	cmd.AddParam("client_input_muted", val)

	return c.internal.SendCommand(cmd)
}

// SetOutputMuted mutes or unmutes the speaker
func (c *Client) SetOutputMuted(muted bool) error {
	if c.internal == nil {
		return fmt.Errorf("not connected")
	}

	val := "0"
	if muted {
		val = "1"
	}

	cmd := protocol.NewCommand("clientupdate")
	cmd.AddParam("client_output_muted", val)

	return c.internal.SendCommand(cmd)
}

// =============================================================================
// Client Methods
// =============================================================================

// GetClients returns all connected clients
func (c *Client) GetClients() []*ClientInfo {
	c.clientsMu.RLock()
	defer c.clientsMu.RUnlock()

	clients := make([]*ClientInfo, 0, len(c.clients))
	for _, cl := range c.clients {
		clients = append(clients, cl)
	}
	return clients
}

// GetClientByID returns a client by ID
func (c *Client) GetClientByID(id uint16) *ClientInfo {
	c.clientsMu.RLock()
	defer c.clientsMu.RUnlock()
	return c.clients[id]
}

// KickFromChannel kicks a client from their current channel
func (c *Client) KickFromChannel(clientID uint16, reason string) error {
	if c.internal == nil {
		return fmt.Errorf("not connected")
	}

	cmd := protocol.NewCommand("clientkick")
	cmd.AddParam("clid", fmt.Sprintf("%d", clientID))
	cmd.AddParam("reasonid", "4") // Kick from channel
	cmd.AddParam("reasonmsg", reason)

	return c.internal.SendCommand(cmd)
}

// KickFromServer kicks a client from the server
func (c *Client) KickFromServer(clientID uint16, reason string) error {
	if c.internal == nil {
		return fmt.Errorf("not connected")
	}

	cmd := protocol.NewCommand("clientkick")
	cmd.AddParam("clid", fmt.Sprintf("%d", clientID))
	cmd.AddParam("reasonid", "5") // Kick from server
	cmd.AddParam("reasonmsg", reason)

	return c.internal.SendCommand(cmd)
}

// =============================================================================
// Info Methods
// =============================================================================

// GetServerInfo returns server information
func (c *Client) GetServerInfo() *ServerInfo {
	return c.serverInfo
}

// GetSelfInfo returns our own client information
func (c *Client) GetSelfInfo() *SelfInfo {
	return c.selfInfo
}

// SetNickname changes the client's nickname
func (c *Client) SetNickname(name string) error {
	if c.internal == nil {
		return fmt.Errorf("not connected")
	}

	cmd := protocol.NewCommand("clientupdate")
	cmd.AddParam("client_nickname", name)

	err := c.internal.SendCommand(cmd)
	if err == nil {
		c.config.Nickname = name
		if c.selfInfo != nil {
			c.selfInfo.Nickname = name
		}
	}
	return err
}
