import Foundation
import SwiftData

/// Per-(POS, drillMode) active study group, mirroring `VocabStudyGroup`.
/// Keying by drill mode means a noun gender drill and an adjective agreement
/// drill can each have their own resumable session at the same time.
///
/// CloudKit-synced; uniqueness on `id` is enforced via fetch-or-create.
@Model
final class LexemeStudyGroup {
    var id: String = ""
    var partOfSpeech: String = ""
    var drillMode: String = ""
    /// JSON-encoded `[StoredLexemeEntry]` — the in-session queue in order.
    var entriesJSON: Data = Data()
    var learnedCount: Int = 0
    var createdAt: Date = Date()

    init(
        partOfSpeech: String,
        drillMode: String,
        entriesJSON: Data,
        learnedCount: Int
    ) {
        self.id = Self.activeID(partOfSpeech: partOfSpeech, drillMode: drillMode)
        self.partOfSpeech = partOfSpeech
        self.drillMode = drillMode
        self.entriesJSON = entriesJSON
        self.learnedCount = learnedCount
        self.createdAt = Date()
    }

    static func activeID(partOfSpeech: String, drillMode: String) -> String {
        "active-\(partOfSpeech)-\(drillMode)"
    }

    var entries: [StoredLexemeEntry] {
        (try? JSONDecoder().decode([StoredLexemeEntry].self, from: entriesJSON)) ?? []
    }
}

/// One lexeme's spot in the persisted study group.
struct StoredLexemeEntry: Codable {
    var lexemeId: String
    /// Raw value of `LexemeSessionQueue.CardState`.
    var state: String
}

/// Fetch / persist / clear the active group for one `(POS, drillMode)` pair.
struct LexemeStudyGroupStore {
    let context: ModelContext
    let partOfSpeech: String
    let drillMode: String

    private var activeID: String {
        LexemeStudyGroup.activeID(partOfSpeech: partOfSpeech, drillMode: drillMode)
    }

    func activeGroup() -> LexemeStudyGroup? {
        let id = activeID
        let descriptor = FetchDescriptor<LexemeStudyGroup>(
            predicate: #Predicate<LexemeStudyGroup> { $0.id == id },
            sortBy: [SortDescriptor(\LexemeStudyGroup.createdAt, order: .reverse)]
        )
        return (try? context.fetch(descriptor))?.first
    }

    func persist(entries: [StoredLexemeEntry], learnedCount: Int) {
        let data = (try? JSONEncoder().encode(entries)) ?? Data()
        let id = activeID
        let descriptor = FetchDescriptor<LexemeStudyGroup>(
            predicate: #Predicate<LexemeStudyGroup> { $0.id == id },
            sortBy: [SortDescriptor(\LexemeStudyGroup.createdAt, order: .reverse)]
        )
        let existing = (try? context.fetch(descriptor)) ?? []
        if let newest = existing.first {
            newest.entriesJSON = data
            newest.learnedCount = learnedCount
            for duplicate in existing.dropFirst() { context.delete(duplicate) }
        } else {
            context.insert(LexemeStudyGroup(
                partOfSpeech: partOfSpeech,
                drillMode: drillMode,
                entriesJSON: data,
                learnedCount: learnedCount
            ))
        }
        try? context.save()
    }

    func clear() {
        let id = activeID
        let descriptor = FetchDescriptor<LexemeStudyGroup>(
            predicate: #Predicate<LexemeStudyGroup> { $0.id == id }
        )
        for group in (try? context.fetch(descriptor)) ?? [] {
            context.delete(group)
        }
        try? context.save()
    }
}
