import Foundation
import Testing
import GRDB
@testable import Music

struct TrackTests {
    // Creates a fresh in-memory database with the tracks table schema.
    // Each test gets its own isolated DatabaseQueue so tests don't share state.
    private static func makeDB() throws -> DatabaseQueue {
        let dbQueue = try DatabaseQueue()
        try dbQueue.write { db in
            try db.create(table: "tracks") { t in
                t.autoIncrementedPrimaryKey("id")
                t.column("fileURL", .text).notNull().unique()
                t.column("title", .text).notNull()
                t.column("artist", .text).notNull()
                t.column("albumArtist", .text).notNull()
                t.column("album", .text).notNull()
                t.column("genre", .text).notNull()
                t.column("year", .integer)
                t.column("trackNumber", .integer)
                t.column("discNumber", .integer)
                t.column("duration", .double).notNull()
                t.column("bpm", .integer)
                t.column("composer", .text).notNull()
                t.column("fileFormat", .text).notNull()
                t.column("bitrate", .integer)
                t.column("sampleRate", .integer)
                t.column("fileSize", .integer).notNull()
                t.column("playCount", .integer).notNull().defaults(to: 0)
                t.column("lastPlayedAt", .datetime)
                t.column("rating", .integer).notNull().defaults(to: 0)
                t.column("dateAdded", .datetime).notNull()
                t.column("dateModified", .datetime).notNull()
                t.column("fileHash", .text).notNull()
            }
        }
        return dbQueue
    }

    // Verifies that Track can round-trip through GRDB's encoding/decoding,
    // meaning it correctly conforms to FetchableRecord and PersistableRecord.
    @Test func roundTripThroughDatabase() throws {
        let dbQueue = try TrackTests.makeDB()
        try dbQueue.write { db in
            var track = Track.fixture(title: "Bohemian Rhapsody", artist: "Queen")
            try track.insert(db)

            #expect(track.id != nil)

            let fetched = try Track.fetchOne(db, key: track.id)
            #expect(fetched?.title == "Bohemian Rhapsody")
            #expect(fetched?.artist == "Queen")
            #expect(fetched?.duration == 210.0)
        }
    }

    // Verifies that didInsert assigns the auto-incremented ID after insertion.
    @Test func didInsertAssignsId() throws {
        let dbQueue = try TrackTests.makeDB()
        try dbQueue.write { db in
            var track = Track.fixture()
            #expect(track.id == nil)
            try track.insert(db)
            #expect(track.id != nil)
        }
    }

    // Verifies the fileHash computation produces a deterministic string from size + date.
    @Test func computeHashIsDeterministic() {
        let date = Date(timeIntervalSince1970: 1700000000)
        let hash1 = Track.computeHash(fileSize: 5_000_000, modificationDate: date)
        let hash2 = Track.computeHash(fileSize: 5_000_000, modificationDate: date)
        #expect(hash1 == hash2)
        #expect(hash1 == "5000000_1700000000")

        let different = Track.computeHash(fileSize: 999, modificationDate: date)
        #expect(different != hash1)
    }
}
