-- allium: 1
-- bDS Metadata Diff and Rebuild
-- Scope: core (Wave 1)
-- Distilled from: src/main/engine/MetadataDiffEngine.ts

use "./post.allium" as post
use "./media.allium" as media
use "./script.allium" as script
use "./template.allium" as template

surface MetadataMaintenanceSurface {
    facing _: MaintenanceOperator

    provides:
        MetadataDiffRequested(project)
        RebuildFromFilesystemRequested(project, entity_type)
        RepairMetadataDiffItemRequested(project, direction, item)
}

value DiffField {
    name: String
    db_value: String
    file_value: String
}

value DiffReport {
    entity_type: String             -- post, post_translation, media,
                                    --   media_translation, script, template, embedding
    entity_id: String
    differences: List<DiffField>
}

value OrphanReport {
    file_path: String
    -- File exists on disk but has no DB record
}

rule RunMetadataDiff {
    when: MetadataDiffRequested(project)
    -- Runs as background task via TaskManager
    -- Compares DB records against filesystem files for:
    --   posts, translations, media, scripts, templates
    -- Detected fields: tags, categories, title, excerpt, author,
    --   language, status, templateSlug, dates
    for post in project.posts:
        let file_data = parse_post_file(post.file_path)
        let diffs = compare_fields(post, file_data)
        if diffs.count > 0:
            ensures: DiffReport.created(entity_type: "post", entity_id: post.id, differences: diffs)

    -- Translation files only carry language-specific metadata. Shared status and
    -- timestamp fields come from the canonical post and must not be reported as
    -- missing when they are absent from the translation file.
    for translation in project.post_translations:
        let translation_file_data = parse_post_file(translation.file_path)
        let translation_diffs = compare_translation_specific_fields(translation, translation_file_data)
        if translation_diffs.count > 0:
            ensures:
                DiffReport.created(
                    entity_type: "post_translation",
                    entity_id: translation.id,
                    differences: translation_diffs
                )

    -- Detect orphan files (on disk but not in DB)
    for file in scan_directory(project.public_dir + "/posts", "*.md"):
        let matching = Posts where file_path = file
        if matching.count = 0:
            ensures: OrphanReport.created(file_path: file)

    -- Same pattern for media sidecars (media), media translation sidecars
    --   (media_translation), scripts, templates, and embeddings.
    -- Embedding diffs compare the stored content_hash against the live post
    --   content to detect vectors that need recomputation.
}

rule RepairMetadataDiffItem {
    when: RepairMetadataDiffItemRequested(project, direction, item)
    -- Resolves a single diff in one direction.
    -- direction = file_to_db (filesystem wins) | db_to_file (database wins)
    -- Dispatched per item.entity_type:
    --   project | categories | category_meta | publishing -> project metadata sync/flush
    --   post              -> sync_post_from_file / rewrite_published_post
    --   post_translation  -> sync_post_translation_from_file / rewrite_published_post_translation
    --   media             -> sync_media_from_sidecar / sync_media_sidecar
    --   media_translation -> sync_media_translation_from_sidecar / sync_media_translation_sidecar
    --   script            -> sync_script_from_file / sync_published_script_file
    --   template          -> sync_template_from_file / sync_published_template_file
    --   embedding         -> sync_post (file_to_db) / refresh_snapshot (db_to_file)
    -- Unknown entity_type or direction -> unsupported error.
}

rule RebuildFromFilesystem {
    when: RebuildFromFilesystemRequested(project, entity_type)
    -- The inverse of metadata diff: filesystem is treated as truth
    -- Reads all files and upserts into DB
    ensures:
        if entity_type = "post":
            post/RebuildPostsFromFiles(project)
        if entity_type = "media":
            media/RebuildMediaFromFiles(project)
        if entity_type = "script":
            script/RebuildScriptsFromFiles(project)
        if entity_type = "template":
            template/RebuildTemplatesFromFiles(project)
}

invariant ThreeWaySync {
    -- Metadata must stay in sync across three representations:
    --   1. Database records
    --   2. Filesystem files (frontmatter/sidecars)
    --   3. Generated site output
    -- MetadataDiff detects divergence between (1) and (2)
    -- Rebuild resolves divergence by treating (2) as truth
    -- Site generation consumes (1) to produce (3)
}
