import Foundation
import Recording

/// A recording service that records its invocations in call order, each one taking a configurable amount of time, and can be
/// configured to fail.
@MainActor
final class CapturingMock: Capturing {
    
    // MARK: Properties

    /// The stream of events the service emits outside its method calls.
    let events: AsyncStream<CapturingEvent>

    /// The duration every method takes before returning, simulating slow capture work.
    var delay: Duration = .zero

    /// The error the service throws from every method, or `nil` when it should succeed.
    var error: Error?

    /// The names of the methods called on the service, in call order.
    private(set) var calls: [String] = []

    /// The continuation that feeds ``events``.
    private let continuation: AsyncStream<CapturingEvent>.Continuation
    
    // MARK: Initializers

    init() {
        (events, continuation) = AsyncStream.makeStream(of: CapturingEvent.self)
    }
    
    // MARK: Methods

    /// Emits an interruption of the ongoing capture through ``events``.
    func interrupt() {
        continuation.yield(.interrupted)
    }

    /// Emits the end of an interruption of the capture through ``events``.
    ///
    /// - Parameter shouldResume: Whether the capture may resume right away.
    func endInterruption(
        shouldResume: Bool
    ) {
        continuation.yield(.interruptionEnded(shouldResume: shouldResume))
    }

    func start() async throws {
        try await called("start")
    }

    func pause() async throws {
        try await called("pause")
    }

    func resume() async throws {
        try await called("resume")
    }

    func stop() async throws -> URL {
        try await called("stop")

        return FileManager.default.temporaryDirectory.appending(path: "mock.m4a")
    }

    private func called(
        _ name: String
    ) async throws {
        calls.append(name)

        try await Task.sleep(for: delay)

        if let error {
            throw error
        }
    }

}
