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

import IndieSync
import SwiftUI
import SwiftData
import UserNotifications

/// The UI-only "When" choice — a superset of `ScheduleRecurrence` with one extra
/// option, `.now`, that never gets persisted: it skips the schedule document
/// entirely and hands the routine straight back to the caller to start immediately.
private enum WhenChoice: String, CaseIterable, Identifiable {
    case now, once, daily, fixedDays

    var id: String { rawValue }

    var displayName: String {
        switch self {
        case .now: "Now"
        case .once: "Once"
        case .daily: "Daily"
        case .fixedDays: "Fixed Days"
        }
    }

    /// The persisted recurrence this choice maps to, or nil for `.now` (never persisted).
    var recurrence: ScheduleRecurrence? {
        switch self {
        case .now: nil
        case .once: .once
        case .daily: .daily
        case .fixedDays: .fixedDays
        }
    }

    init(recurrence: ScheduleRecurrence) {
        switch recurrence {
        case .once: self = .once
        case .daily: self = .daily
        case .fixedDays: self = .fixedDays
        }
    }
}

/// Add / edit a workout plan: pick a routine, tag it with a goal, and choose when it
/// happens — right now (unscheduled, adding only), once, daily, or on fixed days.
/// Fields are snapshotted in `init` so the sheet never holds the live entity.
struct ScheduleAddEditView: View {
    @Environment(SyncEngine.self) private var sync
    @Environment(\.modelContext) private var modelContext
    @Environment(\.dismiss) private var dismiss

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

    /// nil id → add mode; a value → edit mode (id / order / createdAt preserved).
    @State private var scheduleID: String?
    @State private var order: Int
    @State private var createdAt: Date
    @State private var routineID: String
    /// The schedule's routine name when it was created — surfaced when that routine
    /// has since been deleted so the user knows what they're reassigning.
    @State private var originalRoutineName: String
    @State private var goal: GoalKind?
    @State private var when: WhenChoice
    @State private var weekdays: Set<Int>
    @State private var date: Date
    @State private var reminderEnabled: Bool
    @State private var reminderTime: Date
    /// Notification permission has been explicitly denied — surfaced as a footer so
    /// the user knows reminders won't arrive until re-enabled in Settings. Checked
    /// async on appear; never blocks the form.
    @State private var notificationsDenied = false

    /// "Now" starts a workout immediately instead of saving a schedule — only offered
    /// when adding (not editing) for today's default date, since a past/future day
    /// can't be started "now".
    private let offersNow: Bool

    /// Called (after dismiss) when the user picks "Now" and taps Start Workout, with
    /// the routine to start. The Today board starts it and navigates in.
    private let onStartNow: (Routine) -> Void

    /// `defaultDate` seeds the `.once` day when adding — the Today board passes the day
    /// currently on screen, so "navigate to a day, tap +" plans a one-off for that day.
    init(schedule: Schedule? = nil, defaultDate: Date = Date(),
         onStartNow: @escaping (Routine) -> Void = { _ in }) {
        _scheduleID = State(initialValue: schedule?.id)
        _order = State(initialValue: schedule?.order ?? 0)
        _createdAt = State(initialValue: schedule?.createdAt ?? Date())
        _routineID = State(initialValue: schedule?.routineID ?? "")
        _originalRoutineName = State(initialValue: schedule?.routineName ?? "")
        _goal = State(initialValue: schedule?.goalKind)
        _weekdays = State(initialValue: Set(schedule?.weekdays ?? []))
        _date = State(initialValue: schedule?.date ?? defaultDate)
        _reminderEnabled = State(initialValue: schedule?.reminderMinutes != nil)
        _reminderTime = State(initialValue: ScheduleEditing.time(fromMinutes: schedule?.reminderMinutes))
        self.onStartNow = onStartNow

        let offersNow = schedule == nil && defaultDate.isSameDay(as: Date())
        self.offersNow = offersNow
        if let schedule {
            _when = State(initialValue: WhenChoice(recurrence: schedule.recurrenceEnum))
        } else {
            _when = State(initialValue: offersNow ? .now : .once)
        }
    }

    private var isEditing: Bool { scheduleID != nil }

    /// The live routine currently selected, following the seed clone-on-edit redirect
    /// (a schedule made against a starter seed keeps the retired seed id after the
    /// routine is edited); nil when the routine has been deleted.
    private var selectedRoutine: Routine? {
        guard !routineID.isEmpty else { return nil }
        let liveID = sync.currentRoutineID(for: routineID)
        return routines.first { $0.id == liveID }
    }

    /// The When choices this presentation offers — "Now" only when adding on today.
    /// Built as a list (rather than conditionally hiding a tag inside the Picker)
    /// so the segmented control renders a clean 3- or 4-segment strip.
    private var availableChoices: [WhenChoice] {
        offersNow ? WhenChoice.allCases : WhenChoice.allCases.filter { $0 != .now }
    }

    private var canSave: Bool {
        guard selectedRoutine != nil else { return false }
        if when == .fixedDays { return !weekdays.isEmpty }
        return true
    }

    var body: some View {
        NavigationStack {
            Form {
                whenSection
                routineSection
                if when != .now {
                    goalSection
                    reminderSection
                }
            }
            .navigationTitle(isEditing ? "Edit Workout" : "New Workout")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button("Cancel") { dismiss() }
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(when == .now ? "Start Workout" : "Save", action: save)
                        .disabled(!canSave)
                }
            }
            .onAppear {
                // Default to the first routine when adding (the @Query isn't populated at init).
                if routineID.isEmpty { routineID = routines.first?.id ?? "" }
            }
            .task {
                // Surface a denied permission as a footer note — informational only.
                let settings = await UNUserNotificationCenter.current().notificationSettings()
                notificationsDenied = settings.authorizationStatus == .denied
            }
        }
    }

    // MARK: - Sections

    private var routineSection: some View {
        Section {
            NavigationLink {
                RoutinePickerList(routines: routines, currentID: selectedRoutine?.id,
                                  selectedID: $routineID)
            } label: {
                // The row is the selection's preview — the routine's own symbol and
                // color, matching the picker list's rows.
                if let routine = selectedRoutine {
                    HStack(spacing: 12) {
                        Image(systemName: routine.systemImage)
                            .font(.title3)
                            .foregroundStyle(Color.color(from: routine.color))
                            .frame(width: 32)
                        Text(routine.name)
                            .foregroundStyle(.primary)
                    }
                } else {
                    Text("Please select")
                        .foregroundStyle(.secondary)
                }
            }
        } header: {
            Text("Routine")
        } footer: {
            if selectedRoutine == nil, !originalRoutineName.isEmpty {
                Text("\"\(originalRoutineName)\" is no longer available. Choose a routine.")
            }
        }
    }

    private var goalSection: some View {
        Section {
            Picker("Goal", selection: $goal) {
                // A hidden symbol placeholder keeps "None" aligned with the
                // symbol-bearing goal rows below it.
                Label {
                    Text("None")
                } icon: {
                    Image(systemName: "tag").hidden()
                }
                .tag(GoalKind?.none)
                ForEach(GoalKind.orderedCases()) { kind in
                    Label(kind.displayName, systemImage: kind.systemImage)
                        .foregroundStyle(Color.color(from: kind.colorName))
                        .tag(GoalKind?.some(kind))
                }
            }
            .pickerStyle(.inline)
            .labelsHidden()
        } header: {
            Text("Goal")
        }
    }

    @ViewBuilder
    private var whenSection: some View {
        Section {
            Picker("When", selection: $when) {
                ForEach(availableChoices) { choice in
                    Text(choice.displayName).tag(choice)
                }
            }
            .pickerStyle(.segmented)

            switch when {
            case .now, .daily:
                EmptyView()
            case .once:
                DatePicker("Date", selection: $date, displayedComponents: .date)
            case .fixedDays:
                weekdayPicker
            }
        } header: {
            Text("When")
        }
    }

    private var reminderSection: some View {
        Section {
            Toggle("Remind Me", isOn: $reminderEnabled)
            if reminderEnabled {
                DatePicker("Time", selection: $reminderTime, displayedComponents: .hourAndMinute)
            }
        } header: {
            Text("Reminder")
        } footer: {
            if reminderEnabled && notificationsDenied {
                Text("Notifications are turned off for Workouts. Reminders won't arrive until you enable them in Settings.")
            }
        }
    }

    /// A row of toggleable weekday chips, Monday-first.
    private var weekdayPicker: some View {
        HStack(spacing: 6) {
            ForEach(ScheduleEditing.weekdayOrder, id: \.self) { n in
                let on = weekdays.contains(n)
                Button {
                    if on { weekdays.remove(n) } else { weekdays.insert(n) }
                } label: {
                    Text(ScheduleEditing.weekdaySymbols[n - 1])
                        .font(.caption.weight(.semibold))
                        .frame(maxWidth: .infinity)
                        .padding(.vertical, 8)
                        .background(on ? Color.accentColor : Color.secondary.opacity(0.15), in: Capsule())
                        .foregroundStyle(on ? Color.white : Color.primary)
                }
                .buttonStyle(.plain)
            }
        }
        .padding(.vertical, 4)
    }

    // MARK: - Save

    private func save() {
        guard let routine = selectedRoutine else { return }

        // "Now" never persists a schedule — dismiss and hand the routine back so the
        // Today board starts it immediately (mirroring its own start path).
        guard let recurrence = when.recurrence else {
            dismiss()
            onStartNow(routine)
            return
        }

        let doc = ScheduleDocument(
            schemaVersion: ScheduleDocument.currentSchemaVersion,
            id: scheduleID ?? ULID.make(),
            routineID: routine.id,
            routineName: routine.name,
            goal: goal?.rawValue,
            recurrence: recurrence.rawValue,
            weekdays: recurrence == .fixedDays ? weekdays.sorted() : nil,
            date: recurrence == .once ? date : nil,
            reminderMinutes: reminderEnabled ? ScheduleEditing.minutesFromMidnight(of: reminderTime) : nil,
            order: isEditing ? order : ScheduleEditing.nextOrder(in: modelContext),
            createdAt: createdAt,
            updatedAt: Date()
        )
        Task { await sync.save(schedule: doc) }
        dismiss()
    }
}

// MARK: - Routine picker

/// The pushed routine-selection list: each routine with its own symbol and color
/// (the Today board's row treatment), a checkmark on the current selection.
/// Tapping selects and pops back.
private struct RoutinePickerList: View {
    let routines: [Routine]
    /// The resolved live id of the current selection (for the checkmark) — may differ
    /// from the binding's stored value when a seed clone-on-edit redirect is in play.
    let currentID: String?
    @Binding var selectedID: String
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        List(routines) { routine in
            Button {
                selectedID = routine.id
                dismiss()
            } label: {
                HStack(spacing: 12) {
                    Image(systemName: routine.systemImage)
                        .font(.title3)
                        .foregroundStyle(Color.color(from: routine.color))
                        .frame(width: 32)
                    Text(routine.name)
                        .foregroundStyle(.primary)
                    Spacer()
                    if routine.id == currentID {
                        Image(systemName: "checkmark")
                            .fontWeight(.semibold)
                            .foregroundStyle(Color.accentColor)
                    }
                }
                .contentShape(Rectangle())
            }
            .buttonStyle(.plain)
        }
        .navigationTitle("Routine")
        .navigationBarTitleDisplayMode(.inline)
    }
}
