package chat import ( "context" "fmt" "eq2emu/internal/database" ) // DatabaseChannelManager implements ChannelDatabase interface using the existing database wrapper type DatabaseChannelManager struct { db *database.DB } // NewDatabaseChannelManager creates a new database channel manager func NewDatabaseChannelManager(db *database.DB) *DatabaseChannelManager { return &DatabaseChannelManager{ db: db, } } // LoadWorldChannels retrieves all persistent world channels from database func (dcm *DatabaseChannelManager) LoadWorldChannels(ctx context.Context) ([]ChatChannelData, error) { query := "SELECT `name`, `password`, `level_restriction`, `classes`, `races` FROM `channels`" rows, err := dcm.db.QueryContext(ctx, query) if err != nil { return nil, fmt.Errorf("failed to query channels: %w", err) } defer rows.Close() var channels []ChatChannelData for rows.Next() { var channel ChatChannelData var password *string err := rows.Scan( &channel.Name, &password, &channel.LevelRestriction, &channel.ClassRestriction, &channel.RaceRestriction, ) if err != nil { return nil, fmt.Errorf("failed to scan channel row: %w", err) } // Handle nullable password field if password != nil { channel.Password = *password } channels = append(channels, channel) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("error iterating channel rows: %w", err) } return channels, nil } // SaveChannel persists a channel to database (world channels only) func (dcm *DatabaseChannelManager) SaveChannel(ctx context.Context, channel ChatChannelData) error { // Insert or update channel query := ` INSERT OR REPLACE INTO channels (name, password, level_restriction, classes, races) VALUES (?, ?, ?, ?, ?)` var password *string if channel.Password != "" { password = &channel.Password } _, err := dcm.db.ExecContext(ctx, query, 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 { query := "DELETE FROM channels WHERE name = ?" result, err := dcm.db.ExecContext(ctx, query, channelName) if err != nil { return fmt.Errorf("failed to delete channel %s: %w", channelName, err) } rowsAffected, err := result.RowsAffected() if err != nil { return fmt.Errorf("failed to check rows affected for channel %s: %w", channelName, err) } if rowsAffected == 0 { return fmt.Errorf("channel %s not found in database", channelName) } return nil } // EnsureChannelsTable creates the channels table if it doesn't exist func (dcm *DatabaseChannelManager) EnsureChannelsTable(ctx context.Context) error { 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 := dcm.db.ExecContext(ctx, query) 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) { query := "SELECT COUNT(*) FROM channels" var count int err := dcm.db.QueryRowContext(ctx, query).Scan(&count) if err != nil { return 0, fmt.Errorf("failed to get 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) { query := "SELECT `name`, `password`, `level_restriction`, `classes`, `races` FROM `channels` WHERE `name` = ?" var channel ChatChannelData var password *string err := dcm.db.QueryRowContext(ctx, query, channelName).Scan( &channel.Name, &password, &channel.LevelRestriction, &channel.ClassRestriction, &channel.RaceRestriction, ) if err != nil { return nil, fmt.Errorf("failed to get channel %s: %w", channelName, err) } // Handle nullable password field if password != nil { channel.Password = *password } return &channel, nil } // ListChannelNames returns a list of all channel names in the database func (dcm *DatabaseChannelManager) ListChannelNames(ctx context.Context) ([]string, error) { query := "SELECT name FROM channels ORDER BY name" rows, err := dcm.db.QueryContext(ctx, query) if err != nil { return nil, fmt.Errorf("failed to query channel names: %w", err) } defer rows.Close() var names []string for rows.Next() { var name string if err := rows.Scan(&name); err != nil { return nil, fmt.Errorf("failed to scan channel name: %w", err) } names = append(names, name) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("error iterating channel name rows: %w", err) } return names, nil } // UpdateChannelPassword updates just the password for a channel func (dcm *DatabaseChannelManager) UpdateChannelPassword(ctx context.Context, channelName, password string) error { query := "UPDATE channels SET password = ?, updated_at = CURRENT_TIMESTAMP WHERE name = ?" var passwordParam *string if password != "" { passwordParam = &password } result, err := dcm.db.ExecContext(ctx, query, passwordParam, channelName) if err != nil { return fmt.Errorf("failed to update password for channel %s: %w", channelName, err) } rowsAffected, err := result.RowsAffected() if err != nil { return fmt.Errorf("failed to check rows affected for channel %s: %w", channelName, err) } if rowsAffected == 0 { return fmt.Errorf("channel %s not found in database", channelName) } 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 { query := "UPDATE channels SET level_restriction = ?, classes = ?, races = ?, updated_at = CURRENT_TIMESTAMP WHERE name = ?" result, err := dcm.db.ExecContext(ctx, query, levelRestriction, classRestriction, raceRestriction, channelName) if err != nil { return fmt.Errorf("failed to update restrictions for channel %s: %w", channelName, err) } rowsAffected, err := result.RowsAffected() if err != nil { return fmt.Errorf("failed to check rows affected for channel %s: %w", channelName, err) } if rowsAffected == 0 { return fmt.Errorf("channel %s not found in database", channelName) } return nil }