//
// RoutinesLibraryView.swift
// Workouts
//
// Created by rzen on 7/25/25 at 6:24 PM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//

import SwiftUI
import SwiftData

/// The Routines segment of the Library tab: a plain list of routines, one row each,
/// ordered and reorderable via `EditButton` + drag. A leading swipe duplicates a
/// routine; a trailing swipe edits or deletes. Starter routines are seeded
/// automatically on the true first run (`SyncEngine.autoSeedIfEmpty`), so an empty
/// library just invites creating one. Pushed from `LibraryView`, so it relies on that
/// view's `NavigationStack` and contributes only its own toolbar items (+ and Edit).
struct RoutinesLibraryView: View {
    @Environment(SyncEngine.self) private var sync

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

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

    @Query private var schedules: [Schedule]

    @State private var showingAddSheet = false
    @State private var routineToEdit: Routine?
    @State private var routineToDelete: Routine?

    /// Most recent *completed* workout date per routine — "trained", not merely
    /// started, mirroring `ProgressTabView`'s `completed: workout.status == .completed`.
    /// `workouts` sorts start-descending, so the first completed match per live routine
    /// id is its latest. Each workout's `routineID` is resolved through
    /// `sync.currentRoutineID(for:)` so a workout logged against a since-edited seed
    /// still credits the live clone.
    private var lastTrainedByRoutineID: [String: Date] {
        var map: [String: Date] = [:]
        for workout in workouts where workout.status == .completed {
            guard let routineID = workout.routineID else { continue }
            let liveID = sync.currentRoutineID(for: routineID)
            if map[liveID] == nil {
                map[liveID] = workout.start
            }
        }
        return map
    }

    var body: some View {
        let lastTrained = lastTrainedByRoutineID
        List {
            // A separate Section so the browse-starters row never joins the
            // reorderable ForEach below (EditButton's drag/delete affordances only
            // ever apply to actual routines).
            Section {
                ForEach(routines) { routine in
                    NavigationLink {
                        RoutineDetailView(routine: routine)
                    } label: {
                        RoutineRow(routine: routine, lastTrained: lastTrained[routine.id])
                    }
                    .swipeActions(edge: .leading) {
                        Button {
                            Task { await sync.duplicate(routine: routine) }
                        } label: {
                            Label("Duplicate", systemImage: "plus.square.on.square")
                        }
                        .tint(.teal)
                    }
                    .swipeActions {
                        Button(role: .destructive) {
                            routineToDelete = routine
                        } label: {
                            Label("Delete", systemImage: "trash")
                        }
                        Button {
                            routineToEdit = routine
                        } label: {
                            Label("Edit", systemImage: "pencil")
                        }
                        .tint(.indigo)
                    }
                }
                .onMove(perform: moveRoutines)
            }

            Section {
                NavigationLink {
                    StarterGalleryView()
                } label: {
                    Label("Browse Starter Routines", systemImage: "sparkles")
                }
            }
        }
        .listStyle(.insetGrouped)
        .overlay {
            if routines.isEmpty {
                ContentUnavailableView(
                    "No Routines Yet",
                    systemImage: "dumbbell.fill",
                    description: Text("Create a routine to organize your workout routine.")
                )
            }
        }
        .navigationTitle("Routines")
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button {
                    showingAddSheet = true
                } label: {
                    Image(systemName: "plus")
                }
                .accessibilityLabel("Add Routine")
            }
            ToolbarItem(placement: .navigationBarTrailing) {
                EditButton()
            }
        }
        .sheet(isPresented: $showingAddSheet) {
            RoutineAddEditView(routine: nil)
        }
        .sheet(item: $routineToEdit) { routine in
            RoutineAddEditView(routine: routine)
        }
        .confirmationDialog(
            "Delete Routine?",
            isPresented: Binding(
                get: { routineToDelete != nil },
                set: { if !$0 { routineToDelete = nil } }
            ),
            titleVisibility: .visible,
            presenting: routineToDelete
        ) { routine in
            Button("Delete", role: .destructive) {
                Task { await sync.delete(routine: routine) }
                routineToDelete = nil
            }
            Button("Cancel", role: .cancel) {
                routineToDelete = nil
            }
        } message: { routine in
            Text(deleteMessage(for: routine))
        }
    }

    /// The delete-confirmation body, extended with a note about any schedules that
    /// reference this routine — deleting the routine leaves them behind (schedules
    /// aren't cascaded), so the Today board would otherwise show an orphaned day with
    /// no explanation.
    private func deleteMessage(for routine: Routine) -> String {
        let base = "This will permanently delete \"\(routine.name)\" and all its exercises. Past workouts are kept."
        let count = schedules.filter { sync.currentRoutineID(for: $0.routineID) == routine.id }.count
        guard count > 0 else { return base }
        return base + " Its \(count) scheduled day\(count == 1 ? "" : "s") will stay on the Today board without a startable routine."
    }

    /// Reorder and renumber via `RoutineOrdering`, writing only the routines whose
    /// order actually changed. Order-only writes deliberately leave `updatedAt`
    /// alone — it's bookkeeping, not content, and touching it needlessly risks forking
    /// a starter (its edit sheets already guard "pristine" saves against exactly that).
    private func moveRoutines(from source: IndexSet, to destination: Int) {
        let ids = routines.map(\.id)
        let currentOrders = Dictionary(uniqueKeysWithValues: routines.map { ($0.id, $0.order) })
        let changes = RoutineOrdering.changedOrders(
            ids: ids, currentOrders: currentOrders, from: source, to: destination)
        guard !changes.isEmpty else { return }

        Task {
            for change in changes {
                guard let routine = routines.first(where: { $0.id == change.id }), routine.isLive else { continue }
                var doc = RoutineDocument(from: routine)
                doc.order = change.order
                await sync.save(routine: doc)
            }
        }
    }
}

/// A single routine row: a small rounded-square badge (the routine's color at low
/// opacity, its symbol tinted full-strength) beside the routine name — with a small
/// "Starter" capsule for a bundled seed — its exercise count, and (once it's been
/// trained at least once) how long ago.
private struct RoutineRow: View {
    var routine: Routine
    var lastTrained: Date?

    var body: some View {
        HStack(spacing: 12) {
            Image(systemName: routine.systemImage)
                .font(.title3)
                .foregroundStyle(routineColor)
                .frame(width: 36, height: 36)
                .background(routineColor.opacity(0.12), in: RoundedRectangle(cornerRadius: 8))

            VStack(alignment: .leading, spacing: 2) {
                HStack(spacing: 6) {
                    Text(routine.name)
                        .foregroundStyle(.primary)
                    if SeedLibrary.isSeed(id: routine.id) {
                        Text("Starter")
                            .font(.caption2)
                            .foregroundStyle(.secondary)
                            .padding(.horizontal, 6)
                            .padding(.vertical, 2)
                            .background(Color.secondary.opacity(0.15), in: Capsule())
                    }
                }

                Text(caption)
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }
        }
        .padding(.vertical, 2)
    }

    private var routineColor: Color {
        Color.color(from: routine.color)
    }

    private var caption: String {
        let base = "\(routine.exercisesArray.count) exercises"
        guard let lastTrained else { return base }
        return "\(base) · Last trained \(lastTrained.daysAgoLabel())"
    }
}
