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

import SwiftUI
import SwiftData

/// The Progress tab — observe by goals (UX-REDESIGN.md). Per-goal adherence tracks
/// first, then this-week totals, aggregate trends, per-exercise drill-down, the
/// achievements grid, and full history (absorbed from the old Log tab). Everything on
/// this screen is derived by `ProgressPlanner` from the workout documents; nothing is
/// persisted, so it all survives cache rebuilds.
struct ProgressTabView: View {
    /// Screenshot-capture affordance: `.bottom` opens the tab pre-scrolled to the
    /// end (nil = the normal top). Only the DEBUG screenshot root passes it.
    var initialAnchor: UnitPoint?

    @Environment(SyncEngine.self) private var sync

    @Query(sort: [SortDescriptor(\Schedule.order), SortDescriptor(\Schedule.createdAt)])
    private var schedules: [Schedule]

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

    /// Motivational openers for the tab title — each reads as a phrase completed
    /// by "Progress" ("See Your Progress"), mirroring the Today board's rotating
    /// greeting. One per day, rotating by day-of-era: stable all day, fresh the next.
    private static let greetings: [String] = [
        "See Your",
        "Own Your",
        "Earn Your",
        "Trust Your",
        "Watch Your",
        "Feel the",
        "Proof of",
        "Chase",
        "Celebrate",
        "Measure Your",
        "Honor Your",
        "Fuel Your",
    ]

    private var greetingLine: String {
        let day = Calendar.current.ordinality(of: .day, in: .era, for: Date()) ?? 0
        return Self.greetings[day % Self.greetings.count]
    }

    var body: some View {
        NavigationStack {
            ScrollView {
                let facts = workoutFacts
                let tracks = ProgressPlanner.goalTracks(
                    schedules: scheduleFacts, workouts: facts,
                    goalOrder: GoalKind.orderedCases())

                VStack(alignment: .leading, spacing: 16) {
                    WeekSummaryTiles(workouts: facts)

                    if tracks.isEmpty {
                        goalTracksHint
                    } else {
                        ForEach(tracks) { GoalTrackCard(track: $0) }
                    }

                    TrendsSection(workouts: facts)

                    exerciseTrendsLink

                    AchievementsSection(
                        achievements: ProgressPlanner.achievements(tracks: tracks, workouts: facts))

                    historySection
                }
                .padding(.horizontal)
                .padding(.bottom, 24)
            }
            .defaultScrollAnchor(initialAnchor)
            .background(Color(.systemGroupedBackground))
            .navigationTitle("\(greetingLine) Progress")
        }
    }

    // MARK: - Fact building (live-resolve IDs through the seed clone-on-edit redirect)

    private var scheduleFacts: [ScheduleFacts] {
        schedules.map { schedule in
            ScheduleFacts(
                id: schedule.id,
                goal: schedule.goalKind,
                recurrence: schedule.recurrenceEnum,
                weekdays: schedule.weekdays,
                onceDate: schedule.date,
                createdAt: schedule.createdAt,
                routineID: sync.currentRoutineID(for: schedule.routineID),
                routineName: schedule.routineName)
        }
    }

    private var workoutFacts: [WorkoutFacts] {
        workouts.map { workout in
            WorkoutFacts(
                routineID: workout.routineID.map { sync.currentRoutineID(for: $0) },
                start: workout.start,
                end: workout.end,
                completed: workout.status == .completed,
                volume: Self.volume(of: workout),
                activeMinutes: workout.end.map { max(0, $0.timeIntervalSince(workout.start) / 60) } ?? 0,
                energyKcal: workout.metricActiveEnergyKcal ?? 0)
        }
    }

    /// Total lifted volume for one workout: the recorded watch metric when present,
    /// else derived from per-set actuals, else the plan (the same fallback ladder the
    /// documents' `effectiveSetEntries` uses).
    static func volume(of workout: Workout) -> Double {
        if let recorded = workout.metricTotalVolume { return recorded }
        return workout.logs.reduce(0) { sum, log in
            guard log.status == .completed, log.loadTypeEnum == .weight else { return sum }
            if let entries = log.setEntries, !entries.isEmpty {
                return sum + entries.reduce(0) { $0 + Double($1.reps ?? 0) * ($1.weight ?? 0) }
            }
            return sum + Double(log.sets * log.reps) * log.weight
        }
    }

    // MARK: - Sections

    private var goalTracksHint: some View {
        VStack(alignment: .leading, spacing: 6) {
            Label("No Goal Tracks Yet", systemImage: "chart.bar.fill")
                .font(.headline)
            Text("Add schedules on the Today tab and each goal builds a weekly adherence track here.")
                .font(.subheadline)
                .foregroundStyle(.secondary)
        }
        .progressCard()
    }

    private var exerciseTrendsLink: some View {
        NavigationLink {
            ExerciseTrendsListView()
        } label: {
            HStack(spacing: 12) {
                Image(systemName: "chart.line.uptrend.xyaxis")
                    .font(.title3)
                    .foregroundStyle(.blue)
                    .frame(width: 32)
                VStack(alignment: .leading, spacing: 2) {
                    Text("Exercise Trends")
                        .font(.headline)
                        .foregroundStyle(.primary)
                    Text("Weight progression for every exercise you've logged")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
                Spacer()
                Image(systemName: "chevron.right")
                    .font(.footnote.weight(.semibold))
                    .foregroundStyle(.tertiary)
            }
            .progressCard()
        }
        .buttonStyle(.plain)
    }

    private var historySection: some View {
        VStack(alignment: .leading, spacing: 12) {
            HStack {
                Text("History")
                    .font(.headline)
                Spacer()
                NavigationLink("All Workouts") {
                    WorkoutHistoryView()
                }
                .font(.subheadline)
            }
            if workouts.isEmpty {
                Text("No workouts yet. Start one from the Today tab.")
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            } else {
                ForEach(workouts.prefix(3)) { workout in
                    NavigationLink {
                        WorkoutLogListView(workout: workout)
                    } label: {
                        HStack {
                            CalendarListItem(
                                date: workout.start,
                                title: workout.routineName ?? Routine.unnamed,
                                subtitle: subtitle(for: workout),
                                subtitle2: workout.statusName)
                            Spacer()
                            Image(systemName: "chevron.right")
                                .font(.footnote.weight(.semibold))
                                .foregroundStyle(.tertiary)
                        }
                        .contentShape(Rectangle())
                    }
                    .buttonStyle(.plain)
                }
            }
        }
        .progressCard()
    }

    private func subtitle(for workout: Workout) -> String {
        if workout.status == .completed, let endDate = workout.end {
            return workout.start.humanTimeInterval(to: endDate)
        }
        return workout.start.formattedDate()
    }
}

// MARK: - Shared card chrome

extension View {
    /// The Progress tab's card treatment — one shared background so every section
    /// reads as part of the same system.
    func progressCard() -> some View {
        padding(16)
            .frame(maxWidth: .infinity, alignment: .leading)
            .background(
                Color(.secondarySystemGroupedBackground),
                in: RoundedRectangle(cornerRadius: 16, style: .continuous))
    }
}

// MARK: - This-week stat tiles

/// Three at-a-glance totals for the current calendar week, each with a delta arrow
/// against last week.
struct WeekSummaryTiles: View {
    let workouts: [WorkoutFacts]

    @AppStorage("weightUnit") private var weightUnit: WeightUnit = .lb

    var body: some View {
        let calendar = Calendar.current
        let now = Date()
        let this = totals(in: calendar.dateInterval(of: .weekOfYear, for: now))
        let last = totals(in: calendar.date(byAdding: .weekOfYear, value: -1, to: now)
            .flatMap { calendar.dateInterval(of: .weekOfYear, for: $0) })

        HStack(spacing: 12) {
            ProgressStatTile(
                title: "This Week",
                value: "\(this.workouts)",
                unit: this.workouts == 1 ? "workout" : "workouts",
                delta: Double(this.workouts - last.workouts),
                deltaText: "\(abs(this.workouts - last.workouts))")
            ProgressStatTile(
                title: "Volume",
                value: this.volume.formatted(.number.notation(.compactName).precision(.significantDigits(3))),
                unit: weightUnit.abbreviation,
                delta: this.volume - last.volume,
                deltaText: abs(this.volume - last.volume)
                    .formatted(.number.notation(.compactName).precision(.significantDigits(2))))
            ProgressStatTile(
                title: "Active Time",
                value: Self.hoursMinutes(this.minutes),
                unit: nil,
                delta: this.minutes - last.minutes,
                deltaText: Self.hoursMinutes(abs(this.minutes - last.minutes)))
        }
    }

    private func totals(in interval: DateInterval?) -> (workouts: Int, volume: Double, minutes: Double) {
        guard let interval else { return (0, 0, 0) }
        let matching = workouts.filter { $0.completed && interval.contains($0.start) }
        return (matching.count,
                matching.reduce(0) { $0 + $1.volume },
                matching.reduce(0) { $0 + $1.activeMinutes })
    }

    static func hoursMinutes(_ minutes: Double) -> String {
        let total = Int(minutes.rounded())
        let (h, m) = (total / 60, total % 60)
        return h > 0 ? "\(h)h \(m)m" : "\(m)m"
    }
}

/// One compact stat tile: caption, big value, and a vs-last-week arrow. Shared by the
/// week summary row and the exercise drill-down.
struct ProgressStatTile: View {
    let title: String
    let value: String
    var unit: String?
    /// Signed change vs the comparison period; only its sign is displayed.
    var delta: Double?
    /// Preformatted magnitude for the delta line (callers know their unit best).
    var deltaText: String?

    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(title)
                .font(.caption)
                .foregroundStyle(.secondary)
            HStack(alignment: .firstTextBaseline, spacing: 3) {
                Text(value)
                    .font(.title3.weight(.semibold))
                    .monospacedDigit()
                    .lineLimit(1)
                    .minimumScaleFactor(0.7)
                if let unit {
                    Text(unit)
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            }
            if let delta, delta != 0 {
                Label("\(deltaText ?? fallbackDeltaText(delta)) vs last wk",
                      systemImage: delta > 0 ? "arrow.up" : "arrow.down")
                    .font(.caption2.weight(.medium))
                    .foregroundStyle(delta > 0 ? .green : .secondary)
            } else {
                // Keep tile heights equal whether or not a delta renders.
                Text("—")
                    .font(.caption2)
                    .foregroundStyle(.tertiary)
            }
        }
        .padding(12)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(
            Color(.secondarySystemGroupedBackground),
            in: RoundedRectangle(cornerRadius: 16, style: .continuous))
    }

    private func fallbackDeltaText(_ delta: Double) -> String {
        abs(delta).formatted(.number.notation(.compactName).precision(.significantDigits(2)))
    }
}
