package channel

import (
	"fmt"
	"slices"
	"sync"
	"time"
)

// Channel type constants
const (
	TypeNone   = 0
	TypeWorld  = 1 // Persistent, loaded from database
	TypeCustom = 2 // Temporary, deleted when empty
)

// Channel actions for client communication
const (
	ActionJoin       = 0 // Player joins channel
	ActionLeave      = 1 // Player leaves channel
	ActionOtherJoin  = 2 // Another player joins
	ActionOtherLeave = 3 // Another player leaves
)

// Channel name and password limits
const (
	MaxNameLength     = 100
	MaxPasswordLength = 100
)

// Channel restrictions
const (
	NoLevelRestriction = 0
	NoRaceRestriction  = 0
	NoClassRestriction = 0
)

// Channel represents a chat channel with membership and message routing capabilities
type Channel struct {
	mu               sync.RWMutex
	name             string
	password         string
	channelType      int
	levelRestriction int32
	raceRestriction  int32
	classRestriction int32
	members          []int32 // Character IDs
	discordEnabled   bool
	created          time.Time
}

// ChannelInfo provides basic channel information for client lists
type ChannelInfo struct {
	Name             string
	HasPassword      bool
	MemberCount      int
	LevelRestriction int32
	RaceRestriction  int32
	ClassRestriction int32
	ChannelType      int
}

// ChannelMember represents a member in a channel
type ChannelMember struct {
	CharacterID   int32
	CharacterName string
	Level         int32
	Race          int32
	Class         int32
	JoinedAt      time.Time
}

// NewChannel creates a new channel instance
func NewChannel(name string) *Channel {
	return &Channel{
		name:    name,
		members: make([]int32, 0),
		created: time.Now(),
	}
}

// SetName sets the channel name
func (c *Channel) SetName(name string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.name = name
}

// SetPassword sets the channel password
func (c *Channel) SetPassword(password string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.password = password
}

// SetType sets the channel type
func (c *Channel) SetType(channelType int) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.channelType = channelType
}

// SetLevelRestriction sets the minimum level required to join
func (c *Channel) SetLevelRestriction(level int32) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.levelRestriction = level
}

// SetRacesAllowed sets the race bitmask for allowed races
func (c *Channel) SetRacesAllowed(races int32) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.raceRestriction = races
}

// SetClassesAllowed sets the class bitmask for allowed classes
func (c *Channel) SetClassesAllowed(classes int32) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.classRestriction = classes
}

// SetDiscordEnabled enables or disables Discord integration for this channel
func (c *Channel) SetDiscordEnabled(enabled bool) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.discordEnabled = enabled
}

// GetName returns the channel name
func (c *Channel) GetName() string {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.name
}

// GetType returns the channel type
func (c *Channel) GetType() int {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.channelType
}

// GetNumClients returns the number of clients in the channel
func (c *Channel) GetNumClients() int {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return len(c.members)
}

// GetCreatedTime returns when the channel was created
func (c *Channel) GetCreatedTime() time.Time {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.created
}

// HasPassword returns true if the channel has a password
func (c *Channel) HasPassword() bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.password != ""
}

// PasswordMatches checks if the provided password matches the channel password
func (c *Channel) PasswordMatches(password string) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.password == password
}

// CanJoinChannelByLevel checks if a player's level meets the channel requirements
func (c *Channel) CanJoinChannelByLevel(level int32) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return level >= c.levelRestriction
}

// CanJoinChannelByRace checks if a player's race is allowed in the channel
func (c *Channel) CanJoinChannelByRace(raceID int32) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.raceRestriction == NoRaceRestriction || (c.raceRestriction&(1<<raceID)) != 0
}

// CanJoinChannelByClass checks if a player's class is allowed in the channel
func (c *Channel) CanJoinChannelByClass(classID int32) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.classRestriction == NoClassRestriction || (c.classRestriction&(1<<classID)) != 0
}

// IsInChannel checks if a character is in the channel
func (c *Channel) IsInChannel(characterID int32) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.isInChannel(characterID)
}

// isInChannel is the internal implementation without locking
func (c *Channel) isInChannel(characterID int32) bool {
	return slices.Contains(c.members, characterID)
}

// JoinChannel adds a character to the channel
func (c *Channel) JoinChannel(characterID int32) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.joinChannel(characterID)
}

// joinChannel is the internal implementation without locking
func (c *Channel) joinChannel(characterID int32) error {
	// Check if already in channel
	if c.isInChannel(characterID) {
		return fmt.Errorf("character %d is already in channel %s", characterID, c.name)
	}

	// Add to members list
	c.members = append(c.members, characterID)
	return nil
}

// LeaveChannel removes a character from the channel
func (c *Channel) LeaveChannel(characterID int32) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.leaveChannel(characterID)
}

// leaveChannel is the internal implementation without locking
func (c *Channel) leaveChannel(characterID int32) error {
	// Find and remove the character
	for i, memberID := range c.members {
		if memberID == characterID {
			// Remove member by swapping with last element and truncating
			c.members[i] = c.members[len(c.members)-1]
			c.members = c.members[:len(c.members)-1]
			return nil
		}
	}

	return fmt.Errorf("character %d is not in channel %s", characterID, c.name)
}

// GetMembers returns a copy of the current member list
func (c *Channel) GetMembers() []int32 {
	c.mu.RLock()
	defer c.mu.RUnlock()
	
	// Return a copy to prevent external modification
	members := make([]int32, len(c.members))
	copy(members, c.members)
	return members
}

// GetChannelInfo returns basic channel information
func (c *Channel) GetChannelInfo() ChannelInfo {
	c.mu.RLock()
	defer c.mu.RUnlock()
	
	return ChannelInfo{
		Name:             c.name,
		HasPassword:      c.password != "",
		MemberCount:      len(c.members),
		LevelRestriction: c.levelRestriction,
		RaceRestriction:  c.raceRestriction,
		ClassRestriction: c.classRestriction,
		ChannelType:      c.channelType,
	}
}

// ValidateJoin checks if a character can join the channel based on restrictions
func (c *Channel) ValidateJoin(level, race, class int32, password string) error {
	c.mu.RLock()
	defer c.mu.RUnlock()

	// Check password
	if c.password != "" && c.password != password {
		return fmt.Errorf("invalid password for channel %s", c.name)
	}

	// Check level restriction
	if !c.CanJoinChannelByLevel(level) {
		return fmt.Errorf("level %d does not meet minimum requirement of %d for channel %s", 
			level, c.levelRestriction, c.name)
	}

	// Check race restriction
	if !c.CanJoinChannelByRace(race) {
		return fmt.Errorf("race %d is not allowed in channel %s", race, c.name)
	}

	// Check class restriction
	if !c.CanJoinChannelByClass(class) {
		return fmt.Errorf("class %d is not allowed in channel %s", class, c.name)
	}

	return nil
}

// IsEmpty returns true if the channel has no members
func (c *Channel) IsEmpty() bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return len(c.members) == 0
}

// IsDiscordEnabled returns true if Discord integration is enabled for this channel
func (c *Channel) IsDiscordEnabled() bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.discordEnabled
}

// Copy creates a deep copy of the channel (useful for serialization or backups)
func (c *Channel) Copy() *Channel {
	c.mu.RLock()
	defer c.mu.RUnlock()

	newChannel := &Channel{
		name:             c.name,
		password:         c.password,
		channelType:      c.channelType,
		levelRestriction: c.levelRestriction,
		raceRestriction:  c.raceRestriction,
		classRestriction: c.classRestriction,
		discordEnabled:   c.discordEnabled,
		created:          c.created,
		members:          make([]int32, len(c.members)),
	}

	copy(newChannel.members, c.members)
	return newChannel
}

// GetRestrictions returns the channel's access restrictions
func (c *Channel) GetRestrictions() (level, race, class int32) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.levelRestriction, c.raceRestriction, c.classRestriction
}

// GetPassword returns the channel password (for admin purposes)
func (c *Channel) GetPassword() string {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.password
}

// UpdateRestrictions updates all access restrictions at once
func (c *Channel) UpdateRestrictions(level, race, class int32) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.levelRestriction = level
	c.raceRestriction = race
	c.classRestriction = class
}