import Foundation
import Combine

// MARK: - Playback State

/// The current playback/recording state of the app.
/// This is the central mutable state that drives audio and UI behavior.
class PlaybackState: ObservableObject {
    // MARK: - Playback mode
    enum Mode {
        case stopped
        case playing
        case recording
    }

    // MARK: - Which parts to play
    enum PartMode: Equatable {
        case all                    // Play all parts together
        case current                // Play only the selected part
    }

    // MARK: - Audio source
    enum AudioSource: Equatable {
        case tones                  // Synthesized tones
        case recorded               // Recorded audio (with tone fallback)
    }

    // MARK: - Published state
    @Published var mode: Mode = .stopped
    @Published var currentPart: String = "All"   // "All" or a specific part name
    @Published var partMode: PartMode = .all
    @Published var audioSource: AudioSource = .tones
    @Published var tempo: Int = 120              // Current BPM (adjustable via slider)
    @Published var isLooping: Bool = false
    @Published var selection: Selection = .none

    // MARK: - Playback position (updated during playback)
    @Published var currentMeasure: Int = 0
    @Published var currentBeat: Double = 0.0     // Beat within current measure

    // MARK: - Default tempo from file (for reset)
    var defaultTempo: Int = 120

    // MARK: - Recorded parts tracking
    @Published var recordedParts: Set<String> = []

    // MARK: - Computed properties

    /// Whether we're currently playing (either play or record mode)
    var isPlaying: Bool {
        return mode == .playing || mode == .recording
    }

    /// Whether we're currently recording
    var isRecording: Bool {
        return mode == .recording
    }

    /// The effective part name for playback (nil means all parts)
    var effectivePartName: String? {
        if currentPart == "All" { return nil }
        return currentPart
    }

    /// The measure range to play (selection range or entire song)
    func playbackRange(totalMeasures: Int) -> (start: Int, end: Int) {
        if let range = selection.measureRange {
            return range
        }
        return (0, max(0, totalMeasures - 1))
    }

    // MARK: - Actions

    func play() {
        mode = .playing
    }

    func record() {
        mode = .recording
    }

    func stop() {
        mode = .stopped
        currentMeasure = 0
        currentBeat = 0.0
    }

    func resetTempo() {
        tempo = defaultTempo
    }

    func markPartRecorded(_ partName: String) {
        recordedParts.insert(partName)
    }

    func isPartRecorded(_ partName: String) -> Bool {
        return recordedParts.contains(partName)
    }
}
