//
// StartedWorkoutNavigator.swift
// Workouts
//
// Copyright 2026 Rouslan Zenetl. All Rights Reserved.
//

import SwiftUI
import SwiftData

// MARK: - Started-Workout Navigation

/// Drives a programmatic push into a freshly-started workout's log screen. Both start
/// paths — the routine picker sheet and the Today board — mint a `WorkoutDocument`,
/// write it, then hand its id here; the workout row only materializes once the
/// file→observer→cache loop round-trips, so we poll the cache for the entity and push it
/// the moment it lands.
private struct StartedWorkoutNavigator: ViewModifier {
    /// The pushed state is a plain value wrapping the run's id — never a retained
    /// `@Model`. This screen stays pushed for the whole workout, and the run's entity
    /// can be deleted and re-imported underneath it (observer remove/re-add churn, a
    /// remote delete); a `@Model` held in `@State` would be *invalidated* by then —
    /// deleted-and-saved models read `isDeleted == false` yet trap on any property
    /// access (the TestFlight 2.3 (125) "crashed when watch ended an exercise" report).
    private struct RunRoute: Identifiable, Hashable { let id: String }

    @Environment(\.modelContext) private var modelContext
    @Environment(SyncEngine.self) private var sync
    @Binding var pendingWorkoutID: String?
    @State private var resolvedRun: RunRoute?

    func body(content: Content) -> some View {
        content
            .navigationDestination(item: $resolvedRun) { route in
                // Re-fetch on every build so the destination always maps a live
                // entity; a re-imported run resolves to its fresh instance.
                if let workout = CacheMapper.fetchWorkout(id: route.id, in: modelContext) {
                    WorkoutLogListView(workout: workout)
                } else {
                    ContentUnavailableView(
                        "Workout Unavailable",
                        systemImage: "xmark.circle",
                        description: Text("This workout is no longer on this device."))
                }
            }
            .onChange(of: pendingWorkoutID) { _, id in
                guard let id else { return }
                pollForWorkout(id: id)
            }
            // Backing out of a run that never started an exercise discards it —
            // tapping into a routine to look around must not persist a phantom
            // workout. Anything with real progress is kept.
            .onChange(of: resolvedRun) { old, new in
                guard let old, new == nil else { return }
                discardIfPristine(id: old.id)
            }
    }

    /// Delete the just-popped workout if it's still an untouched draft. Fetches
    /// fresh by id — never a retained entity — and re-checks liveness.
    private func discardIfPristine(id: String) {
        guard let workout = CacheMapper.fetchWorkout(id: id, in: modelContext),
              !workout.isDeleted, workout.isPristineDraft else { return }
        Task { await sync.delete(workout: workout) }
    }

    /// Poll for the entity after we write the document (the file→observer→cache loop
    /// typically completes in well under a second). Clear the pending id once it
    /// resolves, or silently after ~3 s if it never arrives.
    private func pollForWorkout(id: String) {
        Task {
            for _ in 0..<20 {
                try? await Task.sleep(for: .milliseconds(150))
                if CacheMapper.fetchWorkout(id: id, in: modelContext) != nil {
                    resolvedRun = RunRoute(id: id)
                    pendingWorkoutID = nil
                    return
                }
            }
            pendingWorkoutID = nil
        }
    }
}

extension View {
    /// Navigate into a workout's log screen once it appears in the cache after being
    /// started. Bind to the state you set to the new workout's id right after saving it.
    func navigatesToStartedWorkout(pendingWorkoutID: Binding<String?>) -> some View {
        modifier(StartedWorkoutNavigator(pendingWorkoutID: pendingWorkoutID))
    }
}
