//
// LiveRunCoverView.swift
// Workouts Watch App
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//

import SwiftUI
import SwiftData

/// Follower surface shown on the watch when the *iPhone* starts driving a live exercise (see
/// `LiveProgress`) and the watch isn't already in that run. It presents the watch's real
/// `ExerciseProgressView`, seeded to the phone's current page — so the watch follows along and
/// the user can take over and drive from the wrist, mirroring the iPhone's `LiveRunCoverView`.
///
///   • Incoming phone frames jump this driver's page (it never re-broadcasts them).
///   • A human transition *here* broadcasts back to the phone and persists through the bridge.
struct LiveRunCoverView: View {
    /// The frame that triggered presentation — fixes which workout/log this cover follows.
    let frame: LiveProgress
    /// Dismiss + suppress re-presentation of this run (the user closed it).
    let onClose: () -> Void

    @Environment(WatchConnectivityBridge.self) private var bridge
    @Environment(\.modelContext) private var modelContext

    /// The live workout entity, filtered to this run's id and observed so durable updates the
    /// phone drove flow back into our working copy.
    @Query private var workouts: [Workout]

    /// Working copy the driver mutates; resolved from the cache on appear. `nil` until then.
    @State private var doc: WorkoutDocument?

    init(frame: LiveProgress, onClose: @escaping () -> Void) {
        self.frame = frame
        self.onClose = onClose
        let workoutID = frame.workoutID
        _workouts = Query(filter: #Predicate<Workout> { $0.id == workoutID })
    }

    private var workout: Workout? { workouts.first }

    /// The latest frame to follow, scoped to this cover's run.
    private var incomingFrame: LiveProgress? {
        bridge.liveIncoming.flatMap { $0.logID == frame.logID ? $0 : nil }
    }

    var body: some View {
        NavigationStack {
            Group {
                if let binding = Binding($doc) {
                    ExerciseProgressView(
                        doc: binding,
                        logID: frame.logID,
                        onChange: { persist() },
                        onLive: { bridge.sendLiveProgress($0) },
                        onLiveEnded: {
                            bridge.sendLiveEnded(workoutID: frame.workoutID, logID: frame.logID)
                        },
                        incomingFrame: incomingFrame
                    )
                } else {
                    // The workout hasn't resolved from the cache yet (or has vanished).
                    ProgressView()
                }
            }
        }
        .onAppear {
            if doc == nil, let workout { doc = WorkoutDocument(from: workout) }
        }
        .onChange(of: workout?.updatedAt) { _, _ in
            // Absorb the durable progress the phone drove. The live *page* is driven by
            // frames, not this — so re-seeding the doc is safe.
            if let workout { doc = WorkoutDocument(from: workout) }
        }
    }

    private func persist() {
        guard let doc else { return }
        bridge.update(workout: doc)
    }
}
