package chat

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"
)

// ChatService provides high-level chat system management
type ChatService struct {
	manager *ChatManager
	mu      sync.RWMutex
}

// NewChatService creates a new chat service instance
func NewChatService(database ChannelDatabase, clientManager ClientManager, playerManager PlayerManager, languageProcessor LanguageProcessor) *ChatService {
	return &ChatService{
		manager: NewChatManager(database, clientManager, playerManager, languageProcessor),
	}
}

// Initialize initializes the chat service and loads world channels
func (cs *ChatService) Initialize(ctx context.Context) error {
	cs.mu.Lock()
	defer cs.mu.Unlock()

	return cs.manager.Initialize(ctx)
}

// ProcessChannelCommand processes chat channel commands (join, leave, create, etc.)
func (cs *ChatService) ProcessChannelCommand(characterID int32, command, channelName string, args ...string) error {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	switch strings.ToLower(command) {
	case "join":
		password := ""
		if len(args) > 0 {
			password = args[0]
		}
		return cs.manager.JoinChannel(characterID, channelName, password)

	case "leave":
		return cs.manager.LeaveChannel(characterID, channelName)

	case "create":
		password := ""
		if len(args) > 0 {
			password = args[0]
		}
		return cs.manager.CreateChannel(channelName, password)

	case "tell", "say":
		if len(args) == 0 {
			return fmt.Errorf("no message provided")
		}
		message := strings.Join(args, " ")
		return cs.manager.TellChannel(characterID, channelName, message)

	case "who", "list":
		return cs.manager.SendChannelUserList(characterID, channelName)

	default:
		return fmt.Errorf("unknown channel command: %s", command)
	}
}

// SendChannelMessage sends a message to a channel
func (cs *ChatService) SendChannelMessage(senderID int32, channelName string, message string, customName ...string) error {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	return cs.manager.TellChannel(senderID, channelName, message, customName...)
}

// JoinChannel adds a character to a channel
func (cs *ChatService) JoinChannel(characterID int32, channelName string, password ...string) error {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	return cs.manager.JoinChannel(characterID, channelName, password...)
}

// LeaveChannel removes a character from a channel
func (cs *ChatService) LeaveChannel(characterID int32, channelName string) error {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	return cs.manager.LeaveChannel(characterID, channelName)
}

// LeaveAllChannels removes a character from all channels
func (cs *ChatService) LeaveAllChannels(characterID int32) error {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	return cs.manager.LeaveAllChannels(characterID)
}

// CreateChannel creates a new custom channel
func (cs *ChatService) CreateChannel(channelName string, password ...string) error {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	return cs.manager.CreateChannel(channelName, password...)
}

// GetWorldChannelList returns available world channels for a character
func (cs *ChatService) GetWorldChannelList(characterID int32) ([]ChannelInfo, error) {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	return cs.manager.GetWorldChannelList(characterID)
}

// ChannelExists checks if a channel exists
func (cs *ChatService) ChannelExists(channelName string) bool {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	return cs.manager.ChannelExists(channelName)
}

// IsInChannel checks if a character is in a channel
func (cs *ChatService) IsInChannel(characterID int32, channelName string) bool {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	return cs.manager.IsInChannel(characterID, channelName)
}

// GetChannelInfo returns information about a specific channel
func (cs *ChatService) GetChannelInfo(channelName string) (*ChannelInfo, error) {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	channel := cs.manager.GetChannel(channelName)
	if channel == nil {
		return nil, fmt.Errorf("channel %s not found", channelName)
	}

	info := channel.GetChannelInfo()
	return &info, nil
}

// GetStatistics returns chat system statistics
func (cs *ChatService) GetStatistics() ChatStatistics {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	return cs.manager.GetStatistics()
}

// SendChannelUserList sends the user list for a channel to a character
func (cs *ChatService) SendChannelUserList(requesterID int32, channelName string) error {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	return cs.manager.SendChannelUserList(requesterID, channelName)
}

// ValidateChannelName checks if a channel name is valid
func (cs *ChatService) ValidateChannelName(channelName string) error {
	if len(channelName) == 0 {
		return fmt.Errorf("channel name cannot be empty")
	}

	if len(channelName) > MaxChannelNameLength {
		return fmt.Errorf("channel name too long: %d > %d", len(channelName), MaxChannelNameLength)
	}

	// Check for invalid characters
	if strings.ContainsAny(channelName, " \t\n\r") {
		return fmt.Errorf("channel name cannot contain whitespace")
	}

	return nil
}

// ValidateChannelPassword checks if a channel password is valid
func (cs *ChatService) ValidateChannelPassword(password string) error {
	if len(password) > MaxChannelPasswordLength {
		return fmt.Errorf("channel password too long: %d > %d", len(password), MaxChannelPasswordLength)
	}

	return nil
}

// GetChannelMembers returns the list of members in a channel
func (cs *ChatService) GetChannelMembers(channelName string) ([]int32, error) {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	channel := cs.manager.GetChannel(channelName)
	if channel == nil {
		return nil, fmt.Errorf("channel %s not found", channelName)
	}

	return channel.GetMembers(), nil
}

// CleanupEmptyChannels removes empty custom channels (called periodically)
func (cs *ChatService) CleanupEmptyChannels() int {
	cs.mu.Lock()
	defer cs.mu.Unlock()

	removed := 0
	for name, channel := range cs.manager.channels {
		if channel.channelType == ChannelTypeCustom && channel.IsEmpty() {
			// Check if channel has been empty for a reasonable time
			if time.Since(channel.created) > 5*time.Minute {
				delete(cs.manager.channels, name)
				removed++
			}
		}
	}

	return removed
}

// BroadcastSystemMessage sends a system message to all members of a channel
func (cs *ChatService) BroadcastSystemMessage(channelName string, message string, systemName string) error {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	return cs.manager.TellChannel(0, channelName, message, systemName)
}

// GetActiveChannels returns a list of channels that have active members
func (cs *ChatService) GetActiveChannels() []string {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	var activeChannels []string
	for name, channel := range cs.manager.channels {
		if !channel.IsEmpty() {
			activeChannels = append(activeChannels, name)
		}
	}

	return activeChannels
}

// GetChannelsByType returns channels of a specific type
func (cs *ChatService) GetChannelsByType(channelType int) []string {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	var channels []string
	for name, channel := range cs.manager.channels {
		if channel.channelType == channelType {
			channels = append(channels, name)
		}
	}

	return channels
}

// ProcessChannelFilter applies filtering to channel lists based on player criteria
func (cs *ChatService) ProcessChannelFilter(characterID int32, filter ChannelFilter) ([]ChannelInfo, error) {
	cs.mu.RLock()
	defer cs.mu.RUnlock()

	playerInfo, err := cs.manager.playerManager.GetPlayerInfo(characterID)
	if err != nil {
		return nil, fmt.Errorf("failed to get player info: %w", err)
	}

	var filteredChannels []ChannelInfo
	for _, channel := range cs.manager.channels {
		// Apply type filters
		if !filter.IncludeWorld && channel.channelType == ChannelTypeWorld {
			continue
		}
		if !filter.IncludeCustom && channel.channelType == ChannelTypeCustom {
			continue
		}

		// Apply level range filters
		if filter.MinLevel > 0 && playerInfo.Level < filter.MinLevel {
			continue
		}
		if filter.MaxLevel > 0 && playerInfo.Level > filter.MaxLevel {
			continue
		}

		// Apply race filter
		if filter.Race > 0 && playerInfo.Race != filter.Race {
			continue
		}

		// Apply class filter
		if filter.Class > 0 && playerInfo.Class != filter.Class {
			continue
		}

		// Check if player can actually join the channel
		if cs.manager.canJoinChannel(playerInfo.Level, playerInfo.Race, playerInfo.Class,
			channel.levelRestriction, channel.raceRestriction, channel.classRestriction) {
			filteredChannels = append(filteredChannels, channel.GetChannelInfo())
		}
	}

	return filteredChannels, nil
}
