import Foundation
import Notifying
import Recording
import Testing

@testable import Attendi

@Suite("Content view model")
struct ContentViewModelTests {

    typealias Model = ContentView.Model

    // MARK: Load

    @MainActor
    @Suite("Load")
    struct Load {

        @Test
        func `load fills the supported locales and aligns the picked locale`() async {
            let preinstaller = PreinstallingMock()

            preinstaller.localesSupported = [.dutch, .english]
            preinstaller.equivalents = [.current: .english]

            let model = Model(preinstaller: preinstaller)

            await model.load()

            #expect(model.locale == .english)
            #expect(model.locales.count == 2)
            #expect(model.locales.contains(.dutch))
            #expect(model.locales.contains(.english))
        }

        @Test
        func `load preinstalls the aligned locale`() async throws {
            let preinstaller = PreinstallingMock()

            preinstaller.localesSupported = [.dutch, .english]
            preinstaller.equivalents = [.current: .english]

            let model = Model(preinstaller: preinstaller)

            await model.load()

            try await Task.sleep(for: .seconds(0.1))

            #expect(preinstaller.localesPreinstalled == [.english])
        }

    }

    // MARK: Preinstall

    @MainActor
    @Suite("Preinstall")
    struct Preinstall {

        @Test
        func `preinstalling the same locale twice runs once`() async throws {
            let preinstaller = PreinstallingMock()
            let model = Model(preinstaller: preinstaller)

            model.locale = .dutch

            model.preinstall()
            model.preinstall()

            try await Task.sleep(for: .seconds(0.1))

            #expect(preinstaller.localesPreinstalled == [.dutch])
        }

        @Test
        func `preinstalling a newly picked locale runs again`() async throws {
            let preinstaller = PreinstallingMock()
            let model = Model(preinstaller: preinstaller)

            model.locale = .dutch
            model.preinstall()

            model.locale = .english
            model.preinstall()

            try await Task.sleep(for: .seconds(0.1))

            #expect(preinstaller.localesPreinstalled == [.dutch, .english])
        }

    }

    // MARK: Events

    @MainActor
    @Suite("Events")
    struct Events {

        @Test
        func `a started event posts an info notification`() async throws {
            let preinstaller = PreinstallingMock()
            let model = Model(preinstaller: preinstaller)

            model.locale = .dutch
            model.preinstall()

            try await Task.sleep(for: .seconds(0.1))

            preinstaller.emit(.started(.dutch))

            try await Task.sleep(for: .seconds(0.1))

            #expect(model.notifier.notifications.count == 1)
            #expect(model.notifier.notifications.first?.kind == .info)
            #expect(model.notifier.notifications.first?.symbol == "arrow.down.circle")
            #expect(model.notifier.notifications.first?.message == String(localized: .viewRecordingNotificationDownloadStarted(model.name(for: .dutch))))
        }

        @Test
        func `a cancelled event posts a warning notification`() async throws {
            let preinstaller = PreinstallingMock()
            let model = Model(preinstaller: preinstaller)

            model.locale = .dutch
            model.preinstall()

            try await Task.sleep(for: .seconds(0.1))

            preinstaller.emit(.cancelled(.dutch))

            try await Task.sleep(for: .seconds(0.1))

            #expect(model.notifier.notifications.first?.kind == .warning)
            #expect(model.notifier.notifications.first?.symbol == "xmark.circle")
            #expect(model.notifier.notifications.first?.message == String(localized: .viewRecordingNotificationDownloadCancelled(model.name(for: .dutch))))
        }

        @Test
        func `a failed event posts an error notification`() async throws {
            let preinstaller = PreinstallingMock()
            let model = Model(preinstaller: preinstaller)

            model.locale = .dutch
            model.preinstall()

            try await Task.sleep(for: .seconds(0.1))

            preinstaller.emit(.failed(.dutch))

            try await Task.sleep(for: .seconds(0.1))

            #expect(model.notifier.notifications.first?.kind == .error)
            #expect(model.notifier.notifications.first?.symbol == "exclamationmark.triangle")
            #expect(model.notifier.notifications.first?.message == String(localized: .viewRecordingNotificationDownloadFailed(model.name(for: .dutch))))
        }

        @Test
        func `a cancellation of the current locale lets a re-pick retry`() async throws {
            let preinstaller = PreinstallingMock()
            let model = Model(preinstaller: preinstaller)

            model.locale = .dutch
            model.preinstall()

            try await Task.sleep(for: .seconds(0.1))

            preinstaller.emit(.cancelled(.dutch))

            try await Task.sleep(for: .seconds(0.1))

            model.preinstall()

            try await Task.sleep(for: .seconds(0.1))

            #expect(preinstaller.localesPreinstalled == [.dutch, .dutch])
        }

        @Test
        func `a cancellation of another locale keeps the current preinstallation`() async throws {
            let preinstaller = PreinstallingMock()
            let model = Model(preinstaller: preinstaller)

            model.locale = .english
            model.preinstall()

            try await Task.sleep(for: .seconds(0.1))

            preinstaller.emit(.cancelled(.dutch))

            try await Task.sleep(for: .seconds(0.1))

            model.preinstall()

            try await Task.sleep(for: .seconds(0.1))

            #expect(preinstaller.localesPreinstalled == [.english])
        }

    }

}

// MARK: - Constants

private extension Locale {
    /// A supported locale to preinstall in the tests.
    static let dutch = Locale(identifier: "nl-NL")
    /// Another supported locale to preinstall in the tests.
    static let english = Locale(identifier: "en-US")
}
