//
// ExerciseView.swift
// Workouts
//
// Created by rzen on 7/18/25 at 5:44 PM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//

import SwiftUI
import SwiftData
import Charts

struct ExerciseView: View {
    @Environment(SyncEngine.self) private var sync
    @Environment(\.dismiss) private var dismiss

    let workout: Workout
    let logID: String

    /// Working copy of the parent workout. Editing a log = editing this doc and
    /// re-saving the whole aggregate. Driving the UI from local state (not the
    /// cache entity) keeps rapid set taps from racing the file→cache update.
    @State private var doc: WorkoutDocument
    @State private var progress: Int = 0
    @State private var showingPlanEdit = false
    @State private var showingNotesEdit = false

    let notStartedColor = Color.white
    let completedColor = Color.green

    /// `seedDoc` lets the caller hand over an in-memory document (e.g. the parent's
    /// working copy right after adding an exercise) so the screen doesn't wait on
    /// the file→cache round-trip to find the just-created log.
    init(workout: Workout, logID: String, seedDoc: WorkoutDocument? = nil) {
        self.workout = workout
        self.logID = logID
        _doc = State(initialValue: seedDoc ?? WorkoutDocument(from: workout))
    }

    /// The log being edited within the working doc.
    private var log: WorkoutLogDocument? {
        doc.logs.first { $0.id == logID }
    }

    var body: some View {
        Group {
            if let log {
                content(for: log)
            } else {
                // The just-added log hasn't reached the cache yet; refresh shortly.
                ProgressView()
            }
        }
        .navigationTitle(log?.exerciseName ?? "")
        .sheet(isPresented: $showingPlanEdit) {
            PlanEditView(workout: workout, logID: logID)
        }
        .sheet(isPresented: $showingNotesEdit) {
            NotesEditView(workout: workout, logID: logID)
        }
        .onAppear {
            refreshDocIfNeeded()
            progress = log?.currentStateIndex ?? 0
        }
    }

    @ViewBuilder
    private func content(for log: WorkoutLogDocument) -> some View {
        Form {
            // MARK: - Progress Section
            Section(header: Text("Progress")) {
                LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: max(1, log.sets)), spacing: 4) {
                    ForEach(1...max(1, log.sets), id: \.self) { index in
                        ZStack {
                            let completed = index <= progress
                            let color = completed ? completedColor : notStartedColor
                            RoundedRectangle(cornerRadius: 8)
                                .fill(
                                    LinearGradient(
                                        gradient: Gradient(colors: [color, color.darker(by: 0.2)]),
                                        startPoint: .topLeading,
                                        endPoint: .bottomTrailing
                                    )
                                )
                                .aspectRatio(0.618, contentMode: .fit)
                                .shadow(radius: 2)
                            Text("\(index)")
                                .font(.title)
                                .fontWeight(.bold)
                                .foregroundColor(.primary)
                                .colorInvert()
                        }
                        .onTapGesture {
                            let totalSets = log.sets
                            let isLastTile = index == totalSets
                            let wasAlreadyAtThisProgress = progress == index

                            withAnimation(.easeInOut(duration: 0.2)) {
                                progress = wasAlreadyAtThisProgress ? 0 : index
                            }

                            updateLogStatus()

                            // Tapping the final tile to complete returns to the list.
                            if isLastTile && !wasAlreadyAtThisProgress {
                                dismiss()
                            }
                        }
                    }
                }
            }

            // MARK: - Plan Section (Read-only with Edit button)
            Section {
                PlanTilesView(log: log)
            } header: {
                HStack {
                    Text("Plan")
                    Spacer()
                    Button("Edit") {
                        showingPlanEdit = true
                    }
                    .font(.subheadline)
                    .textCase(.none)
                }
            }

            // MARK: - Notes Section (Read-only with Edit button)
            Section {
                if let notes = log.notes, !notes.isEmpty {
                    Text(notes)
                        .foregroundColor(.primary)
                } else {
                    Text("No notes")
                        .foregroundColor(.secondary)
                        .italic()
                }
            } header: {
                HStack {
                    Text("Notes")
                    Spacer()
                    Button("Edit") {
                        showingNotesEdit = true
                    }
                    .font(.subheadline)
                    .textCase(.none)
                }
            }

            // MARK: - Progress Tracking Chart
            Section(header: Text("Progress Tracking")) {
                WeightProgressionChartView(exerciseName: log.exerciseName)
            }
        }
        // Pull plan/notes edits made in the sheets back into the live doc.
        .onChange(of: showingPlanEdit) { _, presenting in
            if !presenting { refreshDocFromCache() }
        }
        .onChange(of: showingNotesEdit) { _, presenting in
            if !presenting { refreshDocFromCache() }
        }
    }

    // MARK: - Mutations

    private func updateLogStatus() {
        guard let i = doc.logs.firstIndex(where: { $0.id == logID }) else { return }
        doc.logs[i].currentStateIndex = progress

        if progress >= doc.logs[i].sets {
            doc.logs[i].status = WorkoutStatus.completed.rawValue
            doc.logs[i].completed = true
        } else if progress > 0 {
            doc.logs[i].status = WorkoutStatus.inProgress.rawValue
            doc.logs[i].completed = false
        } else {
            doc.logs[i].status = WorkoutStatus.notStarted.rawValue
            doc.logs[i].completed = false
        }

        recomputeWorkoutStatus()
        doc.updatedAt = Date()
        let snapshot = doc
        Task { await sync.save(workout: snapshot) }
    }

    /// Recompute the workout's status/end from its logs.
    private func recomputeWorkoutStatus() {
        let statuses = doc.logs.map { WorkoutStatus(rawValue: $0.status) ?? .notStarted }
        let allCompleted = !statuses.isEmpty && statuses.allSatisfy { $0 == .completed }
        let anyInProgress = statuses.contains { $0 == .inProgress }
        let allNotStarted = statuses.allSatisfy { $0 == .notStarted }

        if allCompleted {
            doc.status = WorkoutStatus.completed.rawValue
            doc.end = Date()
        } else if anyInProgress || !allNotStarted {
            doc.status = WorkoutStatus.inProgress.rawValue
            doc.end = nil
        } else {
            doc.status = WorkoutStatus.notStarted.rawValue
            doc.end = nil
        }
    }

    /// If the requested log isn't in the working doc yet (just-added race), pull a
    /// fresh copy from the cache entity once it catches up.
    private func refreshDocIfNeeded() {
        guard log == nil else { return }
        refreshDocFromCache()
    }

    /// Re-read the workout from the cache to absorb edits made by child sheets
    /// (plan/notes) without clobbering progress edits made here.
    private func refreshDocFromCache() {
        let fresh = WorkoutDocument(from: workout)
        // Preserve the locally edited progress for the open log if the cache lags.
        if let i = fresh.logs.firstIndex(where: { $0.id == logID }),
           let mine = doc.logs.first(where: { $0.id == logID }),
           fresh.logs[i].currentStateIndex != mine.currentStateIndex {
            doc = fresh
            doc.logs[i].currentStateIndex = mine.currentStateIndex
        } else {
            doc = fresh
        }
        if let current = log {
            progress = current.currentStateIndex
        }
    }
}

// MARK: - Plan Tiles View

struct PlanTilesView: View {
    let log: WorkoutLogDocument

    var body: some View {
        if LoadType(rawValue: log.loadType) == .duration {
            // Duration layout: Sets | Duration
            HStack(spacing: 0) {
                PlanTile(label: "Sets", value: "\(log.sets)")
                PlanTile(label: "Duration", value: formattedDuration)
            }
        } else {
            // Weight layout: Sets | Reps | Weight
            HStack(spacing: 0) {
                PlanTile(label: "Sets", value: "\(log.sets)")
                PlanTile(label: "Reps", value: "\(log.reps)")
                PlanTile(label: "Weight", value: "\(log.weight) lbs")
            }
        }
    }

    private var formattedDuration: String {
        let mins = log.durationSeconds / 60
        let secs = log.durationSeconds % 60
        if mins > 0 && secs > 0 {
            return "\(mins)m \(secs)s"
        } else if mins > 0 {
            return "\(mins) min"
        } else if secs > 0 {
            return "\(secs) sec"
        } else {
            return "0 sec"
        }
    }
}

struct PlanTile: View {
    let label: String
    let value: String

    var body: some View {
        VStack(spacing: 4) {
            Text(label)
                .font(.caption)
                .foregroundColor(.secondary)
            Text(value)
                .font(.title2)
                .fontWeight(.semibold)
                .foregroundColor(.primary)
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 12)
        .background(Color(.systemGray6))
        .cornerRadius(8)
    }
}
