<script lang="ts">
interface Props {
    /** @type {import('./$types').PageData} */
    data: any;
    log_lvl?: number;
}

let { data, log_lvl = 0 }: Props = $props();

import { untrack } from 'svelte';
import { liveQuery } from 'dexie';
import { browser } from '$app/environment';

import { fade } from 'svelte/transition';
import { ae_loc, ae_sess } from '$lib/stores/ae_stores';
import { db_events } from '$lib/ae_events/db_events';
import { EVENTS_MODULE_TITLE } from '$lib/stores/ae_events_stores';
import { page } from '$app/state';
import { ArrowLeft, Contact, Eye, LoaderCircle, Mail, PrinterCheck, UserCheck } from '@lucide/svelte';
import { ae_util } from '$lib/ae_utils/ae_utils';
import Comp_badge_obj_view from '../ae_comp__badge_obj_view.svelte';
import Comp_badge_print_controls from '../ae_comp__badge_print_controls.svelte';

let event_badge_id = $derived(page.params.badge_id);
let event_id = $derived(page.params.event_id);

let is_loading_idb = $state(true);

let lq__event_badge_obj = $derived(
    liveQuery(async () => {
        if (!event_badge_id) return null;
        return await db_events.badge.get(event_badge_id);
    })
);

$effect(() => {
    if ($lq__event_badge_obj !== undefined) {
        untrack(() => (is_loading_idb = false));
    }
});

let lq__event_badge_template_obj = $derived(
    liveQuery(async () => {
        return await db_events.badge_template.get(
            $lq__event_badge_obj?.event_badge_template_id ?? ''
        );
    })
);

// Access level shortcuts
let is_trusted = $derived($ae_loc.trusted_access === true);
let is_admin = $derived($ae_loc.administrator_access === true);

// IMPORTANT: $ae_loc.edit_mode is the GLOBAL AE Edit Mode — a UI preference that
// reveals editable fields, debug info, and advanced options app-wide. It is NOT
// a badge-specific edit toggle. Never write to it from badge components.
// Used here only to gate the header "Print Now" shortcut (allow reprints in edit mode).
let is_edit_mode = $derived($ae_loc.edit_mode === true);

// Print state derived from badge
let print_count = $derived($lq__event_badge_obj?.print_count ?? 0);
let is_printed = $derived(print_count >= 1);

// Badge type for header — simplified chain (no template list lookup).
// Full lookup with template list is in ae_comp__badge_print_controls.
let badge_type_display = $derived(
    $lq__event_badge_obj?.badge_type_override ??
        $lq__event_badge_obj?.badge_type ??
        $lq__event_badge_obj?.badge_type_code_override ??
        $lq__event_badge_obj?.badge_type_code ??
        ''
);

function build_review_url(): string {
    // TODO: append ?passcode=... when person_passcode is added to the event_badge schema
    return `/events/${$lq__event_badge_obj?.event_id}/badges/${$lq__event_badge_obj?.event_badge_id}/review`;
}


// TODO: replace alert with actual email API call when available
function send_review_email() {
    const badge = $lq__event_badge_obj;
    const name =
        badge?.full_name_override ??
        badge?.full_name ??
        `${badge?.given_name ?? ''} ${badge?.family_name ?? ''}`.trim();
    const email = is_trusted
        ? (badge?.email ?? '(no email on file)')
        : ae_util.obscure_email(badge?.email);
    const event_name = EVENTS_MODULE_TITLE;
    alert(
        `PLACEHOLDER: An email will be sent to ${name} at ${email}. Use that link to review your ${event_name} badge.`
    );
}

// Font size overrides (px). null = auto-sizing (Element_fit_text binary search).
// Controls live in Comp_badge_print_controls (right panel) via $bindable().
// Constants and adjust logic are defined there; only the state lives here so
// the values can be forwarded to both the controls and the badge render.
//
// Initialization order: badge cfg_json.font_sizes takes precedence (persisted per-badge),
// falling back to null (auto) on first load. The auto-save in the controls panel writes
// adjusted sizes back to cfg_json so they survive page reloads and are shared cross-kiosk.
let font_size_name: number | null = $state(null);
let font_size_title: number | null = $state(null);
let font_size_affiliations: number | null = $state(null);
let font_size_location: number | null = $state(null);

// Initialization tracking:
//   _font_sizes_loaded_for — badge ID we last initialized for; reset on navigation.
//   _font_sizes_applied_cfg — the cfg_json string we last read font_sizes from.
//     Comparing this (not just the badge ID) lets us re-apply when a SWR background
//     refresh delivers newly-saved sizes — fixing the stale-Dexie race where the first
//     liveQuery tick has a cached badge without font_sizes, locking the badge-ID guard
//     before the API refresh arrives with the real saved values.
let _font_sizes_loaded_for: string | null = null;
let _font_sizes_applied_cfg: string = '$unset'; // sentinel — never equals a real cfg_json value

$effect(() => {
    const badge = $lq__event_badge_obj;
    if (!badge?.event_badge_id) return;

    const current_cfg: string | null = badge.cfg_json ?? null;

    // New badge — reset everything and fall through to apply.
    if (_font_sizes_loaded_for !== badge.event_badge_id) {
        _font_sizes_loaded_for = badge.event_badge_id;
        _font_sizes_applied_cfg = '$unset';
        font_size_name = null;
        font_size_title = null;
        font_size_affiliations = null;
        font_size_location = null;
    }

    // cfg_json hasn't changed since we last applied — nothing to do.
    if (_font_sizes_applied_cfg === current_cfg) return;

    // cfg_json changed (SWR refresh delivered new data). Before applying, check
    // whether local sizes have drifted from what we last applied. If the user has
    // made adjustments since our last apply, the auto-save will sync them back to
    // cfg_json shortly — don't clobber their in-progress work.
    if (_font_sizes_applied_cfg !== '$unset') {
        try {
            const prev_cfg = JSON.parse(_font_sizes_applied_cfg ?? 'null') ?? {};
            const prev_fs = prev_cfg?.font_sizes ?? null;
            const user_adjusted =
                font_size_name !== (prev_fs?.name ?? null) ||
                font_size_title !== (prev_fs?.title ?? null) ||
                font_size_affiliations !== (prev_fs?.affiliations ?? null) ||
                font_size_location !== (prev_fs?.location ?? null);
            if (user_adjusted) {
                // Advance the marker so we don't re-check on the next identical cfg_json.
                _font_sizes_applied_cfg = current_cfg ?? '$unset';
                return;
            }
        } catch { /* ignore parse errors — fall through and apply */ }
    }

    // Apply font sizes from the fresh cfg_json.
    _font_sizes_applied_cfg = current_cfg ?? '$unset';
    try {
        const cfg = typeof badge.cfg_json === 'string'
            ? JSON.parse(badge.cfg_json)
            : (badge.cfg_json ?? {});
        const fs = cfg?.font_sizes;
        font_size_name = fs?.name ?? null;
        font_size_title = fs?.title ?? null;
        font_size_affiliations = fs?.affiliations ?? null;
        font_size_location = fs?.location ?? null;
    } catch {
        // Malformed cfg_json — stay with current values
    }
});

// Per-browser print workstation tweaks.
// Each workstation/printer is calibrated independently — values are stored in
// localStorage so they survive page reloads and browser restarts without any
// server config change.
// WHY localStorage not a store: this is intentionally per-device, not per-user or
// per-event. Different printers at the same venue may need different offsets.
const _PRINT_TWEAKS_KEY = 'ae_badge_print_tweaks';
const _saved_tweaks = (() => {
    if (!browser) return null;
    try {
        return JSON.parse(localStorage.getItem(_PRINT_TWEAKS_KEY) ?? 'null');
    } catch {
        return null;
    }
})();

// Print position offset (mm). Positive X = shift right; positive Y = shift down.
let print_offset_x: number = $state(_saved_tweaks?.x ?? 0);
let print_offset_y: number = $state(_saved_tweaks?.y ?? 0);

// Banner width: true = 100% (production), false = natural pixel size (dev/calibration).
let banner_full_width: boolean = $state(
    _saved_tweaks?.banner_full_width ?? true
);

// Name layout: true = prefer two lines (first / last split), false = one line.
// Default true — most badge templates look best with the name split across two lines.
// Stored in localStorage so the preference persists per workstation across reloads.
let name_two_lines: boolean = $state(_saved_tweaks?.name_two_lines ?? true);

// hide_chrome: hides the page header and sys bar for a clean badge-preview workspace.
// Toggled via the [H] keyboard shortcut or the button in the controls panel staff section.
let hide_chrome: boolean = $state(_saved_tweaks?.hide_chrome ?? false);

// Sync chrome visibility with hide_chrome. Restores all original states on page leave.
// Hides three things:
//   1. $ae_loc.sys_menu.hide  — the fixed bottom-right AE sys bar
//   2. $ae_sess.disable_sys_nav    — the Events layout <nav class="submenu"> (site header)
//   3. $ae_sess.disable_sys_footer — the Events layout <footer> (site footer)
const _restore_sys_bar = $ae_loc.sys_menu.hide;
const _restore_sys_nav = $ae_sess.disable_sys_nav;
const _restore_sys_footer = $ae_sess.disable_sys_footer;
$effect(() => {
    $ae_loc.sys_menu.hide = hide_chrome;
    $ae_sess.disable_sys_nav = hide_chrome;
    $ae_sess.disable_sys_footer = hide_chrome;
    return () => {
        $ae_loc.sys_menu.hide = _restore_sys_bar;
        $ae_sess.disable_sys_nav = _restore_sys_nav;
        $ae_sess.disable_sys_footer = _restore_sys_footer;
    };
});

// Persist all tweaks to localStorage
$effect(() => {
    if (browser)
        localStorage.setItem(
            _PRINT_TWEAKS_KEY,
            JSON.stringify({
                x: print_offset_x,
                y: print_offset_y,
                hide_chrome,
                banner_full_width,
                name_two_lines
            })
        );
});

// Inject print offset as @media print CSS. Uses translate() so the centering
// logic in ae-print-badge.css stays intact — this just shifts the final position.
// The effect re-runs whenever either offset changes, removing the old <style> tag
// and inserting a fresh one (always last in <head>, so it wins the cascade).
$effect(() => {
    const el = document.createElement('style');
    el.textContent = `@media print { .event_badge_wrapper { transform: translate(calc(-50% + ${print_offset_x}mm), calc(-50% + ${print_offset_y}mm)) !important; } }`;
    document.head.appendChild(el);
    return () => el.remove();
});

// Real-time preview: the controls panel sets this to the current edit field
// value on every keystroke. The badge render merges it over the saved badge
// object so the user can see changes as they type, before saving.
let preview_overrides: Record<string, any> | null = $state(null);


// Print margin config from badge template cfg_json.
// When set, overrides the default @page margin: 0 for per-template positioning.
// Expected format in cfg_json: { "print_margin": { "top": "0.25in", "right": "0.25in", "bottom": "0.25in", "left": "0.25in" } }
// All four edges are optional; omitted edges fall back to 0.
let print_margin_cfg = $derived.by(() => {
    try {
        const cfg = JSON.parse($lq__event_badge_template_obj?.cfg_json ?? '{}');
        return (
            (cfg?.print_margin as {
                top?: string;
                right?: string;
                bottom?: string;
                left?: string;
            } | null) ?? null
        );
    } catch {
        return null;
    }
});

// @page size injection: Svelte 5 does not allow <style> inside {#if} blocks in
// <svelte:head> — the parser treats them as raw-text elements and loses document
// structure. Inject programmatically via $effect instead.
let page_size_css = $derived(
    $lq__event_badge_template_obj?.layout === 'badge_3.5x5.5_pvc'
        ? '3.5in 5.5in'
        : $lq__event_badge_template_obj?.layout === 'badge_4x5_fanfold'
          ? '4in 10in' // duplex fanfold: 5in front + 5in back = 10in strip
          : $lq__event_badge_template_obj?.layout === 'badge_4x6_fanfold'
            ? '4in 6in' // single-sided fanfold: front only
            : '4in 6in' // fallback: unknown layout, assume single-sided 4×6
);
// @page size + optional per-template margin override from cfg_json.print_margin.
// print_margin_cfg is parsed from the template's cfg_json field — allows per-event
// margin tuning without a code deploy (edit cfg_json in the DB, refresh page).
// For fine-tuning per-printer/workstation, use the print_offset_x/y controls instead.
$effect(() => {
    const m = print_margin_cfg;
    const margin_val = m
        ? `${m.top ?? '0'} ${m.right ?? '0'} ${m.bottom ?? '0'} ${m.left ?? '0'}`
        : '0';
    const el = document.createElement('style');
    el.textContent = `@page { size: ${page_size_css}; margin: ${margin_val}; }`;
    document.head.appendChild(el);
    return () => el.remove();
});
</script>

<svelte:window
    onkeydown={(e) => {
        // [H] key: toggle header/sys-bar chrome for a clean print workspace.
        // Guard: don't fire when typing in form fields.
        if (
            e.target instanceof HTMLInputElement ||
            e.target instanceof HTMLTextAreaElement ||
            e.target instanceof HTMLSelectElement
        )
            return;
        if (e.key === 'h' && !e.ctrlKey && !e.metaKey && !e.altKey) {
            e.preventDefault();
            hide_chrome = !hide_chrome;
        }
    }} />

<svelte:head>
    <title>
        &AElig;: Print Badge —
        {$lq__event_badge_obj?.full_name_override ??
            $lq__event_badge_obj?.full_name ??
            '—'}
         — {EVENTS_MODULE_TITLE}
    </title>

    <link
        rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"
        integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ=="
        crossorigin="anonymous"
        referrerpolicy="no-referrer" />

    <!--
        @page rule sets paper size for the browser print dialog.
        Scoped per layout code from the badge template.

        Epson 4×5 fanfold:  front (5") + back (5") = 10" strip, 4" wide.
        Zebra ZC10L PVC:    single card, 3.5" × 5.5", single-sided (duplex=0).
        Default (4×6):      front (6") + back (6") = 12" strip, 4" wide.

        @page cannot use selector scoping — it must be top-level CSS — so we inject
        it dynamically here based on the template's layout field rather than putting
        it in the compiled layout CSS files.
    -->

    <!-- Print layout strategy — applied regardless of badge layout:

         Instead of trying to coerce #ae_main_content into a centering container
         (which requires fighting its min-height, overflow, max-width, flex-col etc.),
         we use `display: contents` to dissolve the intermediate wrappers from the
         layout. This makes .event_badge_wrapper a direct flex child of <body>, and
         body becomes the single centering container for the whole printed page.

         For PVC / fanfold: the @page size below matches the badge exactly, so
         margin: 0 fills the page cleanly. If per-template margins are needed,
         set cfg_json: { "print_margin": { "top": "0.25in", ... } } on the template
         and a dynamic @page rule can be injected here via print_margin_cfg.

         Print media CSS lives in static/ae-print-badge.css (loaded below).
         @page size is injected via $effect in the script block (dynamic per template layout).
    -->
    <link rel="stylesheet" href="/ae-print-badge.css" />

    <!-- External client CSS: brand colors, logo sizing, footer stripe colors per event.
         Set style_href on the badge template. Edit the file on the static server and
         refresh to update mid-event without any deployment. -->
    {#if $lq__event_badge_template_obj?.style_href}
        <link
            rel="stylesheet"
            href={$lq__event_badge_template_obj.style_href} />
    {/if}
</svelte:head>

{#if $lq__event_badge_obj && $lq__event_badge_obj.event_id && event_badge_id}
    <!-- Kiosk header: screen-only chrome. Minimal — the badge render is the focus.
         Hidden when hide_chrome is on (toggle with [H] or the controls panel button). -->
    <header
        class="mb-3 flex w-full flex-row flex-wrap items-center justify-between gap-2 border-b border-gray-300 dark:border-gray-700 pb-2 print:hidden"
        class:hidden={hide_chrome}>
        <!-- Left: Back + attendee name + status indicator -->
        <div class="flex min-w-0 flex-row items-center gap-2">
            <a
                href={`/events/${$lq__event_badge_obj.event_id}/badges`}
                class="btn btn-sm preset-tonal-surface flex shrink-0 items-center gap-1"
                title="Back to badge search">
                <ArrowLeft size="1em" />
                <span class="hidden sm:inline">Search</span>
            </a>
            <div class="flex min-w-0 flex-col">
                <div class="flex flex-row flex-wrap items-baseline gap-2">
                    <h2 class="truncate text-base leading-tight font-bold">
                        {$lq__event_badge_obj.full_name_override ??
                            $lq__event_badge_obj.full_name ??
                            '— no name —'}
                    </h2>
                    {#if is_printed}
                        <!-- Amber badge: clearly shows this has already been printed -->
                        <span
                            class="shrink-0 rounded-full border border-amber-300 bg-amber-50 px-1.5 py-0.5 text-sm font-semibold whitespace-nowrap text-amber-700 dark:border-amber-700 dark:bg-amber-950/40"
                            title="This badge has already been printed. Only staff can re-print a badge.">

                            <PrinterCheck size="1.2em" />
                            Printed {print_count}×
                        </span>
                    {:else}
                        <span
                            class="shrink-0 rounded-full border border-green-200 bg-green-50/80 px-1.5 py-0.5 text-sm font-semibold whitespace-nowrap text-green-700 dark:border-green-900 dark:bg-green-950/80 dark:text-green-400
                            "
                            >
                            <UserCheck size="1.2em" />
                            Ready to print
                        </span>
                    {/if}
                </div>
                <p class="truncate text-xs leading-tight text-gray-500">
                    {badge_type_display}{badge_type_display ? ' · ' : ''}{EVENTS_MODULE_TITLE}
                </p>
            </div>
        </div>

        <!-- Right: secondary / staff-only actions.
             Re-print was removed — the controls panel "Test" button covers that use case
             without duplicating the action here where it competes with the panel. -->
        <div class="flex shrink-0 flex-row items-center gap-1">
            <!-- Review page: Trusted + Edit Mode -->
            {#if is_trusted && is_edit_mode}
                <a
                    transition:fade={{ duration: 150 }}
                    href={build_review_url()}
                    class="btn btn-sm preset-tonal-secondary flex items-center gap-1"
                    title="Open badge review page">
                    <Eye size="1em" />
                    <span class="hidden sm:inline">Review</span>
                </a>
            {/if}

            <!-- Email review link: Administrator + Edit Mode only
                 Temporarily restricted — TODO: restore broader access after Axonius 2026 -->
            {#if is_admin && is_edit_mode}
                <button
                    transition:fade={{ duration: 150 }}
                    type="button"
                    class="btn btn-sm preset-tonal-surface flex items-center gap-1"
                    onclick={() => send_review_email()}
                    title="Email a review link to this attendee">
                    <Mail size="1em" />
                    <span class="hidden sm:inline">Email Link</span>
                </button>
            {/if}
        </div>
    </header>

    <!-- Badge + controls: side by side on large screens, stacked on mobile.
         Print: #badge_render_area CSS applies display:contents to dissolve the badge wrapper;
         .event_badge_wrapper uses position:fixed to center on the @page — the row div
         becomes an empty block in print, which is harmless. -->
    <div class="flex w-full flex-col items-center gap-4 lg:flex-row lg:items-start lg:justify-center">

        <!-- Badge column: natural print size, never shrinks. -->
        <div id="badge_render_area" class="shrink-0">
            <Comp_badge_obj_view
                event_id={$lq__event_badge_obj.event_id as string}
                event_badge_id={event_badge_id as string}
                {lq__event_badge_obj}
                {lq__event_badge_template_obj}
                is_review_mode={false}
                {font_size_name}
                {font_size_title}
                {font_size_affiliations}
                {font_size_location}
                {preview_overrides}
                {banner_full_width}
                {name_two_lines} />
        </div>

        <!-- Controls column: max-w-sm (384px) keeps the panel a readable width without
             stretching. On mobile: full width below the badge. Never prints. -->
        <div class="w-full min-w-0 overflow-hidden
                    rounded-lg border border-gray-200 dark:border-gray-700
                    bg-white dark:bg-zinc-900 shadow-sm
                    lg:w-auto lg:flex-1 lg:max-w-sm
                    lg:sticky lg:top-4 lg:self-start
                    lg:max-h-[calc(100vh-6rem)] lg:overflow-y-auto
                    print:hidden">
            <Comp_badge_print_controls
                event_id={$lq__event_badge_obj.event_id as string}
                event_badge_id={event_badge_id as string}
                {lq__event_badge_obj}
                {lq__event_badge_template_obj}
                bind:font_size_name
                bind:font_size_title
                bind:font_size_affiliations
                bind:font_size_location
                bind:preview_overrides
                bind:print_offset_x
                bind:print_offset_y
                bind:hide_chrome
                bind:banner_full_width
                bind:name_two_lines />
        </div>
    </div>
{:else if is_loading_idb || !event_badge_id}
    <div class="flex w-full flex-col items-center justify-center p-16 text-center opacity-50">
        <LoaderCircle size="3em" class="mx-auto mb-2 animate-spin" />
        <p class="text-xl">Loading Badge...</p>
    </div>
{:else}
    <div class="flex w-full flex-col items-center justify-center p-16 text-center">
        <p class="text-error-500 mb-2 text-2xl font-bold">Badge Not Found</p>
        <p class="mb-6 opacity-60 text-sm">
            No record for ID: <span class="font-mono">{event_badge_id}</span>
        </p>
        <div class="flex justify-center gap-2">
            <button
                class="btn preset-tonal-surface"
                onclick={() => window.location.reload()}>
                Retry
            </button>
            <a
                href={`/events/${event_id}/badges`}
                class="btn preset-filled-primary">
                Back to Search
            </a>
        </div>
    </div>
{/if}
