import Foundation
import IndieSync
import SwiftData

/// Document ↔ cache-entity mapping. Nothing else in the app knows both
/// shapes. `upsert` is used only by the metadata observer and cache
/// rebuilds — app code never writes the cache directly.
enum NoteMapper {
    static func fetch(id: String, in context: ModelContext) -> NoteEntity? {
        var descriptor = FetchDescriptor<NoteEntity>(predicate: #Predicate { $0.id == id })
        descriptor.fetchLimit = 1
        return try? context.fetch(descriptor).first
    }

    static func fetch(jsonRelativePath path: String, in context: ModelContext) -> [NoteEntity] {
        let descriptor = FetchDescriptor<NoteEntity>(predicate: #Predicate { $0.jsonRelativePath == path })
        return (try? context.fetch(descriptor)) ?? []
    }

    static func upsert(_ note: Note, relativePath: String, into context: ModelContext) {
        let idString = note.meta.id.stringValue
        let entity: NoteEntity
        if let existing = fetch(id: idString, in: context) {
            entity = existing
        } else {
            entity = NoteEntity(id: idString, jsonRelativePath: relativePath)
            context.insert(entity)
        }
        // Always overwrite, not just on insert — the document just read is at
        // least as fresh as whatever the cache last held.
        entity.text = note.payload.text
        entity.sourceRaw = note.payload.source.rawValue
        entity.tags = note.payload.tags
        entity.project = note.payload.project
        entity.createdAt = note.meta.createdAt
        entity.modifiedAt = note.meta.modifiedAt
        entity.jsonRelativePath = relativePath
        entity.context = note.payload.context
        entity.audio = note.payload.audio
    }

    /// Near-lossless reconstruction from the cache, used only as the offline
    /// fallback when the authoritative file can't be read.
    static func note(from entity: NoteEntity) -> Note? {
        guard let id = ULID(string: entity.id) else { return nil }
        return Note(
            meta: Note.Meta(
                id: id,
                schemaVersion: Note.currentSchemaVersion,
                createdAt: entity.createdAt,
                modifiedAt: entity.modifiedAt
            ),
            payload: Note.Payload(
                text: entity.text,
                source: entity.source,
                tags: entity.tags,
                context: entity.context,
                audio: entity.audio,
                project: entity.project
            )
        )
    }
}
