package main

import (
	"fmt"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

type Channel struct {
	name       string
	topic      string
	topicBy    string
	topicTime  time.Time
	clients    map[string]*Client
	operators  map[string]*Client
	halfops    map[string]*Client
	voices     map[string]*Client
	owners     map[string]*Client
	admins     map[string]*Client
	modes      map[rune]bool
	key        string
	limit      int
	banList    []string
	quietList  []string // Users who can join but not speak (+q)
	exceptList []string // Users exempt from bans (+e)
	inviteList []string // Users who can join invite-only channels (+I)
	created    time.Time

	// Advanced mode settings
	floodSettings string // Flood protection settings (e.g., "10:5")
	joinThrottle  string // Join throttling settings (e.g., "3:10")

	mu sync.RWMutex
}

func NewChannel(name string) *Channel {
	return &Channel{
		name:       name,
		clients:    make(map[string]*Client),
		operators:  make(map[string]*Client),
		halfops:    make(map[string]*Client),
		voices:     make(map[string]*Client),
		owners:     make(map[string]*Client),
		admins:     make(map[string]*Client),
		modes:      make(map[rune]bool),
		banList:    make([]string, 0),
		quietList:  make([]string, 0),
		exceptList: make([]string, 0),
		inviteList: make([]string, 0),
		created:    time.Now(),
	}
}

func (ch *Channel) Name() string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return ch.name
}

func (ch *Channel) Topic() string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return ch.topic
}

func (ch *Channel) TopicBy() string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return ch.topicBy
}

func (ch *Channel) TopicTime() time.Time {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return ch.topicTime
}

func (ch *Channel) SetTopic(topic, by string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()
	ch.topic = topic
	ch.topicBy = by
	ch.topicTime = time.Now()
}

func (ch *Channel) AddClient(client *Client) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	nick := strings.ToLower(client.Nick())

	// Check if client is already in the channel
	if _, exists := ch.clients[nick]; exists {
		// Client already in channel, don't add again
		return
	}

	ch.clients[nick] = client
	client.AddChannel(ch)

	// First user gets configured founder mode (default: operator)
	if len(ch.clients) == 1 {
		// Get the configured founder mode from server config
		founderMode := "o" // Default fallback
		if client.server != nil && client.server.config != nil {
			founderMode = client.server.config.Channels.FounderMode
		}

		// Apply the appropriate mode based on configuration
		switch founderMode {
		case "q":
			ch.owners[nick] = client
		case "a":
			ch.admins[nick] = client
		case "o":
			ch.operators[nick] = client
		case "h":
			ch.halfops[nick] = client
		case "v":
			ch.voices[nick] = client
		default:
			// Invalid config, fallback to operator
			ch.operators[nick] = client
		}
	}
}

func (ch *Channel) RemoveClient(client *Client) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	nick := strings.ToLower(client.Nick())
	delete(ch.clients, nick)
	delete(ch.operators, nick)
	delete(ch.halfops, nick)
	delete(ch.voices, nick)
	delete(ch.owners, nick)
	delete(ch.admins, nick)
	client.RemoveChannel(ch.name)
}

func (ch *Channel) HasClient(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	_, exists := ch.clients[strings.ToLower(client.Nick())]
	return exists
}

func (ch *Channel) IsOperator(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	_, exists := ch.operators[strings.ToLower(client.Nick())]
	return exists
}

func (ch *Channel) IsVoice(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	_, exists := ch.voices[strings.ToLower(client.Nick())]
	return exists
}

func (ch *Channel) IsHalfop(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	_, exists := ch.halfops[strings.ToLower(client.Nick())]
	return exists
}

func (ch *Channel) IsOwner(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	_, exists := ch.owners[strings.ToLower(client.Nick())]
	return exists
}

func (ch *Channel) IsQuieted(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return ch.isQuietedUnsafe(client)
}

func (ch *Channel) isQuietedUnsafe(client *Client) bool {
	hostmask := fmt.Sprintf("%s!%s@%s", client.Nick(), client.User(), client.Host())

	for _, quiet := range ch.quietList {
		if ch.matchesBanMask(client, quiet, hostmask) {
			return true
		}
	}
	return false
}

func (ch *Channel) SetOperator(client *Client, isOp bool) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	nick := strings.ToLower(client.Nick())
	if isOp {
		ch.operators[nick] = client
	} else {
		delete(ch.operators, nick)
	}
}

func (ch *Channel) SetVoice(client *Client, hasVoice bool) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	nick := strings.ToLower(client.Nick())
	if hasVoice {
		ch.voices[nick] = client
	} else {
		delete(ch.voices, nick)
	}
}

func (ch *Channel) SetHalfop(client *Client, isHalfop bool) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	nick := strings.ToLower(client.Nick())
	if isHalfop {
		ch.halfops[nick] = client
	} else {
		delete(ch.halfops, nick)
	}
}

func (ch *Channel) SetOwner(client *Client, isOwner bool) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	nick := strings.ToLower(client.Nick())
	if isOwner {
		ch.owners[nick] = client
	} else {
		delete(ch.owners, nick)
	}
}

func (ch *Channel) IsAdmin(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	_, exists := ch.admins[strings.ToLower(client.Nick())]
	return exists
}

func (ch *Channel) SetAdmin(client *Client, isAdmin bool) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	nick := strings.ToLower(client.Nick())
	if isAdmin {
		ch.admins[nick] = client
	} else {
		delete(ch.admins, nick)
	}
}

func (ch *Channel) GetClients() []*Client {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	clients := make([]*Client, 0, len(ch.clients))
	for _, client := range ch.clients {
		clients = append(clients, client)
	}
	return clients
}

func (ch *Channel) GetClientCount() int {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return len(ch.clients)
}

func (ch *Channel) UserCount() int {
	return ch.GetClientCount()
}

func (ch *Channel) Broadcast(message string, exclude *Client) {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	for _, client := range ch.clients {
		if exclude != nil && client.Nick() == exclude.Nick() {
			continue
		}
		client.SendMessage(message)
	}
}

func (ch *Channel) BroadcastFrom(source, message string, exclude *Client) {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	for _, client := range ch.clients {
		if exclude != nil && client.Nick() == exclude.Nick() {
			continue
		}
		client.SendFrom(source, message)
	}
}

func (ch *Channel) HasMode(mode rune) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return ch.modes[mode]
}

func (ch *Channel) SetMode(mode rune, set bool) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	if set {
		ch.modes[mode] = true
	} else {
		delete(ch.modes, mode)
	}
}

func (ch *Channel) GetModes() string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	var modes []rune
	for mode := range ch.modes {
		modes = append(modes, mode)
	}

	if len(modes) == 0 {
		return ""
	}

	return "+" + string(modes)
}

func (ch *Channel) CanSendMessage(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	// Check if user is quieted first
	if ch.isQuietedUnsafe(client) {
		// Only owners, operators, and halfops can speak when quieted
		nick := strings.ToLower(client.Nick())
		_, isOwner := ch.owners[nick]
		_, isOp := ch.operators[nick]
		_, isHalfop := ch.halfops[nick]

		if !isOwner && !isOp && !isHalfop {
			return false
		}
	}

	// If channel is not moderated, anyone in the channel can send
	if !ch.modes['m'] {
		return true
	}

	// In moderated channels, only owners, operators, halfops and voiced users can send messages
	nick := strings.ToLower(client.Nick())
	_, isOwner := ch.owners[nick]
	_, isOp := ch.operators[nick]
	_, isHalfop := ch.halfops[nick]
	_, hasVoice := ch.voices[nick]

	return isOwner || isOp || isHalfop || hasVoice
}

func (ch *Channel) Key() string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return ch.key
}

func (ch *Channel) SetKey(key string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()
	ch.key = key
}

func (ch *Channel) Limit() int {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return ch.limit
}

func (ch *Channel) SetLimit(limit int) {
	ch.mu.Lock()
	defer ch.mu.Unlock()
	ch.limit = limit
}

func (ch *Channel) GetNamesReply() string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	var names []string
	for _, client := range ch.clients {
		prefix := ""
		if ch.IsOwner(client) {
			prefix = "~"
		} else if ch.IsOperator(client) {
			prefix = "@"
		} else if ch.IsHalfop(client) {
			prefix = "%"
		} else if ch.IsVoice(client) {
			prefix = "+"
		}
		names = append(names, prefix+client.Nick())
	}

	return strings.Join(names, " ")
}

func (ch *Channel) CanSpeak(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	// If channel is not moderated, anyone can speak
	if !ch.modes['m'] {
		return true
	}

	// Operators and voiced users can always speak
	return ch.IsOperator(client) || ch.IsVoice(client)
}

func (ch *Channel) CanJoin(client *Client, key string) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	// Check if invite-only
	if ch.modes['i'] {
		// Check invite list
		for _, mask := range ch.inviteList {
			if ch.matchesMask(client.Prefix(), mask) {
				return true
			}
		}
		return false
	}

	// Check key
	if ch.modes['k'] && ch.key != key {
		return false
	}

	// Check limit
	if ch.modes['l'] && len(ch.clients) >= ch.limit {
		return false
	}

	// Check ban list
	for _, mask := range ch.banList {
		if ch.matchesMask(client.Prefix(), mask) {
			// Check exception list
			for _, exceptMask := range ch.exceptList {
				if ch.matchesMask(client.Prefix(), exceptMask) {
					return true
				}
			}
			return false
		}
	}

	return true
}

func (ch *Channel) matchesMask(target, mask string) bool {
	// Simple mask matching - should be enhanced for production
	return strings.Contains(strings.ToLower(target), strings.ToLower(mask))
}

func (ch *Channel) AddBan(mask string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()
	ch.banList = append(ch.banList, mask)
}

func (ch *Channel) RemoveBan(mask string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	for i, ban := range ch.banList {
		if ban == mask {
			ch.banList = append(ch.banList[:i], ch.banList[i+1:]...)
			break
		}
	}
}

func (ch *Channel) GetBans() []string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	bans := make([]string, len(ch.banList))
	copy(bans, ch.banList)
	return bans
}

// Extended ban list management
func (ch *Channel) AddQuiet(mask string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()
	ch.quietList = append(ch.quietList, mask)
}

func (ch *Channel) RemoveQuiet(mask string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	for i, quiet := range ch.quietList {
		if quiet == mask {
			ch.quietList = append(ch.quietList[:i], ch.quietList[i+1:]...)
			break
		}
	}
}

func (ch *Channel) GetQuiets() []string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	quiets := make([]string, len(ch.quietList))
	copy(quiets, ch.quietList)
	return quiets
}

func (ch *Channel) AddExcept(mask string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()
	ch.exceptList = append(ch.exceptList, mask)
}

func (ch *Channel) RemoveExcept(mask string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	for i, except := range ch.exceptList {
		if except == mask {
			ch.exceptList = append(ch.exceptList[:i], ch.exceptList[i+1:]...)
			break
		}
	}
}

func (ch *Channel) GetExcepts() []string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	excepts := make([]string, len(ch.exceptList))
	copy(excepts, ch.exceptList)
	return excepts
}

func (ch *Channel) AddInviteException(mask string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()
	ch.inviteList = append(ch.inviteList, mask)
}

func (ch *Channel) RemoveInviteException(mask string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()

	for i, invite := range ch.inviteList {
		if invite == mask {
			ch.inviteList = append(ch.inviteList[:i], ch.inviteList[i+1:]...)
			break
		}
	}
}

func (ch *Channel) GetInviteExceptions() []string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	invites := make([]string, len(ch.inviteList))
	copy(invites, ch.inviteList)
	return invites
}

// IsExempt checks if a client is exempt from bans
func (ch *Channel) IsExempt(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	hostmask := fmt.Sprintf("%s!%s@%s", client.Nick(), client.User(), client.Host())

	for _, except := range ch.exceptList {
		if ch.matchesBanMask(client, except, hostmask) {
			return true
		}
	}
	return false
}

// CanJoinInviteOnly checks if a client can join an invite-only channel
func (ch *Channel) CanJoinInviteOnly(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	hostmask := fmt.Sprintf("%s!%s@%s", client.Nick(), client.User(), client.Host())

	for _, invite := range ch.inviteList {
		if ch.matchesBanMask(client, invite, hostmask) {
			return true
		}
	}
	return false
}

func (ch *Channel) Created() time.Time {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return ch.created
}

// IsBanned checks if a client matches any ban mask in the channel
func (ch *Channel) IsBanned(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	// Check exemptions first - if exempt, not banned
	if ch.isExemptUnsafe(client) {
		return false
	}

	hostmask := fmt.Sprintf("%s!%s@%s", client.Nick(), client.User(), client.Host())

	for _, ban := range ch.banList {
		if ch.matchesBanMask(client, ban, hostmask) {
			return true
		}
	}
	return false
}

// isExemptUnsafe checks exemptions without locking (internal use)
func (ch *Channel) isExemptUnsafe(client *Client) bool {
	hostmask := fmt.Sprintf("%s!%s@%s", client.Nick(), client.User(), client.Host())

	for _, except := range ch.exceptList {
		if ch.matchesBanMask(client, except, hostmask) {
			return true
		}
	}
	return false
}

// matchesBanMask checks if a client matches a ban mask (supports extended bans)
func (ch *Channel) matchesBanMask(client *Client, banMask, hostmask string) bool {
	// Check for extended ban format: ~type:parameter or ~type parameter
	if strings.HasPrefix(banMask, "~") {
		return ch.matchesExtendedBan(client, banMask)
	}

	// Traditional hostmask ban
	return matchWildcard(banMask, hostmask)
}

// matchesExtendedBan handles extended ban types
func (ch *Channel) matchesExtendedBan(client *Client, extban string) bool {
	if len(extban) < 2 || extban[0] != '~' {
		return false
	}

	// Parse ~type:parameter or ~type parameter format
	var banType string
	var parameter string

	if strings.Contains(extban, ":") {
		// Format: ~type:parameter
		parts := strings.SplitN(extban[1:], ":", 2)
		banType = parts[0]
		if len(parts) > 1 {
			parameter = parts[1]
		}
	} else {
		// Format: ~type parameter (space separated)
		parts := strings.Fields(extban[1:])
		if len(parts) > 0 {
			banType = parts[0]
			if len(parts) > 1 {
				parameter = strings.Join(parts[1:], " ")
			}
		}
	}

	switch banType {
	case "a": // Account ban: ~a:accountname or ~a accountname
		if parameter == "" {
			// ~a with no parameter bans unregistered users
			return client.account == ""
		}
		// ~a:account bans specific account
		return client.account == parameter

	case "c": // Channel ban: ~c:#channel - ban users in another channel
		if parameter == "" || !strings.HasPrefix(parameter, "#") {
			return false
		}
		targetChannel := client.server.GetChannel(parameter)
		return targetChannel != nil && targetChannel.HasClient(client)

	case "j": // Join prevent: ~j:#channel - prevent joining if in another channel
		if parameter == "" || !strings.HasPrefix(parameter, "#") {
			return false
		}
		targetChannel := client.server.GetChannel(parameter)
		return targetChannel != nil && targetChannel.HasClient(client)

	case "n": // Nick pattern ban: ~n:pattern
		if parameter == "" {
			return false
		}
		return matchWildcard(parameter, client.Nick())

	case "q": // Quiet ban: ~q:mask - this is a special case for quiet functionality
		// When used in regular ban list, ~q acts as a quiet
		if parameter == "" {
			// ~q with no parameter quiets everyone
			return true
		}
		// ~q:mask quiets matching users - check against hostmask pattern
		hostmask := fmt.Sprintf("%s!%s@%s", client.Nick(), client.User(), client.Host())
		return matchWildcard(parameter, hostmask)

	case "r": // Real name ban: ~r:pattern
		if parameter == "" {
			return false
		}
		return matchWildcard(parameter, client.realname)

	case "s": // Server ban: ~s:servername
		if parameter == "" {
			return false
		}
		return matchWildcard(parameter, client.server.config.Server.Name)

	case "o": // Operator ban: ~o (bans all opers)
		return client.IsOper()

	case "z": // Non-SSL ban: ~z (bans non-SSL users)
		return !client.ssl

	case "Z": // SSL-only ban: ~Z (bans SSL users)
		return client.ssl

	case "u": // Username pattern ban: ~u:pattern
		if parameter == "" {
			return false
		}
		return matchWildcard(parameter, client.User())

	case "h": // Hostname pattern ban: ~h:pattern
		if parameter == "" {
			return false
		}
		return matchWildcard(parameter, client.Host())

	case "i": // IP ban: ~i:ip/cidr
		if parameter == "" {
			return false
		}
		// Simple IP matching for now (could be enhanced with CIDR)
		return matchWildcard(parameter, client.Host())

	case "R": // Registered only: ~R (bans unregistered users)
		return client.account == ""

	case "m": // Mute ban: ~m:mask - similar to quiet
		if parameter == "" {
			return true
		}
		hostmask := fmt.Sprintf("%s!%s@%s", client.Nick(), client.User(), client.Host())
		return matchWildcard(parameter, hostmask)

	default:
		// Unknown extended ban type
		return false
	}
}

// IsInvited checks if a client is on the invite list for the channel
func (ch *Channel) IsInvited(client *Client) bool {
	ch.mu.RLock()
	defer ch.mu.RUnlock()

	hostmask := fmt.Sprintf("%s!%s@%s", client.Nick(), client.User(), client.Host())

	for _, invite := range ch.inviteList {
		if matchWildcard(invite, hostmask) {
			return true
		}
	}
	return false
}

// matchWildcard checks if a pattern with wildcards (* and ?) matches a string
func matchWildcard(pattern, str string) bool {
	matched, _ := filepath.Match(strings.ToLower(pattern), strings.ToLower(str))
	return matched
}

// SetFloodSettings sets the flood protection settings
func (ch *Channel) SetFloodSettings(settings string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()
	ch.floodSettings = settings
}

// GetFloodSettings returns the flood protection settings
func (ch *Channel) GetFloodSettings() string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return ch.floodSettings
}

// SetJoinThrottle sets the join throttling settings
func (ch *Channel) SetJoinThrottle(settings string) {
	ch.mu.Lock()
	defer ch.mu.Unlock()
	ch.joinThrottle = settings
}

// GetJoinThrottle returns the join throttling settings
func (ch *Channel) GetJoinThrottle() string {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return ch.joinThrottle
}
