<script lang="ts">
interface Props {
    log_lvl?: number;
    event_file_id: string;
    event_file_obj: any;
    max_filename_length?: number;
    hide_launch_icon?: boolean;
    hide_meta?: boolean;
    hide_created_on?: boolean;
    hide_os?: boolean;
    hide_size?: boolean;
    show_internal_purpose_files?: boolean;
    show_bak_download?: boolean;
    btn_size?: string;
    btn_text_align?: string;
    text_size?: string;
    text_size_md?: string;
    session_type?: string;
    open_method?: null | string;
    modal_title?: string;

    modal__title?: any;
    modal__open_event_file_id?: any;
    modal__event_file_obj?: any;
}

let {
    log_lvl = $bindable(0),
    event_file_id,
    event_file_obj = $bindable({}),
    max_filename_length = $bindable(50),
    hide_launch_icon = $bindable(false),
    hide_meta = $bindable(false),
    hide_created_on = $bindable(false),
    hide_os = $bindable(false),
    hide_size = $bindable(false),
    show_internal_purpose_files = $bindable(false),
    show_bak_download = false,
    btn_size = $bindable('btn-sm'),
    btn_text_align = $bindable('text-left'),
    text_size = $bindable('text-sm'),
    text_size_md = $bindable('md:text-base'),
    session_type = $bindable('oral'),
    open_method = $bindable('download'),
    modal_title = $bindable(''),

    modal__title = $bindable(''),
    modal__open_event_file_id = $bindable(null),
    modal__event_file_obj = $bindable(null)
}: Props = $props();

import { onMount } from 'svelte';
import { fade } from 'svelte/transition';

import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
import { api } from '$lib/api/api';
import { ae_loc, ae_api, ae_sess, slct } from '$lib/stores/ae_stores';
import { core_func } from '$lib/ae_core/ae_core_functions';
import {
    events_loc,
    events_sess,
    events_slct
} from '$lib/stores/ae_events_stores';
import { events_func } from '$lib/ae_events/ae_events_functions';
import {
    AlertCircle,
    AlertTriangle,
    BarChart2,
    CalendarDays,
    FolderOpen,
    Laptop,
    Link2,
    LoaderCircle,
    Monitor,
    Save,
    Send,
    WifiOff
} from '@lucide/svelte';
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';

// Import the relay
import * as native from '$lib/electron/electron_relay';
import {
    type LaunchProfile,
    resolve_launch_profile
} from '$lib/ae_events/ae_launcher__default_launch_profiles';

let ae_promises: key_val = $state({});

let open_file_clicked: null | boolean = $state(null);
let open_file_status: null | string = $state(null);
let open_file_status_message: null | string = $state(null);
let open_file_error_detail: string | null = $state(null);

/** State for the native test mode debug popup */
let test_mode_popup_open: boolean = $state(false);
let test_mode_popup_data: Record<string, any> | null = $state(null);

/** Simple promise-based delay for post-open script timing */
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

/** True when the device has network connectivity. Updated reactively for URL-type files. */
let is_online: boolean = $state(typeof navigator !== 'undefined' ? navigator.onLine : true);

/**
 * True when this file's filename IS a URL rather than a hosted filename.
 * Convention: event_file.filename = 'https://...' or 'http://...'
 * Use event_file.title as the human-readable label; extension = 'url' (or omitted).
 */
const is_url = $derived(
    (event_file_obj?.filename ?? '').startsWith('https://') ||
    (event_file_obj?.filename ?? '').startsWith('http://')
);

let screen_saver_exts = ['jpg', 'png', 'PNG', 'webp'];

/**
 * Resolves the LaunchProfile for a given file extension and optional per-file
 * display override. Checked in priority order:
 *   1. event_device.data_json.launch_profiles   (API-driven, per-device)
 *   2. event_device.data_json.launch_scripts     (legacy alias)
 *   3. $events_loc.launcher.launch_profiles      (local persistent override)
 *   4. $events_loc.launcher.launch_scripts       (legacy alias)
 *   5. DEFAULT_LAUNCH_PROFILES[ext]              (Svelte built-in defaults)
 *   6. DEFAULT_LAUNCH_PROFILES['default']        (catch-all)
 * Per-file display_override from event_file.cfg_json overrides display_mode only.
 */
function get_launch_profile(
    extension: string,
    file_obj?: any
): LaunchProfile {
    const device_profiles =
        ($ae_loc as any).native_device?.launch_profiles ??
        ($ae_loc as any).native_device?.launch_scripts ??
        null;
    const local_profiles =
        ($events_loc as any).launcher?.launch_profiles ??
        ($events_loc as any).launcher?.launch_scripts ??
        null;
    const display_override = file_obj?.cfg_json?.display_override ?? null;
    return resolve_launch_profile(extension, display_override, device_profiles, local_profiles);
}

onMount(() => {
    if (screen_saver_exts.includes(event_file_obj.extension)) {
        if (!$events_loc.launcher.screen_saver_img_kv)
            $events_loc.launcher.screen_saver_img_kv = {};
        $events_loc.launcher.screen_saver_img_kv[event_file_id] = {
            ...event_file_obj
        };
    }
    // Only register online/offline listeners for URL-type files — no point on file rows.
    if (is_url && typeof window !== 'undefined') {
        const on_online = () => (is_online = true);
        const on_offline = () => (is_online = false);
        window.addEventListener('online', on_online);
        window.addEventListener('offline', on_offline);
        return () => {
            window.removeEventListener('online', on_online);
            window.removeEventListener('offline', on_offline);
        };
    }
});

async function handle_open_file() {
    if (log_lvl) console.log('*** handle_open_file() ***');
    if (open_file_clicked) return; // Hard Guard: Already processing

    $events_slct.event_file_id = event_file_id;
    $events_slct.event_file_obj = event_file_obj;

    // URL-TYPE FILE: event_file.filename is a URL (https://...), not a hosted file path.
    // Handled entirely here — no cache, no download, no temp copy.
    if (is_url) {
        const url = event_file_obj.filename as string;

        // Test mode: show debug popup instead of opening
        if ($events_loc.launcher.native_test_mode && $events_loc.launcher.app_mode === 'native') {
            open_file_clicked = true;
            open_file_status = 'opening_file';
            const profile = get_launch_profile('url', event_file_obj);
            test_mode_popup_data = {
                is_url: true,
                filename: url,
                extension: 'url',
                title: event_file_obj.title || null,
                hash_sha256: null,
                simulated_temp_path: null,
                profile,
                open_cmd_resolved: `native.open_external({ url: "${url}", app: "chrome" })`,
                display_override: event_file_obj?.cfg_json?.display_override ?? null,
                cache_check: 'N/A — URL file',
                copy_to_temp: 'N/A — URL file'
            };
            test_mode_popup_open = true;
            open_file_status = 'open';
            open_file_status_message = 'Test Mode: URL profile resolved — see popup';
            setTimeout(() => (open_file_clicked = false), 6000);
            return true;
        }

        // Offline guard: warn and abort before attempting to open
        if (!is_online) {
            open_file_clicked = true;
            open_file_status = 'error';
            open_file_status_message = 'Network offline — cannot open URL';
            open_file_error_detail = `URL: ${url}`;
            setTimeout(() => (open_file_clicked = false), 6000);
            return false;
        }

        open_file_clicked = true;
        open_file_status = 'opening_file';
        const profile = get_launch_profile('url', event_file_obj);

        // URL presentations may still want to set display mode (e.g. Google Slides → extend)
        if (profile.display_mode !== 'none') {
            open_file_status_message = `Setting display (${profile.display_mode})...`;
            await native.set_display_layout({ mode: profile.display_mode }).catch(() => {
                /* No external display or displayplacer unconfigured — continue */
            });
        }

        open_file_status_message = `Opening ${event_file_obj.title || 'URL'}...`;

        if ($ae_loc.is_native && $events_loc.launcher.app_mode === 'native') {
            // Native: open in Chrome for kiosk-style presentation; fall back to default browser
            const result = await native.open_external({ url, app: 'chrome' });
            if (!result?.success) {
                await native.open_external({ url, app: 'default' }).catch(() => {});
            }
        } else {
            window.open(url, '_blank', 'noopener,noreferrer');
        }

        open_file_status = 'open';
        open_file_status_message = `Opened: ${event_file_obj.title || url}`;
        setTimeout(() => (open_file_clicked = false), 4000);
        return true;
    }

    // 0. NATIVE TEST MODE — simulate full native flow, show debug popup instead of running commands
    // Active when native_test_mode toggle is on (regardless of is_native / app_mode).
    // Lets you preview the resolved profile, open command, and post-script from any device.
    if ($events_loc.launcher.native_test_mode && $events_loc.launcher.app_mode === 'native') {
        open_file_clicked = true;
        open_file_status = 'checking_cache';
        open_file_status_message = 'Test Mode: simulating cache check...';

        await sleep(400); // Brief simulated cache check

        open_file_status = 'opening_file';
        open_file_status_message = 'Test Mode: resolving launch profile...';

        const profile = get_launch_profile(event_file_obj.extension, event_file_obj);
        const open_cmd_resolved = profile.open_cmd
            ? profile.open_cmd.replaceAll('{{path}}', `/tmp/ae_test/${event_file_obj.filename}`)
            : null;

        test_mode_popup_data = {
            filename: event_file_obj.filename,
            extension: event_file_obj.extension,
            hash_sha256: event_file_obj.hash_sha256,
            simulated_temp_path: `/tmp/ae_test/${event_file_obj.filename}`,
            profile,
            open_cmd_resolved,
            display_override: event_file_obj?.cfg_json?.display_override ?? null,
            cache_check: 'PASS (simulated)',
            copy_to_temp: 'PASS (simulated)'
        };
        test_mode_popup_open = true;

        open_file_status = 'open';
        open_file_status_message = 'Test Mode: profile resolved — see popup';
        setTimeout(() => (open_file_clicked = false), 6000);
        return true;
    }

    // 1. NATIVE MODE (Electron)
    if ($ae_loc.is_native && $events_loc.launcher.app_mode === 'native') {
        const cache_root = $ae_loc.local_file_cache_path;
        const temp_root = $ae_loc.host_file_temp_path;

        open_file_clicked = true;
        open_file_status = 'checking_cache';
        open_file_status_message = 'Checking local cache...';
        open_file_error_detail = null; // Fix 1: clear stale error from any previous attempt

        // Fix 2: safety valve — if a native call hangs and no path resets the button,
        // force-release it after 60s. All normal paths reset within ~8s so this is last resort.
        setTimeout(() => {
            if (open_file_clicked) {
                open_file_clicked = false;
                open_file_status = 'error';
                open_file_status_message = 'Timed out — please try again';
            }
        }, 60_000);

        const exists = await native.check_hash_file_cache({
            cache_root,
            hash: event_file_obj.hash_sha256,
            verify_hash: true // Hardened: Trust No One!
        });

        if (!exists) {
            open_file_status = 'downloading_file';
            open_file_status_message = 'Downloading file to cache...';

            // Use the PROVEN endpoint path from api.ts that is known to work in Default Mode.
            const url = `${$ae_api.base_url}/v3/action/hosted_file/${event_file_obj.hosted_file_id}/download?return_file=true&filename=${encodeURIComponent(event_file_obj.filename)}&key=${$ae_api.account_id}`;

            const dl_result = await native.download_to_cache({
                url,
                cache_root,
                hash: event_file_obj.hash_sha256,
                api_key: $ae_api.api_secret_key,
                account_id: $ae_api.account_id
            });

            if (!dl_result.success) {
                open_file_status = 'error';
                open_file_status_message = `Download failed: ${dl_result.error}`;
                setTimeout(() => (open_file_clicked = false), 5000);
                return false;
            }
        }

        // --- Step 1: Copy cached file to a writable temp path ---
        open_file_status = 'opening_file';
        open_file_status_message = 'Preparing file...';
        open_file_error_detail = null;

        const copy_result = await native.copy_from_cache_to_temp({
            cache_root,
            hash: event_file_obj.hash_sha256,
            temp_root,
            filename: event_file_obj.filename
        });

        if (!copy_result.success || !copy_result.path) {
            open_file_status = 'error';
            open_file_status_message = 'Failed to prepare file';
            open_file_error_detail = copy_result.error ?? 'copy_from_cache_to_temp returned no path';
            setTimeout(() => (open_file_clicked = false), 6000);
            return false;
        }

        const resolved_path = copy_result.path;

        // --- Step 2: Resolve launch profile ---
        const profile = get_launch_profile(event_file_obj.extension, event_file_obj);
        if (log_lvl) console.log('LaunchProfile:', profile);

        // --- Step 3: Set display layout (skip silently on failure / no external display) ---
        if (profile.display_mode !== 'none') {
            open_file_status_message = `Setting display (${profile.display_mode})...`;
            await native.set_display_layout({ mode: profile.display_mode }).catch(() => {
                /* No external display or displayplacer unavailable — continue */
            });
        }

        // --- Step 4: Open the file ---
        open_file_status_message = `Opening ${profile.app}...`;
        let open_ok = true;
        let open_error: string | null = null;

        if (profile.open_cmd) {
            const cmd = profile.open_cmd.replaceAll('{{path}}', resolved_path);
            const cmd_result = await native.run_cmd({ cmd });
            if (!cmd_result.success) {
                open_ok = false;
                open_error = cmd_result.error ?? 'run_cmd failed';
            }
        } else {
            // No open_cmd → OS default handler via shell.openPath.
            // .catch: IPC reply can be orphaned if the renderer navigates before shell.openPath
            // resolves. The file still opens — treat the orphaned reply as success.
            const os_result = await native.open_local_file_v2(resolved_path).catch(() => ({ success: true }));
            if (!os_result.success) {
                open_ok = false;
                open_error = os_result.error ?? 'open_local_file_v2 failed';
            }
        }

        // Fix 3: update the status message as soon as the open call returns so "Opening..." doesn't
        // appear stuck for the entire post_script sleep. OS has the request; we're just waiting now.
        if (open_ok) {
            open_file_status_message = profile.post_script
                ? `${profile.app} opened — running setup...`
                : `${profile.app} opened`;
        }

        // --- Step 5: Wait for app to load before running post-script ---
        // Only delay if there is actually a post_script to run — no point waiting for nothing.
        if (open_ok && profile.post_script) {
            const delay = profile.post_delay_ms ?? 2000;
            open_file_status_message = `Waiting for ${profile.app}...`;
            await sleep(delay);
        }

        // --- Step 6: Run post-script (AppleScript or shell) ---
        if (open_ok && profile.post_script) {
            open_file_status_message = 'Running post-open automation...';
            let script_ok = true;
            let script_error: string | null = null;

            if (profile.post_script.startsWith('shell:')) {
                const shell_cmd = profile.post_script.slice('shell:'.length);
                const sr = await native.run_cmd({ cmd: shell_cmd });
                if (!sr.success) { script_ok = false; script_error = sr.error ?? 'run_cmd (post) failed'; }
            } else {
                const sr = await native.run_osascript(profile.post_script);
                if (!sr.success) { script_ok = false; script_error = sr.error ?? 'run_osascript failed'; }
            }

            if (!script_ok) {
                // Non-fatal: file is already open. Surface as warning, not error.
                if (log_lvl) console.warn('post_script failed:', script_error);
                open_file_status = 'fallback';
                open_file_status_message = `Opened (post-script failed: ${script_error})`;
                setTimeout(() => (open_file_clicked = false), 8000);
                return true;
            }
        }

        // --- Step 7: Fallback if open_cmd itself failed ---
        if (!open_ok) {
            if (log_lvl) console.warn('open_cmd failed, falling back to OS default:', open_error);
            // .catch: same orphaned-reply guard as Step 4.
            const fb_result = await native.open_local_file_v2(resolved_path).catch(() => ({ success: true }));
            if (!fb_result.success) {
                open_file_status = 'error';
                open_file_status_message = 'Failed to open file';
                open_file_error_detail = `${profile.app} failed: ${open_error}; OS fallback: ${fb_result.error}`;
                setTimeout(() => (open_file_clicked = false), 8000);
                return false;
            }
            open_file_status = 'fallback';
            open_file_status_message = '(opened with OS default)';
            setTimeout(() => (open_file_clicked = false), 5000);
            return true;
        }

        // --- Success ---
        open_file_status = 'open';
        open_file_status_message = `Opened in ${profile.app}`;
        setTimeout(() => (open_file_clicked = false), 5000);
        return true;
    }
    // 2. ONSITE MODE (Browser with Modified Extensions)
    else if ($events_loc.launcher.app_mode === 'onsite') {
        open_file_clicked = true;
        open_file_status = 'downloading_onsite';
        open_file_status_message = 'Downloading (Onsite Mode)...';
        open_file_error_detail = null;

        let filename = event_file_obj.filename;
        if (
            (event_file_obj.extension === 'ppt' ||
                event_file_obj.extension === 'pptx') &&
            event_file_obj.open_in_os === 'win'
        ) {
            filename = event_file_obj.filename + 'win';
        }

        const dl_promise = api.get_object({
            api_cfg: $ae_api,
            endpoint: `/v3/action/hosted_file/${event_file_obj.hosted_file_id}/download`,
            params: {
                filename: filename,
                x_no_account_id_token: 'direct-download'
            },
            filename: filename,
            return_blob: true,
            auto_download: true,
            log_lvl: 1
        });

        setTimeout(() => (open_file_clicked = false), 5000);
        return dl_promise;
    }
    // 3. DEFAULT MODE (Standard Browser)
    else {
        open_file_clicked = true;
        open_file_status = 'downloading_default';
        open_file_status_message = 'Downloading...';
        open_file_error_detail = null;

        const dl_promise = api.get_object({
            api_cfg: $ae_api,
            endpoint: `/v3/action/hosted_file/${event_file_obj.hosted_file_id}/download`,
            params: {
                filename: event_file_obj.filename,
                x_no_account_id_token: 'direct-download'
            },
            filename: event_file_obj.filename,
            return_blob: true,
            auto_download: true,
            log_lvl: 1
        });

        if ($events_loc.launcher.controller == 'local_push') {
            $events_sess.launcher.controller_cmd = `ae_download:hosted_file=${event_file_obj.hosted_file_id}:${event_file_obj.filename}:${event_file_obj.extension}`;
            $events_sess.launcher.controller_trigger_send = true;
        }

        setTimeout(() => (open_file_clicked = false), 5000);
        return dl_promise;
    }
}

function prevent_default<T extends Event>(fn: (event: T) => void) {
    return function (event: T) {
        event.preventDefault();
        fn(event);
    };
}
</script>

<div
    class:justify-between={!hide_meta}
    class:justify-center={hide_meta}
    class:hidden={!show_internal_purpose_files &&
        (event_file_obj.file_purpose == 'outline' ||
            event_file_obj.file_purpose == 'draft' ||
            event_file_obj.file_purpose == 'admin')}
    class="event_launcher_file_cont flex max-w-full grow flex-col flex-wrap items-center justify-center gap-1 transition-all md:flex-row">
    {#if open_file_clicked}
        <div
            class="open_file_clicked alert"
            in:fade={{ duration: 250 }}
            out:fade={{ duration: 2000 }}>
            <div class="alert_msg_pulse">
                <strong
                    >*** {open_file_status_message ||
                        'Please wait while this file downloads...'} ***</strong>
            </div>
            {#if $ae_loc.is_native && $events_loc.launcher.app_mode === 'native'}
                {#if open_file_status === 'error'}
                    <p class="text-red-400">Failed to open file.</p>
                    {#if open_file_error_detail}
                        <pre class="mt-1 max-w-full overflow-x-auto whitespace-pre-wrap break-all rounded bg-black/30 px-2 py-1 font-mono text-xs text-red-300">{open_file_error_detail}</pre>
                    {/if}
                {:else if open_file_status === 'fallback'}
                    <p class="text-yellow-400">(opened with OS default)</p>
                {:else}
                    <p>Most files will automatically be opened full screen.</p>
                    <p>
                        PowerPoint or KeyNote will attempt to display in presenter
                        view.
                    </p>
                    <p>Please close the file when finished.</p>
                {/if}
            {/if}
        </div>
    {/if}

    <span
        class="event_file_action flex max-w-full grow flex-row flex-wrap items-center justify-center gap-1">
        {#if session_type == 'poster' || open_method == 'modal'}
            <AE_Comp_Hosted_Files_Download_Button
                hosted_file_id={event_file_id}
                hosted_file_obj={event_file_obj}
                require_auth={false}
                classes="btn btn-sm md:btn-md lg:btn-lg preset-tonal-primary border border-primary-500 w-full"
                click={() => {
                    modal__open_event_file_id = event_file_id;
                    modal__event_file_obj = event_file_obj;
                    if (!modal__title) modal__title = event_file_obj.filename;
                    $events_slct.event_file_id = event_file_id;
                    $events_slct.event_file_obj = event_file_obj;
                    // Push the open command to the remote display when in local_push mode
                    if (
                        $events_loc.launcher.controller == 'local_push' &&
                        $events_sess.launcher.ws_connect_status == 'connected'
                    ) {
                        $events_sess.launcher.controller_cmd = `ae_open:event_file=${event_file_id}`;
                        $events_sess.launcher.controller_trigger_send = true;
                    }
                }}>
                {#snippet label()}
                    {#if screen_saver_exts.includes(event_file_obj.extension)}
                        <BarChart2
                            size="1em"
                            class="{hide_launch_icon ? 'hidden' : ''} m-1" /> Open
                        Poster
                    {:else}
                        <Send
                            size="1em"
                            class="{hide_launch_icon ? 'hidden' : ''} m-1" />
                        {ae_util.shorten_filename({
                            filename: event_file_obj.filename,
                            max_length: max_filename_length
                        })}
                    {/if}
                {/snippet}
            </AE_Comp_Hosted_Files_Download_Button>
        {:else}
            <AE_Comp_Hosted_Files_Download_Button
                hosted_file_id={event_file_id}
                hosted_file_obj={event_file_obj}
                require_auth={false}
                track_click_promise={!($ae_loc.is_native && $events_loc.launcher.app_mode === 'native')}
                classes="btn {btn_size} gap-1 justify-between min-w-full w-full max-w-96 preset-tonal-primary border border-primary-500"
                click={handle_open_file}>
                {#snippet label()}
                    {@const file_id = event_file_obj.hosted_file_id}
                    <span class="shrink border-r border-gray-400 pr-1 text-xs">
                        {#await ae_promises[event_file_id]}
                            <LoaderCircle
                                size="1em"
                                class="mx-0.5 inline animate-spin" />
                            <span>
                                {#if $ae_sess.api_download_kv[file_id]}
                                    {$ae_sess.api_download_kv[file_id]
                                        .percent_completed}%
                                {:else}
                                    ...
                                {/if}
                            </span>
                        {:then result}
                            {#if is_url}
                                <Link2 size="1em" class="mx-0.5 inline {!is_online ? 'text-warning-500' : ''}" />
                                <span class:text-warning-500={!is_online}>url</span>
                                {#if !is_online}<WifiOff size="0.85em" class="mx-0.5 inline text-warning-500" title="Network offline" />{/if}
                            {:else}
                                {@const FileIcon =
                                    ae_util.file_extension_icon_lucide(
                                        event_file_obj.extension
                                    )}
                                <FileIcon size="1em" class="mx-0.5 inline" />
                                {event_file_obj.extension}
                                {#if result === null || result === false}
                                    <span class="text-error-500"
                                        ><AlertTriangle
                                            size="1em"
                                            class="mx-1 inline" />Failed!</span>
                                {/if}
                            {/if}
                        {:catch error}
                            <span class="text-error-500" title={error?.message}
                                ><AlertCircle
                                    size="1em"
                                    class="mx-0.5 inline" />Error!</span>
                        {/await}
                    </span>

                    <span
                        class="grow {text_size} {text_size_md} w-full max-w-full overflow-hidden text-ellipsis {btn_text_align}">
                        {ae_util.shorten_string({
                            string: is_url
                                ? (event_file_obj.title || event_file_obj.filename)
                                : event_file_obj.filename_no_ext,
                            begin_length: 45,
                            max_length: 65
                        })}
                    </span>

                    <span
                        class="badge preset-tonal-success hover:preset-filled-success-500 my-0 py-0.5 text-xs xl:text-sm"
                        class:hidden={!event_file_obj.file_purpose}>
                        {event_file_obj.file_purpose}
                    </span>
                {/snippet}
            </AE_Comp_Hosted_Files_Download_Button>
        {/if}
    </span>

    <span
        class="event_file_meta wrap flex w-64 max-w-80 grow flex-col items-center justify-between gap-1 font-mono text-sm text-gray-500 sm:flex-row"
        class:hidden={hide_meta}>
        <button
            type="button"
            onclick={async () => {
                let new_val: string | null;
                if (!event_file_obj?.open_in_os) new_val = 'win';
                else if (event_file_obj?.open_in_os == 'win') new_val = 'mac';
                else new_val = null;
                await api.update_ae_obj({
                    api_cfg: $ae_api,
                    obj_type: 'event_file',
                    obj_id: event_file_id,
                    fields: { open_in_os: new_val }
                });
                events_func.load_ae_obj_id__event_file({
                    api_cfg: $ae_api,
                    event_file_id: event_file_obj?.event_file_id,
                    log_lvl
                });
            }}
            class="btn btn-sm group transition-all"
            class:preset-tonal-warning={event_file_obj?.open_in_os == 'win'}
            class:preset-tonal-success={event_file_obj?.open_in_os == 'mac'}
            disabled={!$ae_loc.trusted_access}
            title={`Open in OS: ${
                event_file_obj?.open_in_os
                    ? event_file_obj.open_in_os.toUpperCase()
                    : 'None'
                }`}
            >
            {#if event_file_obj?.open_in_os == 'win'}
                <!-- <Monitor
                    size="1em"
                    class="m-1" /> -->
                Win
            {:else if event_file_obj?.open_in_os == 'mac'}
                <!-- <Laptop
                    size="1em"
                    class="m-1" /> -->
                Mac
            {:else}
                <FolderOpen size="1em" class="m-1" />
                {/if}
        </button>

        {#if $ae_loc.trusted_access && $ae_loc.is_native}
            <!-- Display override: per-file display_mode override for this file only.
                 null = use profile default, 'extend' = force extend, 'mirror' = force mirror.
                 Stored in event_file.cfg_json.display_override. Cycles null → extend → mirror → null. -->
            <button
                type="button"
                onclick={async () => {
                    const cur = event_file_obj?.cfg_json?.display_override ?? null;
                    let next: string | null;
                    if (!cur) next = 'extend';
                    else if (cur === 'extend') next = 'mirror';
                    else next = null;
                    const new_cfg = { ...(event_file_obj.cfg_json ?? {}), display_override: next };
                    await api.update_ae_obj({
                        api_cfg: $ae_api,
                        obj_type: 'event_file',
                        obj_id: event_file_id,
                        fields: { cfg_json: new_cfg }
                    });
                    events_func.load_ae_obj_id__event_file({
                        api_cfg: $ae_api,
                        event_file_id: event_file_obj?.event_file_id,
                        log_lvl
                    });
                }}
                class="btn btn-sm transition-all"
                class:preset-tonal-primary={event_file_obj?.cfg_json?.display_override === 'extend'}
                class:preset-tonal-warning={event_file_obj?.cfg_json?.display_override === 'mirror'}
                title={`Display override: ${event_file_obj?.cfg_json?.display_override ?? 'default'}`}>
                {#if event_file_obj?.cfg_json?.display_override === 'extend'}
                    Ext
                {:else if event_file_obj?.cfg_json?.display_override === 'mirror'}
                    Mir
                {:else}
                    <Monitor size="1em" class="m-1" />
                {/if}
            </button>
        {/if}

        <span
            class="event_file_created_on preset-filled-surface-100-900 flex w-24 flex-row items-center justify-end gap-1 rounded px-1 py-0.5 text-center text-xs md:w-44"
            class:hidden={hide_created_on}>
            <CalendarDays size="0.85em" class="inline" />
            <span class="w-18"
                >{ae_util.iso_datetime_formatter(
                    event_file_obj.created_on,
                    'date_short'
                )}</span>
        </span>

        <span
            class="event_file_size preset-filled-surface-100-900 flex w-22 max-w-28 flex-row items-center justify-end gap-1 rounded py-0.5 text-center text-xs"
            class:hidden={hide_size}>
            <Save size="0.85em" class="inline" />
            {#if event_file_obj.file_size}{ae_util.format_bytes(
                    event_file_obj.file_size
                )}{/if}
        </span>
    </span>
</div>

<!-- Native Test Mode Debug Popup -->
<!-- Shows what WOULD be sent to Electron: resolved profile, open command, post-script.
     Appears when native_test_mode is active and a file is "opened". -->
{#if test_mode_popup_open && test_mode_popup_data}
    <!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
    <div
        class="fixed inset-0 z-[9999] flex items-center justify-center bg-black/70 p-4"
        role="presentation"
        onclick={() => (test_mode_popup_open = false)}>
        <!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
        <div
            class="bg-surface-50/95 dark:bg-surface-900/95 border-warning-500/40 relative flex max-h-[90vh] w-full max-w-2xl flex-col gap-0 overflow-hidden rounded-xl border shadow-2xl"
            role="dialog"
            aria-modal="true"
            aria-label="Native Test Mode Debug Info"
            tabindex="-1"
            onclick={(e) => e.stopPropagation()}>

            <!-- Header -->
            <div class="bg-warning-500/10 border-warning-500/30 flex items-center gap-2 border-b px-4 py-3">
                <span class="text-warning-600 dark:text-warning-400 font-mono text-xs font-bold uppercase tracking-wider">
                    🧪 Native Test Mode — What would run on Mac
                </span>
                <button
                    type="button"
                    onclick={() => (test_mode_popup_open = false)}
                    class="btn btn-xs preset-tonal-surface ml-auto">
                    Close
                </button>
            </div>

            <!-- Scrollable content -->
            <div class="flex flex-col gap-3 overflow-y-auto p-4 font-mono text-xs">

                <!-- File info -->
                <div class="flex flex-col gap-1">
                    <span class="text-[9px] font-bold uppercase opacity-50">File</span>
                    <div class="rounded bg-surface-500/10 px-3 py-2 leading-relaxed">
                        {#if test_mode_popup_data.is_url}
                            <div><span class="opacity-50">type: </span><span class="text-warning-500">URL file (no local cache)</span></div>
                            <div><span class="opacity-50">url: </span><span class="text-primary-500 break-all">{test_mode_popup_data.filename}</span></div>
                            {#if test_mode_popup_data.title}
                                <div><span class="opacity-50">title: </span>{test_mode_popup_data.title}</div>
                            {/if}
                        {:else}
                            <div><span class="opacity-50">filename: </span>{test_mode_popup_data.filename}</div>
                            <div><span class="opacity-50">extension: </span>{test_mode_popup_data.extension}</div>
                            <div><span class="opacity-50">hash: </span><span class="opacity-60">{test_mode_popup_data.hash_sha256}</span></div>
                            <div><span class="opacity-50">temp path: </span><span class="text-success-600 dark:text-success-400">{test_mode_popup_data.simulated_temp_path}</span></div>
                        {/if}
                    </div>
                </div>

                <!-- Cache / copy -->
                <div class="flex flex-col gap-1">
                    <span class="text-[9px] font-bold uppercase opacity-50">Steps 1–2</span>
                    <div class="rounded bg-surface-500/10 px-3 py-2 leading-relaxed">
                        {#if test_mode_popup_data.is_url}
                            <div class="opacity-40">Skipped — URL file (no cache download or temp copy)</div>
                        {:else}
                            <div><span class="opacity-50">check_hash_file_cache: </span><span class="text-success-600 dark:text-success-400">{test_mode_popup_data.cache_check}</span></div>
                            <div><span class="opacity-50">copy_from_cache_to_temp: </span><span class="text-success-600 dark:text-success-400">{test_mode_popup_data.copy_to_temp}</span></div>
                        {/if}
                    </div>
                </div>

                <!-- Resolved profile -->
                <div class="flex flex-col gap-1">
                    <span class="text-[9px] font-bold uppercase opacity-50">Resolved LaunchProfile</span>
                    <div class="rounded bg-surface-500/10 px-3 py-2 leading-relaxed">
                        <div><span class="opacity-50">app: </span><span class="text-primary-500">{test_mode_popup_data.profile.app}</span></div>
                        <div><span class="opacity-50">display_mode: </span><span class:text-primary-500={test_mode_popup_data.profile.display_mode === 'extend'} class:text-warning-500={test_mode_popup_data.profile.display_mode === 'mirror'} class:opacity-40={test_mode_popup_data.profile.display_mode === 'none'}>{test_mode_popup_data.profile.display_mode}</span></div>
                        {#if test_mode_popup_data.display_override}
                            <div><span class="text-warning-500 opacity-80">display_override (cfg_json): </span><span class="text-warning-500">{test_mode_popup_data.display_override}</span></div>
                        {/if}
                        <div><span class="opacity-50">post_delay_ms: </span>{test_mode_popup_data.profile.post_delay_ms ?? '(default: 2000ms)'}</div>
                    </div>
                </div>

                <!-- Step 3: set_display_layout -->
                <div class="flex flex-col gap-1">
                    <span class="text-[9px] font-bold uppercase opacity-50">Step 3 — set_display_layout</span>
                    <div class="rounded bg-surface-500/10 px-3 py-2">
                        {#if test_mode_popup_data.profile.display_mode !== 'none'}
                            <span class="text-primary-500">native.set_display_layout({{ mode: '{test_mode_popup_data.profile.display_mode}' }})</span>
                        {:else}
                            <span class="opacity-40">skipped (display_mode: none)</span>
                        {/if}
                    </div>
                </div>

                <!-- Step 4: open command -->
                <div class="flex flex-col gap-1">
                    <span class="text-[9px] font-bold uppercase opacity-50">Step 4 — Open File</span>
                    <div class="rounded bg-surface-500/10 px-3 py-2">
                        {#if test_mode_popup_data.is_url}
                            <div class="mb-1 opacity-50 text-[9px]">native.open_external()</div>
                            <pre class="whitespace-pre-wrap break-all text-primary-500">{test_mode_popup_data.open_cmd_resolved}</pre>
                        {:else if test_mode_popup_data.open_cmd_resolved}
                            <div class="mb-1 opacity-50 text-[9px]">native.run_cmd()</div>
                            <pre class="whitespace-pre-wrap break-all text-success-600 dark:text-success-400">{test_mode_popup_data.open_cmd_resolved}</pre>
                        {:else}
                            <div class="opacity-50 text-[9px]">native.open_local_file_v2()</div>
                            <span class="text-warning-500">{test_mode_popup_data.simulated_temp_path}</span>
                            <span class="ml-2 opacity-40">(OS default handler)</span>
                        {/if}
                    </div>
                </div>

                <!-- Step 5: post-script -->
                <div class="flex flex-col gap-1">
                    <span class="text-[9px] font-bold uppercase opacity-50">Steps 5–6 — Wait + Post-Script</span>
                    <div class="rounded bg-surface-500/10 px-3 py-2">
                        <div class="mb-1 opacity-50">sleep({test_mode_popup_data.profile.post_script ? (test_mode_popup_data.profile.post_delay_ms ?? 2000) : 0}ms){test_mode_popup_data.profile.post_script ? '' : ' — skipped (no post_script)'}</div>
                        {#if test_mode_popup_data.profile.post_script}
                            {#if test_mode_popup_data.profile.post_script.startsWith('shell:')}
                                <div class="mb-1 opacity-50 text-[9px]">native.run_cmd() [shell prefix]</div>
                                <pre class="whitespace-pre-wrap break-all text-warning-500">{test_mode_popup_data.profile.post_script.slice(6)}</pre>
                            {:else}
                                <div class="mb-1 opacity-50 text-[9px]">native.run_osascript() [AppleScript]</div>
                                <pre class="whitespace-pre-wrap text-purple-500">{test_mode_popup_data.profile.post_script}</pre>
                            {/if}
                        {:else}
                            <span class="opacity-40">no post_script</span>
                        {/if}
                    </div>
                </div>

            </div><!-- end scroll -->
        </div>
    </div>
{/if}
