package main

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"log"
	"strings"
	"time"
)

// ServicesManager handles all network services
type ServicesManager struct {
	server   *Server
	config   *ServicesConfig
	nickServ *NickServ
	chanServ *ChanServ
	operServ *OperServ
	memoServ *MemoServ
	enabled  bool
}

// NickServ handles nickname registration and authentication
type NickServ struct {
	manager  *ServicesManager
	nick     string
	accounts map[string]*NickAccount
}

// ChanServ handles channel registration and management
type ChanServ struct {
	manager  *ServicesManager
	nick     string
	channels map[string]*RegisteredChannel
}

// OperServ handles network operator services
type OperServ struct {
	manager *ServicesManager
	nick    string
}

// MemoServ handles user-to-user messages
type MemoServ struct {
	manager *ServicesManager
	nick    string
	memos   map[string][]*Memo
}

// NickAccount represents a registered nickname
type NickAccount struct {
	Nick         string            `json:"nick"`
	PasswordHash string            `json:"password_hash"`
	Email        string            `json:"email"`
	RegisterTime time.Time         `json:"register_time"`
	LastSeen     time.Time         `json:"last_seen"`
	Settings     map[string]string `json:"settings"`
	AccessList   []string          `json:"access_list"`
	Flags        []string          `json:"flags"`
}

// RegisteredChannel represents a registered channel
type RegisteredChannelServices struct {
	Name         string                    `json:"name"`
	Founder      string                    `json:"founder"`
	RegisterTime time.Time                 `json:"register_time"`
	Topic        string                    `json:"topic"`
	Modes        string                    `json:"modes"`
	AccessList   map[string]*ChannelAccess `json:"access_list"`
	Settings     map[string]string         `json:"settings"`
	AutoModes    map[string]string         `json:"auto_modes"`
}

// ChannelAccess represents access levels for channels - using the one from database.go
// type ChannelAccess struct {
// 	Nick     string    `json:"nick"`
// 	Level    int       `json:"level"` // 0=banned, 1=voice, 2=halfop, 3=op, 4=sop, 5=founder
// 	SetBy    string    `json:"set_by"`
// 	SetTime  time.Time `json:"set_time"`
// 	LastUsed time.Time `json:"last_used"`
// }

// Memo represents a user memo
type Memo struct {
	From    string    `json:"from"`
	To      string    `json:"to"`
	Message string    `json:"message"`
	Time    time.Time `json:"time"`
	Read    bool      `json:"read"`
}

// ServicesConfig configuration for services
type ServicesConfig struct {
	Enable   bool           `json:"enable"`
	NickServ NickServConfig `json:"nickserv"`
	ChanServ ChanServConfig `json:"chanserv"`
	OperServ OperServConfig `json:"operserv"`
	MemoServ MemoServConfig `json:"memoserv"`
	Database DatabaseConfig `json:"database"`
}

type NickServConfig struct {
	Enable          bool   `json:"enable"`
	Nick            string `json:"nick"`
	User            string `json:"user"`
	Host            string `json:"host"`
	Realname        string `json:"realname"`
	ExpireTime      int    `json:"expire_time"`      // Days until unused nicks expire
	IdentifyTimeout int    `json:"identify_timeout"` // Seconds to identify before kill
	EmailVerify     bool   `json:"email_verify"`
	RestrictReg     bool   `json:"restrict_registration"`
}

type ChanServConfig struct {
	Enable      bool   `json:"enable"`
	Nick        string `json:"nick"`
	User        string `json:"user"`
	Host        string `json:"host"`
	Realname    string `json:"realname"`
	ExpireTime  int    `json:"expire_time"`  // Days until unused channels expire
	MaxChannels int    `json:"max_channels"` // Max channels per user
	AutoDeop    bool   `json:"auto_deop"`    // Auto-deop users without access
}

type OperServConfig struct {
	Enable   bool   `json:"enable"`
	Nick     string `json:"nick"`
	User     string `json:"user"`
	Host     string `json:"host"`
	Realname string `json:"realname"`
}

type MemoServConfig struct {
	Enable     bool   `json:"enable"`
	Nick       string `json:"nick"`
	User       string `json:"user"`
	Host       string `json:"host"`
	Realname   string `json:"realname"`
	MaxMemos   int    `json:"max_memos"`
	MemoExpire int    `json:"memo_expire"` // Days until memos expire
}

// NewServicesManager creates a new services manager
func NewServicesManager(server *Server, config *ServicesConfig) *ServicesManager {
	sm := &ServicesManager{
		server:  server,
		config:  config,
		enabled: config.Enable,
	}

	if config.NickServ.Enable {
		sm.nickServ = &NickServ{
			manager:  sm,
			nick:     config.NickServ.Nick,
			accounts: make(map[string]*NickAccount),
		}
	}

	if config.ChanServ.Enable {
		sm.chanServ = &ChanServ{
			manager:  sm,
			nick:     config.ChanServ.Nick,
			channels: make(map[string]*RegisteredChannel),
		}
	}

	if config.OperServ.Enable {
		sm.operServ = &OperServ{
			manager: sm,
			nick:    config.OperServ.Nick,
		}
	}

	if config.MemoServ.Enable {
		sm.memoServ = &MemoServ{
			manager: sm,
			nick:    config.MemoServ.Nick,
			memos:   make(map[string][]*Memo),
		}
	}

	return sm
}

// Start initializes and starts all enabled services
func (sm *ServicesManager) Start() error {
	if !sm.enabled {
		return nil
	}

	log.Println("Starting TechIRCd Services...")

	// Create service clients
	if sm.nickServ != nil {
		sm.createServiceClient(sm.config.NickServ.Nick, sm.config.NickServ.User,
			sm.config.NickServ.Host, sm.config.NickServ.Realname)
	}

	if sm.chanServ != nil {
		sm.createServiceClient(sm.config.ChanServ.Nick, sm.config.ChanServ.User,
			sm.config.ChanServ.Host, sm.config.ChanServ.Realname)
	}

	if sm.operServ != nil {
		sm.createServiceClient(sm.config.OperServ.Nick, sm.config.OperServ.User,
			sm.config.OperServ.Host, sm.config.OperServ.Realname)
	}

	if sm.memoServ != nil {
		sm.createServiceClient(sm.config.MemoServ.Nick, sm.config.MemoServ.User,
			sm.config.MemoServ.Host, sm.config.MemoServ.Realname)
	}

	log.Println("Services started successfully")
	return nil
}

// createServiceClient creates a virtual client for a service
func (sm *ServicesManager) createServiceClient(nick, user, host, realname string) {
	// Create a virtual service client that appears as a regular user
	serviceClient := &Client{
		nick:         nick,
		user:         user,
		host:         host,
		realname:     realname,
		server:       sm.server,
		modes:        make(map[rune]bool),
		channels:     make(map[string]*Channel),
		capabilities: make(map[string]bool),
	}

	// Set service modes
	serviceClient.modes['S'] = true // Service mode
	serviceClient.modes['o'] = true // Operator mode

	// Add to server
	sm.server.clients[strings.ToLower(nick)] = serviceClient

	// Send introduction to network
	sm.server.BroadcastToServers(fmt.Sprintf(":%s NICK %s", sm.server.config.Server.Name, nick))
}

// HandleMessage processes messages directed to services
func (sm *ServicesManager) HandleMessage(from *Client, target, message string) bool {
	if !sm.enabled {
		return false
	}

	lowerTarget := strings.ToLower(target)
	parts := strings.Fields(message)
	if len(parts) == 0 {
		return false
	}

	command := strings.ToUpper(parts[0])

	switch lowerTarget {
	case strings.ToLower(sm.config.NickServ.Nick):
		return sm.handleNickServCommand(from, command, parts[1:])
	case strings.ToLower(sm.config.ChanServ.Nick):
		return sm.handleChanServCommand(from)
	case strings.ToLower(sm.config.OperServ.Nick):
		return sm.handleOperServCommand(from)
	case strings.ToLower(sm.config.MemoServ.Nick):
		return sm.handleMemoServCommand(from)
	}

	return false
}

// NickServ command handlers
func (sm *ServicesManager) handleNickServCommand(from *Client, command string, args []string) bool {
	switch command {
	case "REGISTER":
		return sm.handleNickServRegister(from, args)
	case "IDENTIFY":
		return sm.handleNickServIdentify(from, args)
	case "INFO":
		return sm.handleNickServInfo(from, args)
	case "DROP":
		return sm.handleNickServDrop(from, args)
	case "SET":
		return sm.handleNickServSet(from)
	case "ACCESS":
		return sm.handleNickServAccess(from)
	case "HELP":
		return sm.handleNickServHelp(from)
	default:
		sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
			fmt.Sprintf("Unknown command %s. Type /msg %s HELP for help.", command, sm.config.NickServ.Nick))
		return true
	}
}

func (sm *ServicesManager) handleNickServRegister(from *Client, args []string) bool {
	if len(args) < 2 {
		sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
			"Syntax: REGISTER <password> <email>")
		return true
	}

	nick := strings.ToLower(from.nick)
	password := args[0]
	email := args[1]

	// Check if nick is already registered
	if _, exists := sm.nickServ.accounts[nick]; exists {
		sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
			"This nickname is already registered.")
		return true
	}

	// Hash password
	hash := sha256.Sum256([]byte(password))
	passwordHash := hex.EncodeToString(hash[:])

	// Create account
	account := &NickAccount{
		Nick:         from.nick,
		PasswordHash: passwordHash,
		Email:        email,
		RegisterTime: time.Now(),
		LastSeen:     time.Now(),
		Settings:     make(map[string]string),
		AccessList:   []string{},
		Flags:        []string{},
	}

	sm.nickServ.accounts[nick] = account

	// Set user as identified
	from.account = from.nick

	sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
		fmt.Sprintf("Nickname %s has been registered successfully.", from.nick))

	return true
}

func (sm *ServicesManager) handleNickServIdentify(from *Client, args []string) bool {
	if len(args) < 1 {
		sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
			"Syntax: IDENTIFY <password>")
		return true
	}

	nick := strings.ToLower(from.nick)
	password := args[0]

	account, exists := sm.nickServ.accounts[nick]
	if !exists {
		sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
			"This nickname is not registered.")
		return true
	}

	// Check password
	hash := sha256.Sum256([]byte(password))
	passwordHash := hex.EncodeToString(hash[:])

	if account.PasswordHash != passwordHash {
		sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
			"Invalid password.")
		return true
	}

	// Set user as identified
	from.account = from.nick
	account.LastSeen = time.Now()

	sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
		fmt.Sprintf("You are now identified for %s.", from.nick))

	return true
}

func (sm *ServicesManager) handleNickServInfo(from *Client, args []string) bool {
	var targetNick string
	if len(args) > 0 {
		targetNick = args[0]
	} else {
		targetNick = from.nick
	}

	nick := strings.ToLower(targetNick)
	account, exists := sm.nickServ.accounts[nick]
	if !exists {
		sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
			fmt.Sprintf("The nickname %s is not registered.", targetNick))
		return true
	}

	sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
		fmt.Sprintf("Information for %s:", account.Nick))
	sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
		fmt.Sprintf("Registered: %s", account.RegisterTime.Format("Jan 02, 2006 15:04:05 MST")))
	sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
		fmt.Sprintf("Last seen: %s", account.LastSeen.Format("Jan 02, 2006 15:04:05 MST")))

	return true
}

func (sm *ServicesManager) handleNickServDrop(from *Client, _ []string) bool {
	nick := strings.ToLower(from.nick)

	if from.account != from.nick {
		sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
			"You must be identified to drop your nickname.")
		return true
	}

	delete(sm.nickServ.accounts, nick)
	from.account = ""

	sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
		fmt.Sprintf("Nickname %s has been dropped.", from.nick))

	return true
}

func (sm *ServicesManager) handleNickServSet(from *Client) bool {
	// Handle SET commands (EMAIL, PASSWORD, etc.)
	sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
		"SET command not yet implemented.")
	return true
}

func (sm *ServicesManager) handleNickServAccess(from *Client) bool {
	// Handle ACCESS commands (ADD, DEL, LIST)
	sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
		"ACCESS command not yet implemented.")
	return true
}

func (sm *ServicesManager) handleNickServHelp(from *Client) bool {
	help := []string{
		"*** NickServ Help ***",
		"REGISTER <password> <email> - Register your nickname",
		"IDENTIFY <password>          - Identify to your nickname",
		"INFO [nick]                  - Show information about a nickname",
		"DROP                         - Drop your nickname registration",
		"SET                          - Change settings",
		"ACCESS                       - Manage access list",
		"HELP                         - Show this help",
	}

	for _, line := range help {
		sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick, line)
	}

	return true
}

// ChanServ command handlers (placeholder)
func (sm *ServicesManager) handleChanServCommand(from *Client) bool {
	sm.sendServiceNotice(sm.config.ChanServ.Nick, from.nick,
		"ChanServ commands not yet implemented.")
	return true
}

// OperServ command handlers (placeholder)
func (sm *ServicesManager) handleOperServCommand(from *Client) bool {
	sm.sendServiceNotice(sm.config.OperServ.Nick, from.nick,
		"OperServ commands not yet implemented.")
	return true
}

// MemoServ command handlers (placeholder)
func (sm *ServicesManager) handleMemoServCommand(from *Client) bool {
	sm.sendServiceNotice(sm.config.MemoServ.Nick, from.nick,
		"MemoServ commands not yet implemented.")
	return true
}

// sendServiceNotice sends a notice from a service to a user
func (sm *ServicesManager) sendServiceNotice(from, to, message string) {
	if client := sm.server.GetClient(to); client != nil {
		client.SendMessage(fmt.Sprintf(":%s NOTICE %s :%s", from, to, message))
	}
}

// IsServiceNick checks if a nick is a service
func (sm *ServicesManager) IsServiceNick(nick string) bool {
	if !sm.enabled {
		return false
	}

	lowerNick := strings.ToLower(nick)
	return lowerNick == strings.ToLower(sm.config.NickServ.Nick) ||
		lowerNick == strings.ToLower(sm.config.ChanServ.Nick) ||
		lowerNick == strings.ToLower(sm.config.OperServ.Nick) ||
		lowerNick == strings.ToLower(sm.config.MemoServ.Nick)
}

// GetUserAccount returns the account for a nick
func (sm *ServicesManager) GetUserAccount(nick string) *NickAccount {
	if sm.nickServ == nil {
		return nil
	}
	return sm.nickServ.accounts[strings.ToLower(nick)]
}

// IsIdentified checks if a user is identified
func (sm *ServicesManager) IsIdentified(nick string) bool {
	if client := sm.server.GetClient(nick); client != nil {
		return client.account != ""
	}
	return false
}
