import Foundation
import SharedModels
import SwiftData

/// Cloud-context CRUD for `ExtraStudyMark`. Uniqueness is enforced in code via
/// fetch-or-create on `id` (CloudKit forbids `@Attribute(.unique)`).
struct ExtraStudyStore {
    let context: ModelContext

    private func fetchMark(id: String) -> ExtraStudyMark? {
        let descriptor = FetchDescriptor<ExtraStudyMark>(
            predicate: #Predicate<ExtraStudyMark> { $0.id == id }
        )
        return (try? context.fetch(descriptor))?.first
    }

    func contains(card: VocabCard) -> Bool {
        fetchMark(id: CourseCardStore.reviewKey(for: card)) != nil
    }

    /// Toggle a mark for the given card. Returns the new "is marked" state.
    @discardableResult
    func toggle(
        card: VocabCard,
        courseName: String,
        weekNumber: Int
    ) -> Bool {
        let id = CourseCardStore.reviewKey(for: card)
        if let existing = fetchMark(id: id) {
            context.delete(existing)
            try? context.save()
            return false
        }
        let mark = ExtraStudyMark(
            id: id,
            deckId: card.deckId,
            courseName: courseName,
            weekNumber: weekNumber,
            front: card.front,
            back: card.back
        )
        context.insert(mark)
        try? context.save()
        return true
    }

    func count(courseName: String, weekNumber: Int) -> Int {
        let descriptor = FetchDescriptor<ExtraStudyMark>(
            predicate: #Predicate<ExtraStudyMark> {
                $0.courseName == courseName && $0.weekNumber == weekNumber
            }
        )
        return (try? context.fetchCount(descriptor)) ?? 0
    }

    func countsByWeek(courseName: String) -> [Int: Int] {
        let descriptor = FetchDescriptor<ExtraStudyMark>(
            predicate: #Predicate<ExtraStudyMark> { $0.courseName == courseName }
        )
        let marks = (try? context.fetch(descriptor)) ?? []
        var counts: [Int: Int] = [:]
        for mark in marks {
            counts[mark.weekNumber, default: 0] += 1
        }
        return counts
    }

    func fetch(courseName: String, weekNumber: Int) -> [ExtraStudyMark] {
        let descriptor = FetchDescriptor<ExtraStudyMark>(
            predicate: #Predicate<ExtraStudyMark> {
                $0.courseName == courseName && $0.weekNumber == weekNumber
            },
            sortBy: [SortDescriptor(\.markedAt)]
        )
        return (try? context.fetch(descriptor)) ?? []
    }

    func fetchIds(courseName: String, weekNumber: Int) -> Set<String> {
        Set(fetch(courseName: courseName, weekNumber: weekNumber).map(\.id))
    }
}

/// Lightweight context passed to `VocabFlashcardView` so the in-session star
/// button knows which week/course to attribute a mark to.
struct ExtraStudyMarkContext: Equatable {
    let courseName: String
    let weekNumber: Int
}
