package chat

import (
	"context"
	"fmt"

	"zombiezen.com/go/sqlite"
	"zombiezen.com/go/sqlite/sqlitex"
)

// DatabaseChannelManager implements ChannelDatabase interface using sqlitex.Pool
type DatabaseChannelManager struct {
	pool *sqlitex.Pool
}

// NewDatabaseChannelManager creates a new database channel manager using sqlitex.Pool
func NewDatabaseChannelManager(pool *sqlitex.Pool) *DatabaseChannelManager {
	return &DatabaseChannelManager{
		pool: pool,
	}
}

// LoadWorldChannels retrieves all persistent world channels from database
func (dcm *DatabaseChannelManager) LoadWorldChannels(ctx context.Context) ([]ChatChannelData, error) {
	conn, err := dcm.pool.Take(context.Background())
	if err != nil {
		return nil, fmt.Errorf("failed to get connection: %w", err)
	}
	defer dcm.pool.Put(conn)

	query := "SELECT `name`, `password`, `level_restriction`, `classes`, `races` FROM `channels`"

	var channels []ChatChannelData
	
	err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		ResultFunc: func(stmt *sqlite.Stmt) error {
			var channel ChatChannelData

			channel.Name = stmt.ColumnText(0)
			if stmt.ColumnType(1) != sqlite.TypeNull {
				channel.Password = stmt.ColumnText(1)
			}
			channel.LevelRestriction = int32(stmt.ColumnInt64(2))
			channel.ClassRestriction = int32(stmt.ColumnInt64(3))
			channel.RaceRestriction = int32(stmt.ColumnInt64(4))

			channels = append(channels, channel)
			return nil
		},
	})
	
	if err != nil {
		return nil, fmt.Errorf("failed to query channels: %w", err)
	}

	return channels, nil
}

// SaveChannel persists a channel to database (world channels only)
func (dcm *DatabaseChannelManager) SaveChannel(ctx context.Context, channel ChatChannelData) error {
	conn, err := dcm.pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer dcm.pool.Put(conn)

	// Insert or update channel
	query := `
		INSERT OR REPLACE INTO channels 
		(name, password, level_restriction, classes, races) 
		VALUES (?, ?, ?, ?, ?)`

	var password any
	if channel.Password != "" {
		password = channel.Password
	}

	err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{
			channel.Name,
			password,
			channel.LevelRestriction,
			channel.ClassRestriction,
			channel.RaceRestriction,
		},
	})
	if err != nil {
		return fmt.Errorf("failed to save channel %s: %w", channel.Name, err)
	}

	return nil
}

// DeleteChannel removes a channel from database
func (dcm *DatabaseChannelManager) DeleteChannel(ctx context.Context, channelName string) error {
	conn, err := dcm.pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer dcm.pool.Put(conn)

	query := "DELETE FROM channels WHERE name = ?"

	err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{channelName},
	})
	if err != nil {
		return fmt.Errorf("failed to delete channel %s: %w", channelName, err)
	}

	return nil
}

// EnsureChannelsTable creates the channels table if it doesn't exist
func (dcm *DatabaseChannelManager) EnsureChannelsTable(ctx context.Context) error {
	conn, err := dcm.pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer dcm.pool.Put(conn)

	query := `
		CREATE TABLE IF NOT EXISTS channels (
			name TEXT PRIMARY KEY,
			password TEXT,
			level_restriction INTEGER NOT NULL DEFAULT 0,
			classes INTEGER NOT NULL DEFAULT 0,
			races INTEGER NOT NULL DEFAULT 0,
			created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
			updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
		)`

	err = sqlitex.Execute(conn, query, nil)
	if err != nil {
		return fmt.Errorf("failed to create channels table: %w", err)
	}

	return nil
}

// GetChannelCount returns the total number of channels in the database
func (dcm *DatabaseChannelManager) GetChannelCount(ctx context.Context) (int, error) {
	conn, err := dcm.pool.Take(context.Background())
	if err != nil {
		return 0, fmt.Errorf("failed to get connection: %w", err)
	}
	defer dcm.pool.Put(conn)

	query := "SELECT COUNT(*) FROM channels"

	var count int
	err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		ResultFunc: func(stmt *sqlite.Stmt) error {
			count = int(stmt.ColumnInt64(0))
			return nil
		},
	})
	if err != nil {
		return 0, fmt.Errorf("failed to query channel count: %w", err)
	}

	return count, nil
}

// GetChannelByName retrieves a specific channel by name
func (dcm *DatabaseChannelManager) GetChannelByName(ctx context.Context, channelName string) (*ChatChannelData, error) {
	conn, err := dcm.pool.Take(context.Background())
	if err != nil {
		return nil, fmt.Errorf("failed to get connection: %w", err)
	}
	defer dcm.pool.Put(conn)

	query := "SELECT `name`, `password`, `level_restriction`, `classes`, `races` FROM `channels` WHERE `name` = ?"

	var channel *ChatChannelData
	err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{channelName},
		ResultFunc: func(stmt *sqlite.Stmt) error {
			channel = &ChatChannelData{}
			channel.Name = stmt.ColumnText(0)
			if stmt.ColumnType(1) != sqlite.TypeNull {
				channel.Password = stmt.ColumnText(1)
			}
			channel.LevelRestriction = int32(stmt.ColumnInt64(2))
			channel.ClassRestriction = int32(stmt.ColumnInt64(3))
			channel.RaceRestriction = int32(stmt.ColumnInt64(4))
			return nil
		},
	})
	if err != nil {
		return nil, fmt.Errorf("failed to query channel %s: %w", channelName, err)
	}
	if channel == nil {
		return nil, fmt.Errorf("channel %s not found", channelName)
	}

	return channel, nil
}

// ListChannelNames returns a list of all channel names in the database
func (dcm *DatabaseChannelManager) ListChannelNames(ctx context.Context) ([]string, error) {
	conn, err := dcm.pool.Take(context.Background())
	if err != nil {
		return nil, fmt.Errorf("failed to get connection: %w", err)
	}
	defer dcm.pool.Put(conn)

	query := "SELECT name FROM channels ORDER BY name"

	var names []string
	
	err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		ResultFunc: func(stmt *sqlite.Stmt) error {
			name := stmt.ColumnText(0)
			names = append(names, name)
			return nil
		},
	})
	
	if err != nil {
		return nil, fmt.Errorf("failed to query channel names: %w", err)
	}

	return names, nil
}

// UpdateChannelPassword updates just the password for a channel
func (dcm *DatabaseChannelManager) UpdateChannelPassword(ctx context.Context, channelName, password string) error {
	conn, err := dcm.pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer dcm.pool.Put(conn)

	query := "UPDATE channels SET password = ?, updated_at = CURRENT_TIMESTAMP WHERE name = ?"

	var passwordParam any
	if password != "" {
		passwordParam = password
	}

	err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{passwordParam, channelName},
	})
	if err != nil {
		return fmt.Errorf("failed to update password for channel %s: %w", channelName, err)
	}

	return nil
}

// UpdateChannelRestrictions updates the level, race, and class restrictions for a channel
func (dcm *DatabaseChannelManager) UpdateChannelRestrictions(ctx context.Context, channelName string, levelRestriction, classRestriction, raceRestriction int32) error {
	conn, err := dcm.pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer dcm.pool.Put(conn)

	query := "UPDATE channels SET level_restriction = ?, classes = ?, races = ?, updated_at = CURRENT_TIMESTAMP WHERE name = ?"

	err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{levelRestriction, classRestriction, raceRestriction, channelName},
	})
	if err != nil {
		return fmt.Errorf("failed to update restrictions for channel %s: %w", channelName, err)
	}

	return nil
}