import SwiftUI
import SwiftData

/// A schedule flagged for deletion — captured as plain values (id + a display name),
/// not a retained `@Model`. Reading a persisted property on a since-deleted entity
/// traps (the repo's known crash class), so the confirmation dialog reads only these
/// captured strings and re-resolves the live schedule by id at delete time. Mirrors
/// `MacContentView.PendingRoutineDelete`.
private struct PendingScheduleDelete: Identifiable {
    let id: String
    let name: String
}

/// A schedule flagged for editing — captured as just its id, not a retained `@Model`
/// (see `PendingScheduleDelete`). The sheet re-resolves the live schedule by id 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 PendingScheduleEdit: Identifiable {
    let id: String
}

/// The Schedules management pane: every `Schedules/<ULID>.json` document as a row
/// (routine name, recurrence summary, reminder time, goal tag), with add/edit/delete.
/// All writes go through `syncEngine.save(schedule:)` / `delete(schedule:)` — never the
/// SwiftData cache directly. Mac does not schedule notifications; the iPhone's
/// `ReminderScheduler` resyncs from the document once it observes the change. Mirrors
/// iOS `TodayView`'s schedule rows, sans the per-day "today" status (Mac doesn't run
/// workouts).
struct MacSchedulesView: View {
    @Environment(SyncEngine.self) private var syncEngine

    // Mirrors iOS `TodayView`'s schedule ordering.
    @Query(sort: [SortDescriptor(\Schedule.order), SortDescriptor(\Schedule.createdAt)])
    private var schedules: [Schedule]
    @Query(sort: [SortDescriptor(\Routine.order), SortDescriptor(\Routine.name)])
    private var routines: [Routine]

    @State private var showingNewSchedule = false
    @State private var scheduleToEdit: PendingScheduleEdit?
    @State private var pendingDelete: PendingScheduleDelete?

    var body: some View {
        Group {
            if schedules.isEmpty {
                ContentUnavailableView(
                    "No Schedules",
                    systemImage: "calendar",
                    description: Text("Schedule a routine to see it here.")
                )
            } else {
                List(schedules) { schedule in
                    ScheduleRow(schedule: schedule, routine: resolvedRoutine(for: schedule))
                        .contentShape(Rectangle())
                        .onTapGesture(count: 2) { scheduleToEdit = PendingScheduleEdit(id: schedule.id) }
                        .contextMenu {
                            Button("Edit…") { scheduleToEdit = PendingScheduleEdit(id: schedule.id) }
                            Divider()
                            Button("Delete", role: .destructive) {
                                pendingDelete = PendingScheduleDelete(
                                    id: schedule.id,
                                    name: resolvedRoutine(for: schedule)?.name ?? schedule.routineName)
                            }
                        }
                }
            }
        }
        .navigationTitle("Schedules")
        .toolbar {
            ToolbarItem {
                Button {
                    showingNewSchedule = true
                } label: {
                    Label("New Schedule…", systemImage: "plus")
                }
                .help("New Schedule")
            }
        }
        .sheet(isPresented: $showingNewSchedule) {
            MacScheduleEditorSheet(schedule: nil)
        }
        .sheet(item: $scheduleToEdit) { pending in
            if let schedule = schedules.first(where: { $0.id == pending.id }), schedule.isLive {
                MacScheduleEditorSheet(schedule: schedule)
            }
        }
        .confirmationDialog(
            "Delete Schedule?",
            isPresented: Binding(
                get: { pendingDelete != nil },
                set: { if !$0 { pendingDelete = nil } }
            ),
            titleVisibility: .visible,
            presenting: pendingDelete
        ) { pending in
            Button("Delete", role: .destructive) { confirmDelete(pending) }
            Button("Cancel", role: .cancel) { pendingDelete = nil }
        } message: { pending in
            Text("This removes \"\(pending.name)\" from your schedule. The routine itself is not affected.")
        }
    }

    // MARK: - Actions

    /// The live routine this schedule points at, following the seed clone-on-edit
    /// redirect; nil when the routine has been deleted. Mirrors iOS
    /// `TodayView.resolvedRoutine(for:)`.
    private func resolvedRoutine(for schedule: Schedule) -> Routine? {
        let liveID = syncEngine.currentRoutineID(for: schedule.routineID)
        return routines.first { $0.id == liveID }
    }

    private func confirmDelete(_ pending: PendingScheduleDelete) {
        if let schedule = schedules.first(where: { $0.id == pending.id }), schedule.isLive {
            Task { await syncEngine.delete(schedule: schedule) }
        }
        pendingDelete = nil
    }
}

/// One schedule row: a color badge (the routine's own icon/color, falling back to the
/// goal's color — or plain secondary — when the routine is gone), the routine name
/// (falling back to the schedule's denormalized `routineName`), the recurrence summary,
/// an optional goal tag, and the reminder time if one is set.
private struct ScheduleRow: View {
    let schedule: Schedule
    /// The resolved live routine, or nil when it's been deleted — a dangling
    /// `routineID` is handled the same way iOS's `TodayView` handles it: fall back to
    /// the schedule's own denormalized name/goal color rather than showing an error.
    let routine: Routine?

    var body: some View {
        HStack(spacing: 12) {
            Image(systemName: routine?.systemImage ?? "figure.strengthtraining.traditional")
                .font(.title3)
                .foregroundStyle(badgeColor)
                .frame(width: 32, height: 32)
                .background(badgeColor.opacity(0.12), in: RoundedRectangle(cornerRadius: 6))

            VStack(alignment: .leading, spacing: 2) {
                Text(routine?.name ?? schedule.routineName)

                HStack(spacing: 8) {
                    Text(schedule.recurrenceSummary)
                        .font(.caption)
                        .foregroundStyle(.secondary)

                    if let goal = schedule.goalKind {
                        Label(goal.displayName, systemImage: goal.systemImage)
                            .font(.caption)
                            .foregroundStyle(Color.color(from: goal.colorName))
                    }
                }
            }

            Spacer()

            if let minutes = schedule.reminderMinutes {
                Label(Self.timeString(fromMinutes: minutes), systemImage: "bell.fill")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }
        }
        .padding(.vertical, 3)
    }

    private var badgeColor: Color {
        if let routine { return Color.color(from: routine.color) }
        return Color.color(from: schedule.goalKind?.colorName ?? "")
    }

    /// Render minutes-from-midnight (local wall-clock) as a short time string, e.g.
    /// "8:00 AM" — the same interpretation `ScheduleAddEditView` writes on save.
    private static func timeString(fromMinutes minutes: Int) -> String {
        ScheduleEditing.time(fromMinutes: minutes).formattedTime()
    }
}
