package factions import ( "fmt" "time" "eq2emu/internal/database" ) // DatabaseAdapter implements the factions.Database interface using our database wrapper type DatabaseAdapter struct { db *database.DB } // NewDatabaseAdapter creates a new database adapter for factions func NewDatabaseAdapter(db *database.DB) *DatabaseAdapter { return &DatabaseAdapter{db: db} } // LoadAllFactions loads all factions from the database func (da *DatabaseAdapter) LoadAllFactions() ([]*Faction, error) { // Create factions table if it doesn't exist if err := da.db.Exec(` CREATE TABLE IF NOT EXISTS factions ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, type TEXT, description TEXT, negative_change INTEGER DEFAULT 0, positive_change INTEGER DEFAULT 0, default_value INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); err != nil { return nil, fmt.Errorf("failed to create factions table: %w", err) } var factions []*Faction err := da.db.Query("SELECT id, name, type, description, negative_change, positive_change, default_value FROM factions", func(row *database.Row) error { faction := &Faction{ ID: int32(row.Int64(0)), Name: row.Text(1), Type: row.Text(2), Description: row.Text(3), NegativeChange: int16(row.Int64(4)), PositiveChange: int16(row.Int64(5)), DefaultValue: int32(row.Int64(6)), } factions = append(factions, faction) return nil }) if err != nil { return nil, fmt.Errorf("failed to load factions: %w", err) } return factions, nil } // SaveFaction saves a faction to the database func (da *DatabaseAdapter) SaveFaction(faction *Faction) error { if faction == nil { return fmt.Errorf("faction is nil") } // Use INSERT OR REPLACE to handle both insert and update err := da.db.Exec(` INSERT OR REPLACE INTO factions (id, name, type, description, negative_change, positive_change, default_value, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `, faction.ID, faction.Name, faction.Type, faction.Description, faction.NegativeChange, faction.PositiveChange, faction.DefaultValue, time.Now().Unix()) if err != nil { return fmt.Errorf("failed to save faction %d: %w", faction.ID, err) } return nil } // DeleteFaction deletes a faction from the database func (da *DatabaseAdapter) DeleteFaction(factionID int32) error { err := da.db.Exec("DELETE FROM factions WHERE id = ?", factionID) if err != nil { return fmt.Errorf("failed to delete faction %d: %w", factionID, err) } return nil } // LoadHostileFactionRelations loads all hostile faction relations func (da *DatabaseAdapter) LoadHostileFactionRelations() ([]*FactionRelation, error) { // Create faction_relations table if it doesn't exist if err := da.db.Exec(` CREATE TABLE IF NOT EXISTS faction_relations ( faction_id INTEGER NOT NULL, related_faction_id INTEGER NOT NULL, is_hostile INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (faction_id, related_faction_id), FOREIGN KEY (faction_id) REFERENCES factions(id), FOREIGN KEY (related_faction_id) REFERENCES factions(id) ) `); err != nil { return nil, fmt.Errorf("failed to create faction_relations table: %w", err) } var relations []*FactionRelation err := da.db.Query("SELECT faction_id, related_faction_id FROM faction_relations WHERE is_hostile = 1", func(row *database.Row) error { relation := &FactionRelation{ FactionID: int32(row.Int64(0)), HostileFactionID: int32(row.Int64(1)), } relations = append(relations, relation) return nil }) if err != nil { return nil, fmt.Errorf("failed to load hostile faction relations: %w", err) } return relations, nil } // LoadFriendlyFactionRelations loads all friendly faction relations func (da *DatabaseAdapter) LoadFriendlyFactionRelations() ([]*FactionRelation, error) { var relations []*FactionRelation err := da.db.Query("SELECT faction_id, related_faction_id FROM faction_relations WHERE is_hostile = 0", func(row *database.Row) error { relation := &FactionRelation{ FactionID: int32(row.Int64(0)), FriendlyFactionID: int32(row.Int64(1)), } relations = append(relations, relation) return nil }) if err != nil { return nil, fmt.Errorf("failed to load friendly faction relations: %w", err) } return relations, nil } // SaveFactionRelation saves a faction relation to the database func (da *DatabaseAdapter) SaveFactionRelation(relation *FactionRelation) error { if relation == nil { return fmt.Errorf("faction relation is nil") } var relatedFactionID int32 var isHostile int if relation.HostileFactionID != 0 { relatedFactionID = relation.HostileFactionID isHostile = 1 } else if relation.FriendlyFactionID != 0 { relatedFactionID = relation.FriendlyFactionID isHostile = 0 } else { return fmt.Errorf("faction relation has no related faction ID") } err := da.db.Exec(` INSERT OR REPLACE INTO faction_relations (faction_id, related_faction_id, is_hostile) VALUES (?, ?, ?) `, relation.FactionID, relatedFactionID, isHostile) if err != nil { return fmt.Errorf("failed to save faction relation %d -> %d: %w", relation.FactionID, relatedFactionID, err) } return nil } // DeleteFactionRelation deletes a faction relation from the database func (da *DatabaseAdapter) DeleteFactionRelation(factionID, relatedFactionID int32, isHostile bool) error { hostileFlag := 0 if isHostile { hostileFlag = 1 } err := da.db.Exec("DELETE FROM faction_relations WHERE faction_id = ? AND related_faction_id = ? AND is_hostile = ?", factionID, relatedFactionID, hostileFlag) if err != nil { return fmt.Errorf("failed to delete faction relation %d -> %d: %w", factionID, relatedFactionID, err) } return nil } // LoadPlayerFactions loads player faction values from the database func (da *DatabaseAdapter) LoadPlayerFactions(playerID int32) (map[int32]int32, error) { // Create player_factions table if it doesn't exist if err := da.db.Exec(` CREATE TABLE IF NOT EXISTS player_factions ( player_id INTEGER NOT NULL, faction_id INTEGER NOT NULL, faction_value INTEGER NOT NULL DEFAULT 0, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (player_id, faction_id), FOREIGN KEY (faction_id) REFERENCES factions(id) ) `); err != nil { return nil, fmt.Errorf("failed to create player_factions table: %w", err) } factionValues := make(map[int32]int32) err := da.db.Query("SELECT faction_id, faction_value FROM player_factions WHERE player_id = ?", func(row *database.Row) error { factionID := int32(row.Int64(0)) factionValue := int32(row.Int64(1)) factionValues[factionID] = factionValue return nil }, playerID) if err != nil { return nil, fmt.Errorf("failed to load player factions for player %d: %w", playerID, err) } return factionValues, nil } // SavePlayerFaction saves a player's faction value to the database func (da *DatabaseAdapter) SavePlayerFaction(playerID, factionID, factionValue int32) error { err := da.db.Exec(` INSERT OR REPLACE INTO player_factions (player_id, faction_id, faction_value, updated_at) VALUES (?, ?, ?, ?) `, playerID, factionID, factionValue, time.Now().Unix()) if err != nil { return fmt.Errorf("failed to save player faction %d/%d: %w", playerID, factionID, err) } return nil } // SaveAllPlayerFactions saves all faction values for a player func (da *DatabaseAdapter) SaveAllPlayerFactions(playerID int32, factionValues map[int32]int32) error { return da.db.Transaction(func(txDB *database.DB) error { // Clear existing faction values for this player if err := txDB.Exec("DELETE FROM player_factions WHERE player_id = ?", playerID); err != nil { return fmt.Errorf("failed to clear player factions: %w", err) } // Insert all current faction values for factionID, factionValue := range factionValues { err := txDB.Exec(` INSERT INTO player_factions (player_id, faction_id, faction_value, updated_at) VALUES (?, ?, ?, ?) `, playerID, factionID, factionValue, time.Now().Unix()) if err != nil { return fmt.Errorf("failed to insert player faction %d/%d: %w", playerID, factionID, err) } } return nil }) }