import IndieSync
import SwiftUI
import SwiftData

/// An exercise flagged for deletion — captured as plain values, never a retained
/// `@Model` (see `PendingRoutineDelete`). The routine document is rebuilt by
/// exercise id, so the id is all the delete needs; the name is only for the prompt.
private struct PendingExerciseDelete: Identifiable {
    let id: String
    let name: String
}

/// An exercise flagged for editing — captured as just its id, not a retained `@Model`
/// (see `PendingExerciseDelete`). The sheet re-resolves the live exercise by id off the
/// current `routine` when it presents, so a remote delete between the tap and the
/// sheet's next body evaluation can't hand the editor a dangling entity.
private struct PendingExerciseEdit: Identifiable {
    let id: String
}

/// The detail pane for one routine: a header (color badge, name, activity type, Edit)
/// over a reorderable exercise list. All mutations rebuild the `RoutineDocument` and go
/// through `syncEngine.save(routine:)` — never the SwiftData cache directly — so editing
/// a starter forks it to a fresh ULID for free. The routine is resolved by id every
/// render (never a captured entity) so an open detail follows that fork, mirroring iOS
/// `RoutineDetailView`.
struct MacRoutineDetailView: View {
    @Environment(SyncEngine.self) private var syncEngine

    @State private var routineID: String
    @Query private var routines: [Routine]

    @State private var showingRoutineEdit = false
    @State private var showingAddExercise = false
    @State private var exerciseToEdit: PendingExerciseEdit?
    @State private var pendingExerciseDelete: PendingExerciseDelete?
    @AppStorage("weightUnit") private var weightUnit: WeightUnit = .lb

    init(routineID: String) {
        _routineID = State(initialValue: routineID)
    }

    /// The live routine behind the id we hold, resolved through the seed clone-on-edit
    /// redirect so a fork mid-screen keeps resolving.
    private var routine: Routine? {
        let id = syncEngine.currentRoutineID(for: routineID)
        return routines.first { $0.id == id }
    }

    var body: some View {
        Group {
            if let routine {
                content(for: routine)
            } else {
                // The id no longer maps to a live routine (deleted elsewhere, or a
                // transient mid-clone frame).
                ContentUnavailableView("Routine Unavailable", systemImage: "dumbbell")
            }
        }
        .sheet(isPresented: $showingRoutineEdit) {
            if let routine { MacRoutineEditorSheet(routine: routine) }
        }
        .sheet(isPresented: $showingAddExercise) {
            MacExerciseEditorSheet(routineID: syncEngine.currentRoutineID(for: routineID), exercise: nil)
        }
        .sheet(item: $exerciseToEdit) { pending in
            if let exercise = routine?.exercisesArray.first(where: { $0.id == pending.id }), exercise.isLive {
                MacExerciseEditorSheet(routineID: syncEngine.currentRoutineID(for: routineID), exercise: exercise)
            }
        }
        .confirmationDialog(
            "Delete Exercise?",
            isPresented: Binding(
                get: { pendingExerciseDelete != nil },
                set: { if !$0 { pendingExerciseDelete = nil } }
            ),
            titleVisibility: .visible,
            presenting: pendingExerciseDelete
        ) { pending in
            Button("Delete", role: .destructive) {
                deleteExercise(id: pending.id)
                pendingExerciseDelete = nil
            }
            Button("Cancel", role: .cancel) { pendingExerciseDelete = nil }
        } message: { pending in
            Text("Remove \"\(pending.name)\" from this routine?")
        }
    }

    @ViewBuilder
    private func content(for routine: Routine) -> some View {
        List {
            Section {
                header(for: routine)
            }

            Section("Exercises") {
                if routine.exercisesArray.isEmpty {
                    Text("No exercises added yet.")
                        .foregroundStyle(.secondary)
                } else {
                    ForEach(routine.exercisesArray) { exercise in
                        ExerciseRow(exercise: exercise, weightUnit: weightUnit)
                            .contentShape(Rectangle())
                            .onTapGesture(count: 2) { exerciseToEdit = PendingExerciseEdit(id: exercise.id) }
                            .contextMenu {
                                Button("Edit…") { exerciseToEdit = PendingExerciseEdit(id: exercise.id) }
                                Button("Duplicate") { duplicateExercise(id: exercise.id) }
                                Divider()
                                Button("Delete", role: .destructive) {
                                    pendingExerciseDelete = PendingExerciseDelete(id: exercise.id, name: exercise.name)
                                }
                            }
                    }
                    .onMove { source, destination in
                        moveExercises(routine: routine, from: source, to: destination)
                    }
                }
            }
        }
        .navigationTitle(routine.name)
        .toolbar {
            ToolbarItem {
                Button {
                    showingAddExercise = true
                } label: {
                    Label("Add Exercise", systemImage: "plus")
                }
            }
        }
    }

    private func header(for routine: Routine) -> some View {
        HStack(spacing: 14) {
            Image(systemName: routine.systemImage)
                .font(.largeTitle)
                .foregroundStyle(Color.color(from: routine.color))
                .frame(width: 56, height: 56)
                .background(Color.color(from: routine.color).opacity(0.12), in: RoundedRectangle(cornerRadius: 14))

            VStack(alignment: .leading, spacing: 4) {
                Text(routine.name)
                    .font(.title2.weight(.semibold))
                Label(routine.activityTypeEnum.displayName, systemImage: routine.activityTypeEnum.systemImage)
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            }

            Spacer()

            Button {
                showingRoutineEdit = true
            } label: {
                Label("Edit", systemImage: "pencil")
            }
        }
        .padding(.vertical, 6)
    }

    // MARK: - Mutations (all rebuild the document, never the cache)

    /// Reorder and renumber the exercises, then save. Resolves the routine at call time
    /// so it follows a clone-on-edit. Stamps `updatedAt` — an exercise reorder is a real
    /// content edit that should fork a starter (unlike a routine-list reorder).
    private func moveExercises(routine: Routine, from source: IndexSet, to destination: Int) {
        var ordered = routine.exercisesArray
        ordered.move(fromOffsets: source, toOffset: destination)

        var doc = RoutineDocument(from: routine)
        doc.exercises = ordered.enumerated().map { i, ex in
            var ed = ExerciseDocument(from: ex)
            ed.order = i
            return ed
        }
        doc.updatedAt = Date()
        Task { await syncEngine.save(routine: doc) }
    }

    private func deleteExercise(id: String) {
        guard let routine else { return }
        var doc = RoutineDocument(from: routine)
        doc.exercises.removeAll { $0.id == id }
        for i in doc.exercises.indices { doc.exercises[i].order = i }
        doc.updatedAt = Date()
        Task { await syncEngine.save(routine: doc) }
    }

    /// Copy an exercise in place, right after itself (the interval-segment case). The
    /// clone is an exact duplicate under a fresh id; renaming it is a follow-up edit.
    /// Mirrors iOS `RoutineDetailView.duplicateExercise`.
    private func duplicateExercise(id: String) {
        guard let routine else { return }
        var doc = RoutineDocument(from: routine)
        guard let idx = doc.exercises.firstIndex(where: { $0.id == id }) else { return }
        var copy = doc.exercises[idx]
        copy.id = ULID.make()
        doc.exercises.insert(copy, at: idx + 1)
        for i in doc.exercises.indices { doc.exercises[i].order = i }
        doc.updatedAt = Date()
        Task { await syncEngine.save(routine: doc) }
    }
}

/// One exercise row: name over a one-line plan summary (`ExerciseDocument+PlanSummary`
/// parity via `Exercise.planSummary`).
private struct ExerciseRow: View {
    let exercise: Exercise
    let weightUnit: WeightUnit

    var body: some View {
        VStack(alignment: .leading, spacing: 2) {
            Text(exercise.name)
            Text(exercise.planSummary(weightUnit: weightUnit))
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding(.vertical, 2)
    }
}
