-- allium: 1
-- bDS Liquid Template Context Specification
-- Scope: core (Wave 4 — rendering parity)
-- Distilled from: ../bDS2/src/main/engine/GenerationRouteRendererFactory.ts,
--   PageRenderer.ts, BlogGenerationEngine.ts
--
-- This document specifies the exact data structure passed to Liquid templates
-- during rendering. It is the contract between the generation engine and
-- template authors.

-- ============================================================================
-- GLOBAL TEMPLATE VARIABLES
-- ============================================================================

surface TemplateRenderingSurface {
    facing _: RenderPipeline

    provides:
        RenderPostPageRequested(post, language)
        RenderListPageRequested(posts, pagination, archive_context)
        RenderNotFoundPageRequested()
}

surface RenderContextSurface {
    context context: RenderContext

    exposes:
        context.language
        context.language_prefix
        context.page_title
        context.pico_stylesheet_href
        context.blog_languages
        context.alternate_links
        context.menu_items
        context.post
        context.day_blocks
        context.archive_context
        context.is_list_page
        context.prev_page_href
        context.next_page_href
        context.not_found_message
}

surface PaginationContextSurface {
    context pagination: PaginationContext

    exposes:
        pagination.is_first_page
        pagination.is_last_page
        pagination.has_prev_page
        pagination.has_next_page
        pagination.prev_page_href
        pagination.next_page_href
        pagination.current_page
        pagination.total_pages
        pagination.total_items
        pagination.items_per_page
}

value RenderContext {
    -- Top-level variables available in all templates
    language: String                -- Current language code
    language_prefix: String?        -- "/de" or "" depending on language
    html_theme_attribute: String    -- Theme class for <html> element
    page_title: String              -- Blog title (description, else name) for the <title> tag; archive-specific labels come from archive_context
    pico_stylesheet_href: String    -- Path to Pico CSS theme
    blog_languages: List<BlogLanguage>
    alternate_links: List<AlternateLink>
    menu_items: List<MenuItem>
    calendar_initial_year: Integer
    calendar_initial_month: Integer
    post: PostContext?              -- Present on single post pages
    post_categories: List<Category>
    post_tags: List<Tag>
    tag_color_by_name: Map<String, String>
    backlinks: List<Backlink>
    day_blocks: List<DayBlock>
    archive_context: ArchiveContext?
    show_archive_range_heading: Boolean
    min_date: Timestamp?
    max_date: Timestamp?
    is_list_page: Boolean
    is_first_page: Boolean
    is_last_page: Boolean
    has_prev_page: Boolean
    has_next_page: Boolean
    prev_page_href: String?
    next_page_href: String?
    not_found_message: String?
    not_found_back_label: String?
    
    -- Lookup maps for macro expansion
    canonical_post_path_by_slug: Map<String, String>
    canonical_media_path_by_source_path: Map<String, String>
    post_data_json_by_id: Map<String, PostDataJson>
}

value BlogLanguage {
    is_current: Boolean
    code: String
    flag: String
    href: String
    href_prefix: String
}

value AlternateLink {
    href: String
    hreflang: String
}

value MenuItem {
    href: String
    title: String
    has_children: Boolean
    children: List<MenuItem>?
}

-- ============================================================================
-- POST CONTEXT (Single Post Pages)
-- ============================================================================

value PostContext {
    id: String
    title: String
    content: String         -- Rendered HTML (after markdown + macros)
    slug: String
    excerpt: String?
    author: String?
    language: String?
    show_title: Boolean     -- True for post pages; in list/archive pages it is false when any of the post's categories has "show title" disabled (e.g. asides)
    published_at: Timestamp
    created_at: Timestamp
    updated_at: Timestamp
    tags: List<String>
    categories: List<String>
    template_slug: String?
    do_not_translate: Boolean
    linked_media: List<MediaContext>
    outgoing_links: List<LinkContext>
    incoming_links: List<LinkContext>
}

value PostDataJson {
    -- Serialized post data for Liquid template access
    id: String
    title: String
    slug: String
    excerpt: String?
    author: String?
    language: String?
    published_at: Timestamp
    created_at: Timestamp
    updated_at: Timestamp
    tags: List<String>
    categories: List<String>
}

value MediaContext {
    id: String
    filename: String
    original_name: String
    mime_type: String
    title: String?
    alt: String?
    caption: String?
    author: String?
    width: Integer?
    height: Integer?
    file_path: String
}

value LinkContext {
    href: String
    title: String
    display_slug: String
}

-- ============================================================================
-- ARCHIVE CONTEXT (List Pages)
-- ============================================================================

value ArchiveContext {
    kind: category | tag | date
    name: String?           -- Heading label: for categories the configured descriptive title (falls back to the category key), for tags the raw tag name
    month: Integer?
    year: Integer?
    day: Integer?
}

value DayBlock {
    show_date_marker: Boolean
    date_label: String
    posts: List<PostContext>
    show_separator: Boolean
}

value Category {
    name: String
    slug: String
    post_count: Integer
}

value Tag {
    name: String
    slug: String
    color: String?
    post_count: Integer
}

value Backlink {
    path: String
    display_slug: String
    title: String
}

-- ============================================================================
-- PAGINATION CONTEXT
-- ============================================================================

value PaginationContext {
    is_list_page: Boolean
    is_first_page: Boolean
    is_last_page: Boolean
    has_prev_page: Boolean
    has_next_page: Boolean
    prev_page_href: String?
    next_page_href: String?
    current_page: Integer
    total_pages: Integer
    total_items: Integer
    items_per_page: Integer
}

-- ============================================================================
-- LIQUID FILTER SPECIFICATION
-- Note: Allium does not have a 'filter' keyword. Filters are documented here
-- for reference but are implemented in the template engine, not in Allium specs.
-- ============================================================================

-- Built-in filters:
--   default: {{ value | default: "fallback" }}
--   escape: {{ text | escape }}
--   url_encode: {{ text | url_encode }}
--   append: {{ text | append: suffix }}
--
-- Custom filters:
--   i18n: {{ "key" | i18n: language }} - translation lookup
--   markdown: {{ content | markdown: ... }} - markdown rendering with macro expansion

-- ============================================================================
-- BUILT-IN MACROS
-- Note: Allium does not have a 'macro' keyword. Macros are documented here
-- for reference but are implemented by the rendering subsystem.
-- ============================================================================

-- Built-in macros:
--   gallery: [[gallery images=post.linked_media columns=3]]
--   youtube: [[youtube id=dQw4w9WgXcQ]]
--   vimeo: [[vimeo id=123456789]]
--   photo_archive: [[photo_archive media=project.media]]
--   tag_cloud: [[tag_cloud tags=Tags]]

-- ============================================================================
-- TEMPLATE LOOKUP RULES
-- ============================================================================

invariant TemplateLookupPriority {
    -- Templates are resolved in this order:
    -- 1. Post-specific template (post.template_slug)
    -- 2. Tag-specific template (tag.post_template_slug)
    -- 3. Category-specific template (category.post_template_slug)
    -- 4. Default "post" template
    --
    -- List pages use "list" template
    -- 404 pages use "not_found" template
    -- Partials are referenced via {% render 'partial' %}
}

invariant PartialResolution {
    -- Partials are looked up in templates/ directory
    -- Usage: {% render 'partials/header', context_var: value %}
    -- Partial files: templates/partials/{name}.liquid
}

-- ============================================================================
-- RENDERING RULES
-- ============================================================================

rule RenderPostPage {
    when: RenderPostPageRequested(post, language)
    let template = resolve_template(post)
    let context = BuildPostContext(post, language)
    ensures: RenderedHtml = liquid_render(template.content, context)
}

rule RenderListPage {
    when: RenderListPageRequested(posts, pagination, archive_context)
    let template = first (t in Templates where t.kind = "list" and t.enabled)
    let context = BuildListContext(posts, pagination, archive_context)
    ensures: RenderedHtml = liquid_render(template.content, context)
}

rule RenderNotFoundPage {
    when: RenderNotFoundPageRequested()
    let template = first (t in Templates where t.kind = "not_found" and t.enabled)
    let context = BuildNotFoundContext
    ensures: RenderedHtml = liquid_render(template.content, context)
}
