import XCTest
@testable import MixBoard

@MainActor
final class PlayerViewModelTests: XCTestCase {

    // MARK: - Initial State

    func testInitialIsPlayingFalse() {
        let vm = PlayerViewModel()
        XCTAssertFalse(vm.isPlaying)
    }

    func testInitialCurrentTimeZero() {
        let vm = PlayerViewModel()
        XCTAssertEqual(vm.currentTime, 0)
    }

    func testInitialDurationZero() {
        let vm = PlayerViewModel()
        XCTAssertEqual(vm.duration, 0)
    }

    func testInitialCurrentTrackNil() {
        let vm = PlayerViewModel()
        XCTAssertNil(vm.currentTrack)
    }

    func testInitialShowNowPlayingFalse() {
        let vm = PlayerViewModel()
        XCTAssertFalse(vm.showNowPlaying)
    }

    func testInitialShowingWaveformTrue() {
        let vm = PlayerViewModel()
        XCTAssertTrue(vm.showingWaveform)
    }

    func testInitialWaveformSamplesEmpty() {
        let vm = PlayerViewModel()
        XCTAssertTrue(vm.waveformSamples.isEmpty)
    }

    func testInitialCurrentPlayingEntryIDNil() {
        let vm = PlayerViewModel()
        XCTAssertNil(vm.currentPlayingEntryID)
    }

    func testInitialCurrentPlaylistNil() {
        let vm = PlayerViewModel()
        XCTAssertNil(vm.currentPlaylist)
    }

    func testInitialShuffleDisabled() {
        let vm = PlayerViewModel()
        XCTAssertFalse(vm.shuffleEnabled)
    }

    func testInitialRepeatModeOff() {
        let vm = PlayerViewModel()
        XCTAssertEqual(vm.repeatMode, .off)
    }

    func testInitialLoadingWaveformFalse() {
        let vm = PlayerViewModel()
        XCTAssertFalse(vm.isLoadingWaveform)
    }

    // MARK: - Progress Calculations

    func testProgressZeroDuration() {
        let vm = PlayerViewModel()
        XCTAssertEqual(vm.progress, 0)
    }

    func testProgressCalculation() {
        let vm = PlayerViewModel()
        // We can't easily set these through the audio engine,
        // but we can verify the formula via the public property
        // When duration is 0, progress should be 0
        XCTAssertEqual(vm.progress, 0)
    }

    // MARK: - Time Formatting

    func testCurrentTimeFormattedZero() {
        let vm = PlayerViewModel()
        XCTAssertEqual(vm.currentTimeFormatted, "0:00")
    }

    func testDurationFormattedZero() {
        let vm = PlayerViewModel()
        XCTAssertEqual(vm.durationFormatted, "0:00")
    }

    func testRemainingTimeFormattedZero() {
        let vm = PlayerViewModel()
        XCTAssertEqual(vm.remainingTimeFormatted, "-0:00")
    }

    // MARK: - Repeat Mode

    func testRepeatModeOffIcon() {
        XCTAssertEqual(PlayerViewModel.RepeatMode.off.icon, "repeat")
    }

    func testRepeatModeAllIcon() {
        XCTAssertEqual(PlayerViewModel.RepeatMode.all.icon, "repeat")
    }

    func testRepeatModeOneIcon() {
        XCTAssertEqual(PlayerViewModel.RepeatMode.one.icon, "repeat.1")
    }

    func testRepeatModeAllCases() {
        XCTAssertEqual(PlayerViewModel.RepeatMode.allCases.count, 3)
    }

    func testRepeatModeRawValues() {
        XCTAssertEqual(PlayerViewModel.RepeatMode.off.rawValue, "Off")
        XCTAssertEqual(PlayerViewModel.RepeatMode.all.rawValue, "Repeat All")
        XCTAssertEqual(PlayerViewModel.RepeatMode.one.rawValue, "Repeat One")
    }

    // MARK: - Volume

    func testDefaultVolume() {
        let vm = PlayerViewModel()
        XCTAssertEqual(vm.volume, 0.8, accuracy: 0.001)
    }

    func testSetVolume() {
        let vm = PlayerViewModel()
        vm.volume = 0.5
        XCTAssertEqual(vm.volume, 0.5, accuracy: 0.001)
    }

    // MARK: - Stop

    func testStopClearsWaveform() {
        let vm = PlayerViewModel()
        vm.waveformSamples = [WaveformGenerator.WaveformSample(min: -0.5, max: 0.5)]
        XCTAssertFalse(vm.waveformSamples.isEmpty)
        vm.stop()
        XCTAssertTrue(vm.waveformSamples.isEmpty)
    }

    func testStopClearsEntryID() {
        let vm = PlayerViewModel()
        vm.currentPlayingEntryID = UUID()
        vm.stop()
        XCTAssertNil(vm.currentPlayingEntryID)
    }

    func testStopSetsNotPlaying() {
        let vm = PlayerViewModel()
        vm.stop()
        XCTAssertFalse(vm.isPlaying)
    }

    // MARK: - Load and Play (error path)

    func testLoadAndPlayNonExistentFile() {
        let vm = PlayerViewModel()
        let track = Track(title: "Missing", filePath: "nonexistent/file.mp3")
        // Should not crash — error is caught
        vm.loadAndPlay(track)
        XCTAssertFalse(vm.isPlaying)
    }

    // MARK: - Toggle Play/Pause Without Track

    func testTogglePlayPauseWithoutTrack() {
        let vm = PlayerViewModel()
        vm.togglePlayPause()
        // Should not crash
        XCTAssertFalse(vm.isPlaying)
    }

    // MARK: - Shuffle Toggle

    func testShuffleToggle() {
        let vm = PlayerViewModel()
        XCTAssertFalse(vm.shuffleEnabled)
        vm.shuffleEnabled = true
        XCTAssertTrue(vm.shuffleEnabled)
        vm.shuffleEnabled = false
        XCTAssertFalse(vm.shuffleEnabled)
    }

    // MARK: - Repeat Mode Cycling

    func testRepeatModeCycling() {
        let vm = PlayerViewModel()
        XCTAssertEqual(vm.repeatMode, .off)
        vm.repeatMode = .all
        XCTAssertEqual(vm.repeatMode, .all)
        vm.repeatMode = .one
        XCTAssertEqual(vm.repeatMode, .one)
        vm.repeatMode = .off
        XCTAssertEqual(vm.repeatMode, .off)
    }

    // MARK: - Play Next/Previous Without Playlist

    func testPlayNextWithoutPlaylist() {
        let vm = PlayerViewModel()
        // Should not crash
        vm.playNext()
        XCTAssertFalse(vm.isPlaying)
    }

    func testPlayPreviousWithoutPlaylist() {
        let vm = PlayerViewModel()
        vm.playPrevious()
        XCTAssertFalse(vm.isPlaying)
    }

    // MARK: - Seek

    func testSeekToProgress() {
        let vm = PlayerViewModel()
        // Without a loaded file, this should not crash
        vm.seekToProgress(0.5)
    }

    func testSkipForwardDefault() {
        let vm = PlayerViewModel()
        vm.skipForward()
        // Should not crash
    }

    func testSkipBackwardDefault() {
        let vm = PlayerViewModel()
        vm.skipBackward()
        // Should not crash
    }

    func testSkipForwardCustom() {
        let vm = PlayerViewModel()
        vm.skipForward(30)
        // Should not crash
    }

    func testSkipBackwardCustom() {
        let vm = PlayerViewModel()
        vm.skipBackward(30)
        // Should not crash
    }

    // MARK: - Waveform Loading (cached)

    func testLoadWaveformFromCache() {
        let vm = PlayerViewModel()
        let track = Track(title: "Test", filePath: "Music/test.mp3")
        let samples = [WaveformGenerator.WaveformSample(min: -0.3, max: 0.7)]
        track.waveformData = try? JSONEncoder().encode(samples)

        vm.loadWaveform(for: track)
        XCTAssertEqual(vm.waveformSamples.count, 1)
        XCTAssertEqual(vm.waveformSamples[0].min, -0.3, accuracy: 0.001)
        XCTAssertEqual(vm.waveformSamples[0].max, 0.7, accuracy: 0.001)
    }

    func testLoadWaveformCorruptedCacheTriggersGeneration() {
        let vm = PlayerViewModel()
        let track = Track(title: "Test", filePath: "Music/test.mp3")
        track.waveformData = Data([0xFF, 0xFE])

        vm.loadWaveform(for: track)
        // Corrupted cache should not set samples synchronously
        // (generation will fail async since file doesn't exist)
    }
}
