import Foundation
import SwiftData

/// SyncService handles upserting API DTOs into the local SwiftData cache.
/// All methods are @MainActor because ModelContext must be used on the main actor.
@MainActor
final class SyncService {
    static let shared = SyncService()

    private let api = BookStackAPI.shared

    private init() {}

    // MARK: - Sync Shelves

    func syncShelves(context: ModelContext) async throws {
        let dtos = try await api.fetchShelves()
        for dto in dtos {
            let id = dto.id
            let descriptor = FetchDescriptor<CachedShelf>(
                predicate: #Predicate { $0.id == id }
            )
            if let existing = try context.fetch(descriptor).first {
                existing.name = dto.name
                existing.shelfDescription = dto.description
                existing.coverURL = dto.cover?.url
                existing.lastFetched = Date()
            } else {
                context.insert(CachedShelf(from: dto))
            }
        }
        try context.save()
    }

    // MARK: - Sync Books

    func syncBooks(context: ModelContext) async throws {
        let dtos = try await api.fetchBooks()
        for dto in dtos {
            let id = dto.id
            let descriptor = FetchDescriptor<CachedBook>(
                predicate: #Predicate { $0.id == id }
            )
            if let existing = try context.fetch(descriptor).first {
                existing.name = dto.name
                existing.bookDescription = dto.description
                existing.coverURL = dto.cover?.url
                existing.lastFetched = Date()
            } else {
                context.insert(CachedBook(from: dto))
            }
        }
        try context.save()
    }

    // MARK: - Sync Page (on demand, after viewing)

    func cachePageContent(_ dto: PageDTO, context: ModelContext) throws {
        let id = dto.id
        let descriptor = FetchDescriptor<CachedPage>(
            predicate: #Predicate { $0.id == id }
        )
        if let existing = try context.fetch(descriptor).first {
            existing.html = dto.html
            existing.markdown = dto.markdown
            existing.lastFetched = Date()
        } else {
            context.insert(CachedPage(from: dto))
        }
        try context.save()
    }

    // MARK: - Full sync

    func syncAll(context: ModelContext) async throws {
        async let shelvesTask: Void = syncShelves(context: context)
        async let booksTask: Void = syncBooks(context: context)
        _ = try await (shelvesTask, booksTask)
    }

    // MARK: - Clear all cached content

    func clearAllCache(context: ModelContext) throws {
        try context.delete(model: CachedShelf.self)
        try context.delete(model: CachedBook.self)
        try context.delete(model: CachedPage.self)
        try context.save()
        UserDefaults.standard.removeObject(forKey: "lastSynced")
        AppLog(.info, "Cleared all cached content", category: "SyncService")
    }
}
