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

import IndieSync
import SwiftUI
import SwiftData

// MARK: - Routine Picker Sheet

struct RoutinePickerSheet: View {
    @Environment(SyncEngine.self) private var sync
    @Environment(AppServices.self) private var services
    @Environment(\.dismiss) private var dismiss

    /// Called with the new workout's id right after it's saved, so the presenting
    /// screen can navigate into it once the cache catches up.
    var onStarted: (String) -> Void = { _ in }

    @Query(sort: [SortDescriptor(\Routine.order), SortDescriptor(\Routine.name)])
    private var routines: [Routine]

    @Query(sort: \Workout.start, order: .reverse)
    private var workouts: [Workout]

    /// Set when the user picks a routine while other workouts are still going — drives the
    /// "end the current one(s) or run in parallel?" prompt.
    @State private var routineAwaitingConfirmation: Routine?

    private var activeWorkouts: [Workout] {
        workouts.filter { $0.status == .inProgress || $0.status == .notStarted }
    }

    /// The routines most recently trained, newest first, each with the start date
    /// of its latest workout — derived from the workout log (`workouts` is
    /// already sorted by start descending). Each workout's `routineID` follows the
    /// seed clone-on-edit redirect so a workout started from a since-forked seed
    /// still credits the live clone. Deduped, capped, and limited to routines that
    /// still exist.
    private var recentRoutines: [(routine: Routine, lastStart: Date)] {
        var seen = Set<String>()
        var recents: [(routine: Routine, lastStart: Date)] = []
        for workout in workouts {
            guard let routineID = workout.routineID else { continue }
            let liveID = sync.currentRoutineID(for: routineID)
            guard !seen.contains(liveID) else { continue }
            seen.insert(liveID)
            if let routine = routines.first(where: { $0.id == liveID }) {
                recents.append((routine, workout.start))
                if recents.count == 3 { break }
            }
        }
        return recents
    }

    /// One selectable routine row, shared by the Recent and All Routines sections.
    /// Recent rows pass `lastTrained` to show how long ago the routine was run.
    private func routineRow(_ routine: Routine, lastTrained: Date? = nil) -> some View {
        Button {
            confirmAndStart(with: routine)
        } label: {
            HStack {
                Image(systemName: routine.systemImage)
                    .foregroundColor(Color.color(from: routine.color))
                    .frame(width: 28)
                VStack(alignment: .leading, spacing: 2) {
                    Text(routine.name)
                    if let lastTrained {
                        Text(lastTrained.daysAgoLabel())
                            .font(.caption)
                            .foregroundColor(.secondary)
                    }
                }
                Spacer()
                Text("\(routine.exercisesArray.count) exercises")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
        }
    }

    var body: some View {
        NavigationStack {
            List {
                if !recentRoutines.isEmpty {
                    Section("Recent") {
                        ForEach(recentRoutines, id: \.routine.id) { recent in
                            routineRow(recent.routine, lastTrained: recent.lastStart)
                        }
                    }
                }

                Section(recentRoutines.isEmpty ? "" : "All Routines") {
                    ForEach(routines) { routine in
                        routineRow(routine)
                    }
                }
            }
            .navigationTitle("Select a Routine")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button("Cancel") {
                        dismiss()
                    }
                }
            }
            .confirmationDialog(
                activePromptTitle,
                isPresented: Binding(
                    get: { routineAwaitingConfirmation != nil },
                    set: { if !$0 { routineAwaitingConfirmation = nil } }
                ),
                titleVisibility: .visible,
                presenting: routineAwaitingConfirmation
            ) { routine in
                Button("End Current & Start New") { endActiveThenStart(with: routine) }
                Button("Start in Parallel") { start(with: routine) }
                Button("Cancel", role: .cancel) { routineAwaitingConfirmation = nil }
            } message: { _ in
                Text(activePromptMessage)
            }
        }
    }

    private var activePromptTitle: String {
        activeWorkouts.count == 1 ? "Workout in Progress" : "\(activeWorkouts.count) Workouts in Progress"
    }

    private var activePromptMessage: String {
        let n = activeWorkouts.count
        let those = n == 1 ? "it" : "them"
        return "You already have \(n == 1 ? "a workout" : "\(n) workouts") going. End \(those) first, or run this one alongside."
    }

    /// Prompt before starting if other workouts are still going; otherwise start straight away.
    private func confirmAndStart(with routine: Routine) {
        if activeWorkouts.isEmpty {
            start(with: routine)
        } else {
            routineAwaitingConfirmation = routine
        }
    }

    /// End every in-flight workout (keeping its progress), then start the picked routine.
    private func endActiveThenStart(with routine: Routine) {
        // A pristine draft (no exercise ever started) has nothing worth keeping —
        // "ending" it would mint a junk all-skipped completed workout, so it's
        // discarded instead, same rule as backing out of one.
        let toDiscard = activeWorkouts.filter(\.isPristineDraft)
        let toEnd = activeWorkouts.filter { !$0.isPristineDraft }.map { WorkoutDocument(from: $0) }
        routineAwaitingConfirmation = nil
        Task {
            for workout in toDiscard {
                await sync.delete(workout: workout)
            }
            for var doc in toEnd {
                doc.endKeepingProgress()
                await sync.save(workout: doc)
            }
        }
        start(with: routine)
    }

    private func start(with routine: Routine) {
        // Hand the id back after the save so the presenter can poll the cache and
        // navigate into the run — mirroring the exercise-list start path.
        Task {
            let id = await WorkoutStarter.start(routine: routine, sync: sync)
            onStarted(id)
        }
        dismiss()
    }
}
