package guilds

import (
	"context"
	"fmt"
	"time"

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

// DatabaseGuildManager implements GuildDatabase interface using zombiezen/go-sqlite
type DatabaseGuildManager struct {
	pool *sqlitex.Pool
}

// NewDatabaseGuildManager creates a new database guild manager
func NewDatabaseGuildManager(pool *sqlitex.Pool) *DatabaseGuildManager {
	return &DatabaseGuildManager{
		pool: pool,
	}
}

// LoadGuilds retrieves all guilds from database
func (dgm *DatabaseGuildManager) LoadGuilds(ctx context.Context) ([]GuildData, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep("SELECT `id`, `name`, `motd`, `level`, `xp`, `xp_needed`, `formed_on` FROM `guilds`")
	defer stmt.Finalize()

	var guilds []GuildData
	for {
		hasRow, err := stmt.Step()
		if err != nil {
			return nil, fmt.Errorf("failed to query guilds: %w", err)
		}
		if !hasRow {
			break
		}

		var guild GuildData
		guild.ID = int32(stmt.ColumnInt64(0))
		guild.Name = stmt.ColumnText(1)
		if stmt.ColumnType(2) != sqlite.TypeNull {
			guild.MOTD = stmt.ColumnText(2)
		}
		guild.Level = int8(stmt.ColumnInt64(3))
		guild.EXPCurrent = stmt.ColumnInt64(4)
		guild.EXPToNextLevel = stmt.ColumnInt64(5)
		guild.FormedDate = time.Unix(stmt.ColumnInt64(6), 0)

		guilds = append(guilds, guild)
	}

	return guilds, nil
}

// LoadGuild retrieves a specific guild from database
func (dgm *DatabaseGuildManager) LoadGuild(ctx context.Context, guildID int32) (*GuildData, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep("SELECT `id`, `name`, `motd`, `level`, `xp`, `xp_needed`, `formed_on` FROM `guilds` WHERE `id` = ?")
	defer stmt.Finalize()
	stmt.BindInt64(1, int64(guildID))

	hasRow, err := stmt.Step()
	if err != nil {
		return nil, fmt.Errorf("failed to load guild %d: %w", guildID, err)
	}
	if !hasRow {
		return nil, fmt.Errorf("guild %d not found", guildID)
	}

	var guild GuildData
	guild.ID = int32(stmt.ColumnInt64(0))
	guild.Name = stmt.ColumnText(1)
	if stmt.ColumnType(2) != sqlite.TypeNull {
		guild.MOTD = stmt.ColumnText(2)
	}
	guild.Level = int8(stmt.ColumnInt64(3))
	guild.EXPCurrent = stmt.ColumnInt64(4)
	guild.EXPToNextLevel = stmt.ColumnInt64(5)
	guild.FormedDate = time.Unix(stmt.ColumnInt64(6), 0)

	return &guild, nil
}

// LoadGuildMembers retrieves all members for a guild
func (dgm *DatabaseGuildManager) LoadGuildMembers(ctx context.Context, guildID int32) ([]GuildMemberData, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep(`SELECT char_id, guild_id, account_id, recruiter_id, name, guild_status, points,
		adventure_class, adventure_level, tradeskill_class, tradeskill_level, rank,
		member_flags, zone, join_date, last_login_date, note, officer_note,
		recruiter_description, recruiter_picture_data, recruiting_show_adventure_class
		FROM guild_members WHERE guild_id = ?`)
	defer stmt.Finalize()
	stmt.BindInt64(1, int64(guildID))

	var members []GuildMemberData
	for {
		hasRow, err := stmt.Step()
		if err != nil {
			return nil, fmt.Errorf("failed to query guild members for guild %d: %w", guildID, err)
		}
		if !hasRow {
			break
		}

		var member GuildMemberData
		member.CharacterID = int32(stmt.ColumnInt64(0))
		member.GuildID = int32(stmt.ColumnInt64(1))
		member.AccountID = int32(stmt.ColumnInt64(2))
		member.RecruiterID = int32(stmt.ColumnInt64(3))
		member.Name = stmt.ColumnText(4)
		member.GuildStatus = int32(stmt.ColumnInt64(5))
		member.Points = stmt.ColumnFloat(6)
		member.AdventureClass = int8(stmt.ColumnInt64(7))
		member.AdventureLevel = int8(stmt.ColumnInt64(8))
		member.TradeskillClass = int8(stmt.ColumnInt64(9))
		member.TradeskillLevel = int8(stmt.ColumnInt64(10))
		member.Rank = int8(stmt.ColumnInt64(11))
		member.MemberFlags = int8(stmt.ColumnInt64(12))
		member.Zone = stmt.ColumnText(13)
		member.JoinDate = time.Unix(stmt.ColumnInt64(14), 0)
		member.LastLoginDate = time.Unix(stmt.ColumnInt64(15), 0)
		if stmt.ColumnType(16) != sqlite.TypeNull {
			member.Note = stmt.ColumnText(16)
		}
		if stmt.ColumnType(17) != sqlite.TypeNull {
			member.OfficerNote = stmt.ColumnText(17)
		}
		if stmt.ColumnType(18) != sqlite.TypeNull {
			member.RecruiterDescription = stmt.ColumnText(18)
		}
		if stmt.ColumnType(19) != sqlite.TypeNull {
			len := stmt.ColumnLen(19)
			if len > 0 {
				member.RecruiterPictureData = make([]byte, len)
				stmt.ColumnBytes(19, member.RecruiterPictureData)
			}
		}
		member.RecruitingShowAdventureClass = int8(stmt.ColumnInt64(20))

		members = append(members, member)
	}

	return members, nil
}

// LoadGuildEvents retrieves events for a guild
func (dgm *DatabaseGuildManager) LoadGuildEvents(ctx context.Context, guildID int32) ([]GuildEventData, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep(`SELECT event_id, guild_id, date, type, description, locked
		FROM guild_events WHERE guild_id = ?
		ORDER BY event_id DESC LIMIT ?`)
	defer stmt.Finalize()
	stmt.BindInt64(1, int64(guildID))
	stmt.BindInt64(2, MaxEvents)

	var events []GuildEventData
	for {
		hasRow, err := stmt.Step()
		if err != nil {
			return nil, fmt.Errorf("failed to query guild events for guild %d: %w", guildID, err)
		}
		if !hasRow {
			break
		}

		var event GuildEventData
		event.EventID = stmt.ColumnInt64(0)
		event.GuildID = int32(stmt.ColumnInt64(1))
		event.Date = time.Unix(stmt.ColumnInt64(2), 0)
		event.Type = int32(stmt.ColumnInt64(3))
		event.Description = stmt.ColumnText(4)
		event.Locked = int8(stmt.ColumnInt64(5))

		events = append(events, event)
	}

	return events, nil
}

// LoadGuildRanks retrieves custom rank names for a guild
func (dgm *DatabaseGuildManager) LoadGuildRanks(ctx context.Context, guildID int32) ([]GuildRankData, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep("SELECT guild_id, rank, name FROM guild_ranks WHERE guild_id = ?")
	defer stmt.Finalize()
	stmt.BindInt64(1, int64(guildID))

	var ranks []GuildRankData
	for {
		hasRow, err := stmt.Step()
		if err != nil {
			return nil, fmt.Errorf("failed to query guild ranks for guild %d: %w", guildID, err)
		}
		if !hasRow {
			break
		}

		var rank GuildRankData
		rank.GuildID = int32(stmt.ColumnInt64(0))
		rank.Rank = int8(stmt.ColumnInt64(1))
		rank.Name = stmt.ColumnText(2)

		ranks = append(ranks, rank)
	}

	return ranks, nil
}

// LoadGuildPermissions retrieves permissions for a guild
func (dgm *DatabaseGuildManager) LoadGuildPermissions(ctx context.Context, guildID int32) ([]GuildPermissionData, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep("SELECT guild_id, rank, permission, value FROM guild_permissions WHERE guild_id = ?")
	defer stmt.Finalize()
	stmt.BindInt64(1, int64(guildID))

	var permissions []GuildPermissionData
	for {
		hasRow, err := stmt.Step()
		if err != nil {
			return nil, fmt.Errorf("failed to query guild permissions for guild %d: %w", guildID, err)
		}
		if !hasRow {
			break
		}

		var permission GuildPermissionData
		permission.GuildID = int32(stmt.ColumnInt64(0))
		permission.Rank = int8(stmt.ColumnInt64(1))
		permission.Permission = int8(stmt.ColumnInt64(2))
		permission.Value = int8(stmt.ColumnInt64(3))

		permissions = append(permissions, permission)
	}

	return permissions, nil
}

// LoadGuildEventFilters retrieves event filters for a guild
func (dgm *DatabaseGuildManager) LoadGuildEventFilters(ctx context.Context, guildID int32) ([]GuildEventFilterData, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep("SELECT guild_id, event_id, category, value FROM guild_event_filters WHERE guild_id = ?")
	defer stmt.Finalize()
	stmt.BindInt64(1, int64(guildID))

	var filters []GuildEventFilterData
	for {
		hasRow, err := stmt.Step()
		if err != nil {
			return nil, fmt.Errorf("failed to query guild event filters for guild %d: %w", guildID, err)
		}
		if !hasRow {
			break
		}

		var filter GuildEventFilterData
		filter.GuildID = int32(stmt.ColumnInt64(0))
		filter.EventID = int8(stmt.ColumnInt64(1))
		filter.Category = int8(stmt.ColumnInt64(2))
		filter.Value = int8(stmt.ColumnInt64(3))

		filters = append(filters, filter)
	}

	return filters, nil
}

// LoadGuildRecruiting retrieves recruiting settings for a guild
func (dgm *DatabaseGuildManager) LoadGuildRecruiting(ctx context.Context, guildID int32) ([]GuildRecruitingData, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep("SELECT guild_id, flag, value FROM guild_recruiting WHERE guild_id = ?")
	defer stmt.Finalize()
	stmt.BindInt64(1, int64(guildID))

	var recruiting []GuildRecruitingData
	for {
		hasRow, err := stmt.Step()
		if err != nil {
			return nil, fmt.Errorf("failed to query guild recruiting for guild %d: %w", guildID, err)
		}
		if !hasRow {
			break
		}

		var recruit GuildRecruitingData
		recruit.GuildID = int32(stmt.ColumnInt64(0))
		recruit.Flag = int8(stmt.ColumnInt64(1))
		recruit.Value = int8(stmt.ColumnInt64(2))

		recruiting = append(recruiting, recruit)
	}

	return recruiting, nil
}

// LoadPointHistory retrieves point history for a member
func (dgm *DatabaseGuildManager) LoadPointHistory(ctx context.Context, characterID int32) ([]PointHistoryData, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep(`SELECT char_id, date, modified_by, comment, points
		FROM guild_point_history WHERE char_id = ?
		ORDER BY date DESC LIMIT ?`)
	defer stmt.Finalize()
	stmt.BindInt64(1, int64(characterID))
	stmt.BindInt64(2, MaxPointHistory)

	var history []PointHistoryData
	for {
		hasRow, err := stmt.Step()
		if err != nil {
			return nil, fmt.Errorf("failed to query point history for character %d: %w", characterID, err)
		}
		if !hasRow {
			break
		}

		var entry PointHistoryData
		entry.CharacterID = int32(stmt.ColumnInt64(0))
		entry.Date = time.Unix(stmt.ColumnInt64(1), 0)
		entry.ModifiedBy = stmt.ColumnText(2)
		entry.Comment = stmt.ColumnText(3)
		entry.Points = stmt.ColumnFloat(4)

		history = append(history, entry)
	}

	return history, nil
}

// SaveGuild saves guild basic data
func (dgm *DatabaseGuildManager) SaveGuild(ctx context.Context, guild *Guild) error {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep(`INSERT OR REPLACE INTO guilds
		(id, name, motd, level, xp, xp_needed, formed_on)
		VALUES (?, ?, ?, ?, ?, ?, ?)`)
	defer stmt.Finalize()

	formedTimestamp := guild.GetFormedDate().Unix()

	stmt.BindInt64(1, int64(guild.GetID()))
	stmt.BindText(2, guild.GetName())
	stmt.BindText(3, guild.GetMOTD())
	stmt.BindInt64(4, int64(guild.GetLevel()))
	stmt.BindInt64(5, guild.GetEXPCurrent())
	stmt.BindInt64(6, guild.GetEXPToNextLevel())
	stmt.BindInt64(7, formedTimestamp)

	_, err = stmt.Step()
	if err != nil {
		return fmt.Errorf("failed to save guild %d: %w", guild.GetID(), err)
	}

	return nil
}

// SaveGuildMembers saves all guild members
func (dgm *DatabaseGuildManager) SaveGuildMembers(ctx context.Context, guildID int32, members []*GuildMember) error {
	if len(members) == 0 {
		return nil
	}

	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	endFn, err := sqlitex.ImmediateTransaction(conn)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer endFn(&err)

	// Delete existing members for this guild
	delStmt := conn.Prep("DELETE FROM guild_members WHERE guild_id = ?")
	delStmt.BindInt64(1, int64(guildID))
	_, err = delStmt.Step()
	delStmt.Finalize()
	if err != nil {
		return fmt.Errorf("failed to delete existing guild members: %w", err)
	}

	// Insert all members
	insertStmt := conn.Prep(`INSERT INTO guild_members
		(char_id, guild_id, account_id, recruiter_id, name, guild_status, points,
		 adventure_class, adventure_level, tradeskill_class, tradeskill_level, rank,
		 member_flags, zone, join_date, last_login_date, note, officer_note,
		 recruiter_description, recruiter_picture_data, recruiting_show_adventure_class)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
	defer insertStmt.Finalize()

	for _, member := range members {
		joinTimestamp := member.GetJoinDate().Unix()
		lastLoginTimestamp := member.GetLastLoginDate().Unix()

		insertStmt.Reset()
		insertStmt.BindInt64(1, int64(member.GetCharacterID()))
		insertStmt.BindInt64(2, int64(guildID))
		insertStmt.BindInt64(3, int64(member.AccountID))
		insertStmt.BindInt64(4, int64(member.GetRecruiterID()))
		insertStmt.BindText(5, member.GetName())
		insertStmt.BindInt64(6, int64(member.GuildStatus))
		insertStmt.BindFloat(7, member.GetPoints())
		insertStmt.BindInt64(8, int64(member.GetAdventureClass()))
		insertStmt.BindInt64(9, int64(member.GetAdventureLevel()))
		insertStmt.BindInt64(10, int64(member.GetTradeskillClass()))
		insertStmt.BindInt64(11, int64(member.GetTradeskillLevel()))
		insertStmt.BindInt64(12, int64(member.GetRank()))
		insertStmt.BindInt64(13, int64(member.GetMemberFlags()))
		insertStmt.BindText(14, member.GetZone())
		insertStmt.BindInt64(15, joinTimestamp)
		insertStmt.BindInt64(16, lastLoginTimestamp)
		insertStmt.BindText(17, member.GetNote())
		insertStmt.BindText(18, member.GetOfficerNote())
		insertStmt.BindText(19, member.GetRecruiterDescription())
		if pictureData := member.GetRecruiterPictureData(); pictureData != nil {
			insertStmt.BindBytes(20, pictureData)
		} else {
			insertStmt.BindNull(20)
		}
		insertStmt.BindInt64(21, int64(member.RecruitingShowAdventureClass))

		_, err = insertStmt.Step()
		if err != nil {
			return fmt.Errorf("failed to insert guild member %d: %w", member.GetCharacterID(), err)
		}
	}

	return nil
}

// SaveGuildEvents saves guild events
func (dgm *DatabaseGuildManager) SaveGuildEvents(ctx context.Context, guildID int32, events []GuildEvent) error {
	if len(events) == 0 {
		return nil
	}

	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep(`INSERT OR REPLACE INTO guild_events
		(event_id, guild_id, date, type, description, locked)
		VALUES (?, ?, ?, ?, ?, ?)`)
	defer stmt.Finalize()

	for _, event := range events {
		if !event.SaveNeeded {
			continue
		}

		dateTimestamp := event.Date.Unix()

		stmt.Reset()
		stmt.BindInt64(1, event.EventID)
		stmt.BindInt64(2, int64(guildID))
		stmt.BindInt64(3, dateTimestamp)
		stmt.BindInt64(4, int64(event.Type))
		stmt.BindText(5, event.Description)
		stmt.BindInt64(6, int64(event.Locked))

		_, err = stmt.Step()
		if err != nil {
			return fmt.Errorf("failed to save guild event %d: %w", event.EventID, err)
		}
	}

	return nil
}

// SaveGuildRanks saves guild rank names
func (dgm *DatabaseGuildManager) SaveGuildRanks(ctx context.Context, guildID int32, ranks map[int8]string) error {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	endFn, err := sqlitex.ImmediateTransaction(conn)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer endFn(&err)

	// Delete existing ranks for this guild
	delStmt := conn.Prep("DELETE FROM guild_ranks WHERE guild_id = ?")
	delStmt.BindInt64(1, int64(guildID))
	_, err = delStmt.Step()
	delStmt.Finalize()
	if err != nil {
		return fmt.Errorf("failed to delete existing guild ranks: %w", err)
	}

	// Insert all ranks
	insertStmt := conn.Prep("INSERT INTO guild_ranks (guild_id, rank, name) VALUES (?, ?, ?)")
	defer insertStmt.Finalize()

	for rank, name := range ranks {
		// Only save non-default rank names
		if defaultName, exists := DefaultRankNames[rank]; !exists || name != defaultName {
			insertStmt.Reset()
			insertStmt.BindInt64(1, int64(guildID))
			insertStmt.BindInt64(2, int64(rank))
			insertStmt.BindText(3, name)

			_, err = insertStmt.Step()
			if err != nil {
				return fmt.Errorf("failed to insert guild rank %d: %w", rank, err)
			}
		}
	}

	return nil
}

// SaveGuildPermissions saves guild permissions
func (dgm *DatabaseGuildManager) SaveGuildPermissions(ctx context.Context, guildID int32, permissions map[int8]map[int8]int8) error {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	endFn, err := sqlitex.ImmediateTransaction(conn)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer endFn(&err)

	// Delete existing permissions for this guild
	delStmt := conn.Prep("DELETE FROM guild_permissions WHERE guild_id = ?")
	delStmt.BindInt64(1, int64(guildID))
	_, err = delStmt.Step()
	delStmt.Finalize()
	if err != nil {
		return fmt.Errorf("failed to delete existing guild permissions: %w", err)
	}

	// Insert all permissions
	insertStmt := conn.Prep("INSERT INTO guild_permissions (guild_id, rank, permission, value) VALUES (?, ?, ?, ?)")
	defer insertStmt.Finalize()

	for rank, rankPermissions := range permissions {
		for permission, value := range rankPermissions {
			insertStmt.Reset()
			insertStmt.BindInt64(1, int64(guildID))
			insertStmt.BindInt64(2, int64(rank))
			insertStmt.BindInt64(3, int64(permission))
			insertStmt.BindInt64(4, int64(value))

			_, err = insertStmt.Step()
			if err != nil {
				return fmt.Errorf("failed to insert guild permission %d/%d: %w", rank, permission, err)
			}
		}
	}

	return nil
}

// SaveGuildEventFilters saves guild event filters
func (dgm *DatabaseGuildManager) SaveGuildEventFilters(ctx context.Context, guildID int32, filters map[int8]map[int8]int8) error {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	endFn, err := sqlitex.ImmediateTransaction(conn)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer endFn(&err)

	// Delete existing filters for this guild
	delStmt := conn.Prep("DELETE FROM guild_event_filters WHERE guild_id = ?")
	delStmt.BindInt64(1, int64(guildID))
	_, err = delStmt.Step()
	delStmt.Finalize()
	if err != nil {
		return fmt.Errorf("failed to delete existing guild event filters: %w", err)
	}

	// Insert all filters
	insertStmt := conn.Prep("INSERT INTO guild_event_filters (guild_id, event_id, category, value) VALUES (?, ?, ?, ?)")
	defer insertStmt.Finalize()

	for eventID, eventFilters := range filters {
		for category, value := range eventFilters {
			insertStmt.Reset()
			insertStmt.BindInt64(1, int64(guildID))
			insertStmt.BindInt64(2, int64(eventID))
			insertStmt.BindInt64(3, int64(category))
			insertStmt.BindInt64(4, int64(value))

			_, err = insertStmt.Step()
			if err != nil {
				return fmt.Errorf("failed to insert guild event filter %d/%d: %w", eventID, category, err)
			}
		}
	}

	return nil
}

// SaveGuildRecruiting saves guild recruiting settings
func (dgm *DatabaseGuildManager) SaveGuildRecruiting(ctx context.Context, guildID int32, flags, descTags map[int8]int8) error {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	endFn, err := sqlitex.ImmediateTransaction(conn)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer endFn(&err)

	// Delete existing recruiting settings for this guild
	delStmt := conn.Prep("DELETE FROM guild_recruiting WHERE guild_id = ?")
	delStmt.BindInt64(1, int64(guildID))
	_, err = delStmt.Step()
	delStmt.Finalize()
	if err != nil {
		return fmt.Errorf("failed to delete existing guild recruiting: %w", err)
	}

	// Insert recruiting flags and description tags
	insertStmt := conn.Prep("INSERT INTO guild_recruiting (guild_id, flag, value) VALUES (?, ?, ?)")
	defer insertStmt.Finalize()

	for flag, value := range flags {
		insertStmt.Reset()
		insertStmt.BindInt64(1, int64(guildID))
		insertStmt.BindInt64(2, int64(flag))
		insertStmt.BindInt64(3, int64(value))

		_, err = insertStmt.Step()
		if err != nil {
			return fmt.Errorf("failed to insert guild recruiting flag %d: %w", flag, err)
		}
	}

	// Insert description tags (with negative flag values to distinguish)
	for tag, value := range descTags {
		insertStmt.Reset()
		insertStmt.BindInt64(1, int64(guildID))
		insertStmt.BindInt64(2, int64(-tag-1)) // Negative to distinguish from flags
		insertStmt.BindInt64(3, int64(value))

		_, err = insertStmt.Step()
		if err != nil {
			return fmt.Errorf("failed to insert guild recruiting desc tag %d: %w", tag, err)
		}
	}

	return nil
}

// SavePointHistory saves point history for a member
func (dgm *DatabaseGuildManager) SavePointHistory(ctx context.Context, characterID int32, history []PointHistory) error {
	if len(history) == 0 {
		return nil
	}

	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	endFn, err := sqlitex.ImmediateTransaction(conn)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer endFn(&err)

	// Delete existing history for this character
	delStmt := conn.Prep("DELETE FROM guild_point_history WHERE char_id = ?")
	delStmt.BindInt64(1, int64(characterID))
	_, err = delStmt.Step()
	delStmt.Finalize()
	if err != nil {
		return fmt.Errorf("failed to delete existing point history: %w", err)
	}

	// Insert all history entries
	insertStmt := conn.Prep("INSERT INTO guild_point_history (char_id, date, modified_by, comment, points) VALUES (?, ?, ?, ?, ?)")
	defer insertStmt.Finalize()

	for _, entry := range history {
		dateTimestamp := entry.Date.Unix()

		insertStmt.Reset()
		insertStmt.BindInt64(1, int64(characterID))
		insertStmt.BindInt64(2, dateTimestamp)
		insertStmt.BindText(3, entry.ModifiedBy)
		insertStmt.BindText(4, entry.Comment)
		insertStmt.BindFloat(5, entry.Points)

		_, err = insertStmt.Step()
		if err != nil {
			return fmt.Errorf("failed to insert point history entry: %w", err)
		}
	}

	return nil
}

// GetGuildIDByCharacterID returns guild ID for a character
func (dgm *DatabaseGuildManager) GetGuildIDByCharacterID(ctx context.Context, characterID int32) (int32, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return 0, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep("SELECT guild_id FROM guild_members WHERE char_id = ?")
	defer stmt.Finalize()
	stmt.BindInt64(1, int64(characterID))

	hasRow, err := stmt.Step()
	if err != nil {
		return 0, fmt.Errorf("failed to get guild ID for character %d: %w", characterID, err)
	}
	if !hasRow {
		return 0, fmt.Errorf("character %d not found in any guild", characterID)
	}

	guildID := int32(stmt.ColumnInt64(0))
	return guildID, nil
}

// CreateGuild creates a new guild
func (dgm *DatabaseGuildManager) CreateGuild(ctx context.Context, guildData GuildData) (int32, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return 0, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep(`INSERT INTO guilds (name, motd, level, xp, xp_needed, formed_on)
		VALUES (?, ?, ?, ?, ?, ?)`)
	defer stmt.Finalize()

	formedTimestamp := guildData.FormedDate.Unix()

	stmt.BindText(1, guildData.Name)
	stmt.BindText(2, guildData.MOTD)
	stmt.BindInt64(3, int64(guildData.Level))
	stmt.BindInt64(4, guildData.EXPCurrent)
	stmt.BindInt64(5, guildData.EXPToNextLevel)
	stmt.BindInt64(6, formedTimestamp)

	_, err = stmt.Step()
	if err != nil {
		return 0, fmt.Errorf("failed to create guild: %w", err)
	}

	id := conn.LastInsertRowID()
	return int32(id), nil
}

// DeleteGuild removes a guild and all related data
func (dgm *DatabaseGuildManager) DeleteGuild(ctx context.Context, guildID int32) error {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	endFn, err := sqlitex.ImmediateTransaction(conn)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer endFn(&err)

	// Delete related data first (foreign key constraints)
	tables := []string{
		"guild_point_history",
		"guild_members",
		"guild_events",
		"guild_ranks",
		"guild_permissions",
		"guild_event_filters",
		"guild_recruiting",
	}

	for _, table := range tables {
		var query string
		if table == "guild_point_history" {
			// Special case: delete point history for all members of this guild
			query = "DELETE FROM guild_point_history WHERE char_id IN (SELECT char_id FROM guild_members WHERE guild_id = ?)"
		} else {
			query = fmt.Sprintf("DELETE FROM %s WHERE guild_id = ?", table)
		}

		stmt := conn.Prep(query)
		stmt.BindInt64(1, int64(guildID))
		_, err = stmt.Step()
		stmt.Finalize()
		if err != nil {
			return fmt.Errorf("failed to delete from %s: %w", table, err)
		}
	}

	// Finally delete the guild itself
	delStmt := conn.Prep("DELETE FROM guilds WHERE id = ?")
	delStmt.BindInt64(1, int64(guildID))
	_, err = delStmt.Step()
	delStmt.Finalize()
	if err != nil {
		return fmt.Errorf("failed to delete guild: %w", err)
	}

	return nil
}

// GetNextGuildID returns the next available guild ID
func (dgm *DatabaseGuildManager) GetNextGuildID(ctx context.Context) (int32, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return 0, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep("SELECT COALESCE(MAX(id), 0) + 1 FROM guilds")
	defer stmt.Finalize()

	hasRow, err := stmt.Step()
	if err != nil {
		return 0, fmt.Errorf("failed to get next guild ID: %w", err)
	}
	if !hasRow {
		return 1, nil // If no guilds exist, start with ID 1
	}

	nextID := int32(stmt.ColumnInt64(0))
	return nextID, nil
}

// GetNextEventID returns the next available event ID for a guild
func (dgm *DatabaseGuildManager) GetNextEventID(ctx context.Context, guildID int32) (int64, error) {
	conn, err := dgm.pool.Take(ctx)
	if err != nil {
		return 0, fmt.Errorf("failed to get database connection: %w", err)
	}
	defer dgm.pool.Put(conn)

	stmt := conn.Prep("SELECT COALESCE(MAX(event_id), 0) + 1 FROM guild_events WHERE guild_id = ?")
	defer stmt.Finalize()
	stmt.BindInt64(1, int64(guildID))

	hasRow, err := stmt.Step()
	if err != nil {
		return 0, fmt.Errorf("failed to get next event ID for guild %d: %w", guildID, err)
	}
	if !hasRow {
		return 1, nil // If no events exist for this guild, start with ID 1
	}

	nextID := stmt.ColumnInt64(0)
	return nextID, nil
}
