import Foundation
import Observation
import OSLog
import SwiftUI

/// Composition root for the watch app. Owns the recorder and the transfer
/// service and mediates between them: a finished recording is handed to the
/// transfer queue. The watch has no iCloud or local database — its whole job is
/// to capture audio and get it to the phone.
@Observable
@MainActor
final class WatchAppServices {
    let recorder = WatchAudioRecorder()
    let transfer = WatchTransferService()

    func bootstrap() {
        transfer.activate()
        transfer.reconcileQueue()
        // The 10-minute safety cap ends a take without a Stop tap — queue it
        // rather than lose it.
        recorder.onInvoluntaryFinish = { [weak self] recording in
            self?.transfer.enqueue(recording)
        }
        // A capture that silently produced no audio (encode error, dead input)
        // surfaces on screen — there's no console on a real watch.
        recorder.onCaptureFailure = { [weak self] message in
            self?.lastError = message
        }
    }

    /// Single-button behaviour: start if idle, stop-and-queue if recording.
    func toggleRecording() {
        if recorder.isRecording {
            stopAndQueue()
        } else {
            Task { await startRecording() }
        }
    }

    private let logger = Logger(subsystem: "dev.rzen.indie.Notes.watchkitapp", category: "recording")

    /// Why the last start attempt failed, surfaced in the UI — on a real watch
    /// there's no console, so a silent failure is indistinguishable from a dead
    /// button. Cleared when a recording starts successfully.
    private(set) var lastError: String?

    /// Request permission (once) then begin recording. A no-op if already
    /// recording or permission is refused.
    func startRecording() async {
        guard !recorder.isRecording else { return }
        guard await recorder.requestPermission() else {
            logger.warning("microphone permission refused")
            return
        }
        do {
            try await recorder.startRecording()
            lastError = nil
        } catch {
            logger.error("could not start recording: \(error, privacy: .public)")
            lastError = error.localizedDescription
        }
    }

    private func stopAndQueue() {
        if let recording = recorder.stopRecording() {
            transfer.enqueue(recording)
        }
    }

    /// Complication deep link (`contextful://record?autostart=1`) → auto-start a
    /// recording. Plain launches never carry the URL, so they never auto-record.
    func handleDeepLink(_ url: URL) {
        guard url.scheme == "contextful", url.host == "record" else { return }
        let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
        let autostart = components?.queryItems?.first { $0.name == "autostart" }?.value == "1"
        guard autostart else { return }
        Task { await startRecording() }
    }

    func handleScenePhase(_ phase: ScenePhase) {
        switch phase {
        case .active:
            // Catch up any file whose delivery we never confirmed.
            transfer.reconcileQueue()
        case .background:
            // Never lose audio to the app being suspended: stop-and-queue
            // whatever is recording rather than dropping the take. (A brief
            // wrist-down is `.inactive` and is covered by the recorder's
            // frontmost-timeout extension, so it keeps recording.)
            stopAndQueue()
        default:
            break
        }
    }
}
