import Foundation
import SwiftData

// SwiftData cache entities. These are a rebuildable read-through cache of the
// iCloud Drive JSON documents — never the source of truth. App code reads them
// (via @Query); all writes go through `SyncEngine` to the document files, and the
// metadata observer mirrors the files back into these entities (see Mappers).
//
// `id` is the aggregate/child ULID (stable across cache rebuilds, unlike the
// SwiftData PersistentIdentifier). Computed helpers preserve the API the views
// used against the old Core Data classes.

// MARK: - Split

@Model
final class Split {
    @Attribute(.unique) var id: String = ULID.make()
    var name: String = ""
    var color: String = "indigo"
    var systemImage: String = "dumbbell.fill"
    var order: Int = 0
    var createdAt: Date = Date()
    var updatedAt: Date = Date()
    var jsonRelativePath: String = ""

    @Relationship(deleteRule: .cascade, inverse: \Exercise.split)
    var exercises: [Exercise] = []

    init(id: String, name: String, color: String, systemImage: String, order: Int,
         createdAt: Date, updatedAt: Date, jsonRelativePath: String) {
        self.id = id
        self.name = name
        self.color = color
        self.systemImage = systemImage
        self.order = order
        self.createdAt = createdAt
        self.updatedAt = updatedAt
        self.jsonRelativePath = jsonRelativePath
    }

    static let unnamed = "Unnamed Split"

    var exercisesArray: [Exercise] { exercises.sorted { $0.order < $1.order } }
}

// MARK: - Exercise

@Model
final class Exercise {
    @Attribute(.unique) var id: String = ULID.make()
    var name: String = ""
    var order: Int = 0
    var sets: Int = 0
    var reps: Int = 0
    var weight: Int = 0
    var loadType: Int = LoadType.weight.rawValue
    var durationTotalSeconds: Int = 0
    var weightLastUpdated: Date?
    var weightReminderTimeIntervalWeeks: Int = 2

    var split: Split?

    init(id: String, name: String, order: Int, sets: Int, reps: Int, weight: Int,
         loadType: Int, durationTotalSeconds: Int, weightLastUpdated: Date?,
         weightReminderTimeIntervalWeeks: Int) {
        self.id = id
        self.name = name
        self.order = order
        self.sets = sets
        self.reps = reps
        self.weight = weight
        self.loadType = loadType
        self.durationTotalSeconds = durationTotalSeconds
        self.weightLastUpdated = weightLastUpdated
        self.weightReminderTimeIntervalWeeks = weightReminderTimeIntervalWeeks
    }

    var loadTypeEnum: LoadType {
        get { LoadType(rawValue: loadType) ?? .weight }
        set { loadType = newValue.rawValue }
    }

    /// Minutes component of the total duration (for min:sec pickers).
    var durationMinutes: Int {
        get { durationTotalSeconds / 60 }
        set { durationTotalSeconds = newValue * 60 + durationSeconds }
    }

    /// Seconds component (0–59) of the total duration.
    var durationSeconds: Int {
        get { durationTotalSeconds % 60 }
        set { durationTotalSeconds = durationMinutes * 60 + newValue }
    }
}

// MARK: - Workout

@Model
final class Workout {
    @Attribute(.unique) var id: String = ULID.make()
    var splitID: String?
    var splitName: String?
    var start: Date = Date()
    var end: Date?
    var statusRaw: String = WorkoutStatus.notStarted.rawValue
    var createdAt: Date = Date()
    var updatedAt: Date = Date()
    var jsonRelativePath: String = ""

    @Relationship(deleteRule: .cascade, inverse: \WorkoutLog.workout)
    var logs: [WorkoutLog] = []

    init(id: String, splitID: String?, splitName: String?, start: Date, end: Date?,
         statusRaw: String, createdAt: Date, updatedAt: Date, jsonRelativePath: String) {
        self.id = id
        self.splitID = splitID
        self.splitName = splitName
        self.start = start
        self.end = end
        self.statusRaw = statusRaw
        self.createdAt = createdAt
        self.updatedAt = updatedAt
        self.jsonRelativePath = jsonRelativePath
    }

    var status: WorkoutStatus {
        get { WorkoutStatus(rawValue: statusRaw) ?? .notStarted }
        set { statusRaw = newValue.rawValue }
    }

    var statusName: String { status.displayName }

    var logsArray: [WorkoutLog] { logs.sorted { $0.order < $1.order } }

    var label: String {
        if status == .completed, let endDate = end {
            if start.isSameDay(as: endDate) {
                return "\(start.formattedDate())—\(endDate.formattedTime())"
            } else {
                return "\(start.formattedDate())—\(endDate.formattedDate())"
            }
        } else {
            return start.formattedDate()
        }
    }
}

// MARK: - WorkoutLog

@Model
final class WorkoutLog {
    @Attribute(.unique) var id: String = ULID.make()
    var exerciseName: String = ""
    var order: Int = 0
    var sets: Int = 0
    var reps: Int = 0
    var weight: Int = 0
    var loadType: Int = LoadType.weight.rawValue
    var durationTotalSeconds: Int = 0
    var currentStateIndex: Int = 0
    var completed: Bool = false
    var statusRaw: String = WorkoutStatus.notStarted.rawValue
    var notes: String?
    var date: Date = Date()

    var workout: Workout?

    init(id: String, exerciseName: String, order: Int, sets: Int, reps: Int, weight: Int,
         loadType: Int, durationTotalSeconds: Int, currentStateIndex: Int, completed: Bool,
         statusRaw: String, notes: String?, date: Date) {
        self.id = id
        self.exerciseName = exerciseName
        self.order = order
        self.sets = sets
        self.reps = reps
        self.weight = weight
        self.loadType = loadType
        self.durationTotalSeconds = durationTotalSeconds
        self.currentStateIndex = currentStateIndex
        self.completed = completed
        self.statusRaw = statusRaw
        self.notes = notes
        self.date = date
    }

    var status: WorkoutStatus {
        get { WorkoutStatus(rawValue: statusRaw) ?? .notStarted }
        set { statusRaw = newValue.rawValue }
    }

    var loadTypeEnum: LoadType {
        get { LoadType(rawValue: loadType) ?? .weight }
        set { loadType = newValue.rawValue }
    }

    var durationMinutes: Int {
        get { durationTotalSeconds / 60 }
        set { durationTotalSeconds = newValue * 60 + durationSeconds }
    }

    var durationSeconds: Int {
        get { durationTotalSeconds % 60 }
        set { durationTotalSeconds = durationMinutes * 60 + newValue }
    }
}
