package database import ( "database/sql" "fmt" "time" _ "github.com/mattn/go-sqlite3" ) // GetExternalRelease retrieves an external_release row by RGID. // Returns sql.ErrNoRows if the release is not found. func GetExternalRelease(db *DB, rgid string) (*ExternalRelease, error) { var r ExternalRelease var cachedAt sql.NullTime err := db.Conn().QueryRow( "SELECT rgid, artist_id, title, type, release_date, is_ignored, cached_at FROM external_releases WHERE rgid = ?", rgid, ).Scan(&r.RGID, &r.ArtistID, &r.Title, &r.Type, &r.ReleaseDate, &r.IsIgnored, &cachedAt) if err != nil { return nil, err } if cachedAt.Valid { r.CachedAt = cachedAt.Time } return &r, nil } // SaveExternalRelease inserts or replaces an external_release row. func SaveExternalRelease(db *DB, release *ExternalRelease) error { cachedAtStr := "" if !release.CachedAt.IsZero() { cachedAtStr = release.CachedAt.Format("2006-01-02 15:04:05") } _, err := db.Conn().Exec( "INSERT OR REPLACE INTO external_releases (rgid, artist_id, title, type, release_date, is_ignored, cached_at) VALUES (?, ?, ?, ?, ?, ?, ?)", release.RGID, release.ArtistID, release.Title, release.Type, release.ReleaseDate, release.IsIgnored, cachedAtStr, ) if err != nil { return fmt.Errorf("save external release: %w", err) } return nil } // GetExternalReleasesByArtist returns all external_release rows for a given artist_id. func GetExternalReleasesByArtist(db *DB, artistID string) ([]ExternalRelease, error) { rows, err := db.Conn().Query( "SELECT rgid, artist_id, title, type, release_date, is_ignored, cached_at FROM external_releases WHERE artist_id = ?", artistID, ) if err != nil { return nil, fmt.Errorf("query external releases by artist: %w", err) } defer rows.Close() var results []ExternalRelease for rows.Next() { var r ExternalRelease var cachedAt sql.NullTime if err := rows.Scan(&r.RGID, &r.ArtistID, &r.Title, &r.Type, &r.ReleaseDate, &r.IsIgnored, &cachedAt); err != nil { return nil, fmt.Errorf("scan external release: %w", err) } if cachedAt.Valid { r.CachedAt = cachedAt.Time } results = append(results, r) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate external releases: %w", err) } return results, nil } // GetIgnoredReleases returns all external_release rows where is_ignored = 1. func GetIgnoredReleases(db *DB) ([]ExternalRelease, error) { rows, err := db.Conn().Query( "SELECT rgid, artist_id, title, type, release_date, is_ignored, cached_at FROM external_releases WHERE is_ignored = 1", ) if err != nil { return nil, fmt.Errorf("query ignored releases: %w", err) } defer rows.Close() var results []ExternalRelease for rows.Next() { var r ExternalRelease var cachedAt sql.NullTime if err := rows.Scan(&r.RGID, &r.ArtistID, &r.Title, &r.Type, &r.ReleaseDate, &r.IsIgnored, &cachedAt); err != nil { return nil, fmt.Errorf("scan ignored release: %w", err) } if cachedAt.Valid { r.CachedAt = cachedAt.Time } results = append(results, r) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate ignored releases: %w", err) } return results, nil } // SetReleaseIgnored updates the is_ignored flag for a given RGID. func SetReleaseIgnored(db *DB, rgid string, ignored bool) error { result, err := db.Conn().Exec( "UPDATE external_releases SET is_ignored = ? WHERE rgid = ?", ignored, rgid, ) if err != nil { return fmt.Errorf("set release ignored: %w", err) } rowsAffected, err := result.RowsAffected() if err != nil { return fmt.Errorf("rows affected: %w", err) } if rowsAffected == 0 { return fmt.Errorf("release not found: %s", rgid) } return nil } // DeleteExternalReleasesByArtist removes all external_release rows for a given artist_id. func DeleteExternalReleasesByArtist(db *DB, artistID string) error { _, err := db.Conn().Exec( "DELETE FROM external_releases WHERE artist_id = ?", artistID, ) if err != nil { return fmt.Errorf("delete external releases by artist: %w", err) } return nil } // CountExternalReleases returns the total number of external_release rows. func CountExternalReleases(db *DB) (int, error) { var count int err := db.Conn().QueryRow("SELECT COUNT(*) FROM external_releases").Scan(&count) if err != nil { return 0, fmt.Errorf("count external releases: %w", err) } return count, nil } // GetExternalReleasesByArtistWithCache returns cached external_release rows for a given artist_id // that are within the specified TTL. func GetExternalReleasesByArtistWithCache(db *DB, artistID string, ttl time.Duration) ([]ExternalRelease, error) { cutoff := time.Now().Add(-ttl) rows, err := db.Conn().Query( "SELECT rgid, artist_id, title, type, release_date, is_ignored, cached_at FROM external_releases WHERE artist_id = ? AND cached_at >= ?", artistID, cutoff.Format("2006-01-02 15:04:05"), ) if err != nil { return nil, fmt.Errorf("query cached external releases: %w", err) } defer rows.Close() var results []ExternalRelease for rows.Next() { var r ExternalRelease var releaseDate sql.NullString var releaseType sql.NullString var cachedAt sql.NullTime if err := rows.Scan(&r.RGID, &r.ArtistID, &r.Title, &releaseType, &releaseDate, &r.IsIgnored, &cachedAt); err != nil { return nil, fmt.Errorf("scan cached external release: %w", err) } if releaseType.Valid { r.Type = releaseType.String } if releaseDate.Valid { r.ReleaseDate = releaseDate.String } if cachedAt.Valid { r.CachedAt = cachedAt.Time } results = append(results, r) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate cached external releases: %w", err) } return results, nil }