package drops import ( "fmt" "dk/internal/database" "zombiezen.com/go/sqlite" ) // Drop represents a drop item in the database type Drop struct { ID int `json:"id"` Name string `json:"name"` Level int `json:"level"` Type int `json:"type"` Att string `json:"att"` db *database.DB } // DropType constants for drop types const ( TypeConsumable = 1 ) // Find retrieves a drop by ID func Find(db *database.DB, id int) (*Drop, error) { drop := &Drop{db: db} query := "SELECT id, name, level, type, att FROM drops WHERE id = ?" err := db.Query(query, func(stmt *sqlite.Stmt) error { drop.ID = stmt.ColumnInt(0) drop.Name = stmt.ColumnText(1) drop.Level = stmt.ColumnInt(2) drop.Type = stmt.ColumnInt(3) drop.Att = stmt.ColumnText(4) return nil }, id) if err != nil { return nil, fmt.Errorf("failed to find drop: %w", err) } if drop.ID == 0 { return nil, fmt.Errorf("drop with ID %d not found", id) } return drop, nil } // All retrieves all drops func All(db *database.DB) ([]*Drop, error) { var drops []*Drop query := "SELECT id, name, level, type, att FROM drops ORDER BY id" err := db.Query(query, func(stmt *sqlite.Stmt) error { drop := &Drop{ ID: stmt.ColumnInt(0), Name: stmt.ColumnText(1), Level: stmt.ColumnInt(2), Type: stmt.ColumnInt(3), Att: stmt.ColumnText(4), db: db, } drops = append(drops, drop) return nil }) if err != nil { return nil, fmt.Errorf("failed to retrieve all drops: %w", err) } return drops, nil } // ByLevel retrieves drops by minimum level requirement func ByLevel(db *database.DB, minLevel int) ([]*Drop, error) { var drops []*Drop query := "SELECT id, name, level, type, att FROM drops WHERE level <= ? ORDER BY level, id" err := db.Query(query, func(stmt *sqlite.Stmt) error { drop := &Drop{ ID: stmt.ColumnInt(0), Name: stmt.ColumnText(1), Level: stmt.ColumnInt(2), Type: stmt.ColumnInt(3), Att: stmt.ColumnText(4), db: db, } drops = append(drops, drop) return nil }, minLevel) if err != nil { return nil, fmt.Errorf("failed to retrieve drops by level: %w", err) } return drops, nil } // ByType retrieves drops by type func ByType(db *database.DB, dropType int) ([]*Drop, error) { var drops []*Drop query := "SELECT id, name, level, type, att FROM drops WHERE type = ? ORDER BY level, id" err := db.Query(query, func(stmt *sqlite.Stmt) error { drop := &Drop{ ID: stmt.ColumnInt(0), Name: stmt.ColumnText(1), Level: stmt.ColumnInt(2), Type: stmt.ColumnInt(3), Att: stmt.ColumnText(4), db: db, } drops = append(drops, drop) return nil }, dropType) if err != nil { return nil, fmt.Errorf("failed to retrieve drops by type: %w", err) } return drops, nil } // Builder provides a fluent interface for creating drops type Builder struct { drop *Drop db *database.DB } // NewBuilder creates a new drop builder func NewBuilder(db *database.DB) *Builder { return &Builder{ drop: &Drop{db: db}, db: db, } } // WithName sets the drop name func (b *Builder) WithName(name string) *Builder { b.drop.Name = name return b } // WithLevel sets the drop level requirement func (b *Builder) WithLevel(level int) *Builder { b.drop.Level = level return b } // WithType sets the drop type func (b *Builder) WithType(dropType int) *Builder { b.drop.Type = dropType return b } // WithAtt sets the attributes func (b *Builder) WithAtt(att string) *Builder { b.drop.Att = att return b } // Create saves the drop to the database and returns it func (b *Builder) Create() (*Drop, error) { // Use a transaction to ensure we can get the ID var drop *Drop err := b.db.Transaction(func(tx *database.Tx) error { query := `INSERT INTO drops (name, level, type, att) VALUES (?, ?, ?, ?)` if err := tx.Exec(query, b.drop.Name, b.drop.Level, b.drop.Type, b.drop.Att); err != nil { return fmt.Errorf("failed to insert drop: %w", err) } // Get the last inserted ID within the same transaction var lastID int err := tx.Query("SELECT last_insert_rowid()", func(stmt *sqlite.Stmt) error { lastID = stmt.ColumnInt(0) return nil }) if err != nil { return fmt.Errorf("failed to get last insert ID: %w", err) } // Create the drop with the ID drop = &Drop{ ID: lastID, Name: b.drop.Name, Level: b.drop.Level, Type: b.drop.Type, Att: b.drop.Att, db: b.db, } return nil }) if err != nil { return nil, fmt.Errorf("failed to create drop: %w", err) } return drop, nil } // Save updates an existing drop in the database func (d *Drop) Save() error { if d.ID == 0 { return fmt.Errorf("cannot save drop without ID") } query := `UPDATE drops SET name = ?, level = ?, type = ?, att = ? WHERE id = ?` return d.db.Exec(query, d.Name, d.Level, d.Type, d.Att, d.ID) } // Delete removes the drop from the database func (d *Drop) Delete() error { if d.ID == 0 { return fmt.Errorf("cannot delete drop without ID") } query := "DELETE FROM drops WHERE id = ?" return d.db.Exec(query, d.ID) } // IsConsumable returns true if the drop is a consumable item func (d *Drop) IsConsumable() bool { return d.Type == TypeConsumable } // TypeName returns the string representation of the drop type func (d *Drop) TypeName() string { switch d.Type { case TypeConsumable: return "Consumable" default: return "Unknown" } }