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

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

// *** Import Svelte specific
import { untrack } from 'svelte';
// import { browser } from '$app/environment';

// import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
import Comp_event_session_obj_li_wrapper from '../../../ae_comp__event_session_obj_li_wrapper.svelte';

import { liveQuery } from 'dexie';
import { db_events } from '$lib/ae_events/db_events';
import {
    // ae_snip,
    ae_loc,
    // ae_sess,
    ae_api,
    // ae_trig,
    // slct,
    // slct_trigger
} from '$lib/stores/ae_stores';
import {
    EVENTS_MODULE_TITLE,
    events_sess,
    events_slct,
    // events_trigger
} from '$lib/stores/ae_events_stores';
import { pres_mgmt_loc } from '$lib/stores/ae_events_stores__pres_mgmt.svelte';
import { events_auth_loc } from '$lib/stores/ae_events_stores__auth.svelte';
import { events_func } from '$lib/ae_events/ae_events_functions';

import Comp_event_files_upload from '../../../ae_comp__event_files_upload.svelte';
import Element_manage_event_file_li_wrap from '$lib/elements/element_manage_event_file_li_direct.svelte';
import Event_page_menu from '../ae_comp__event_page_menu.svelte';
import {
    Archive,
    CalendarDays,
    ChevronRight,
    FileText,
    ListChecks,
    LoaderCircle,
    Mails,
    Search,
    TriangleAlert,
    Upload
} from '@lucide/svelte';
import Comp_pres_mgmt_session_search from './ae_comp__pres_mgmt_session_search.svelte';
// Quickly save the data passed from the parent(s) to the Svelte stores, localStorage, and other.
// NOTE: Derived from data.account_id (prop) instead of $slct.account_id (store)
// to prevent circular dependency loops during hydration.
let ae_acct = $derived(data[data.account_id]);
// let event_id = $derived(data.params.event_id);

$effect(() => {
    if (ae_acct) {
        untrack(() => {
            $events_slct.event_id = ae_acct.slct.event_id;
        });
    }
});

// pres_mgmt_loc (PersistedState) is always initialized from pres_mgmt_loc_defaults —
// no guard needed. Fields present in the defaults will never be undefined.

let lq__event_obj = $derived(
    liveQuery(async () => {
        if (log_lvl)
            console.log(
                `*** LiveQuery: lq__event_obj *** event_id=${$events_slct.event_id}`
            );
        return await db_events.event.get($events_slct?.event_id ?? '');
    })
);

// Sync server-side event pres_mgmt config into the local PersistedState store.
// Runs whenever the event object changes (e.g. after a fresh API load).
// The sync function writes directly to pres_mgmt_loc.current — no local_cfg arg needed.
$effect(() => {
    const remote_cfg = $lq__event_obj?.mod_pres_mgmt_json;
    if (remote_cfg) {
        untrack(() => {
            events_func.sync_config__event_pres_mgmt({
                pres_mgmt_cfg_remote: remote_cfg,
                log_lvl: log_lvl
            });
        });
    }
});

let event_session_id_li: Array<string> = $state([]);
let search_debounce_timer: any = null;
let last_search_id = 0;
let last_executed_key = ''; // Search Guard Key

// Stable LiveQuery Pattern (Aether UI V3)
// Use `$derived.by(() => ...)` to build a stable observable instance from
// explicit, plain dependencies (`event_session_id_li`, `event_id`). This
// ensures the `liveQuery` is recreated only when those inputs change and
// avoids accidental recreation from surrounding reactive state.
let lq__event_session_obj_li = $derived.by(() => {
    const ids = event_session_id_li;
    const event_id = $events_slct?.event_id;
    // Captured as a $derived.by dependency so the liveQuery instance is recreated
    // when qry_hidden changes — ensures SCENARIO 2 immediately reflects the new
    // filter without waiting for the debounced search to set event_session_id_li.
    const qry_hidden = pres_mgmt_loc.current.qry_hidden;

    return liveQuery(async () => {
        // SCENARIO 1: Specific IDs provided (Search Results)
        if (Array.isArray(ids) && ids.length > 0) {
            if (log_lvl)
                console.log(`Session Page LQ: bulkGet ${ids.length} IDs`);
            const results = await db_events.session.bulkGet(ids);
            return results.filter((item) => item !== undefined);
        }

        // SCENARIO 2: Fallback broad search (Only if no active filters)
        // Applies the same hide filter as the search fast path to prevent hidden
        // sessions briefly appearing on initial load before the debounce fires.
        if (
            event_id &&
            !pres_mgmt_loc.current.fulltext_search_qry_str &&
            !pres_mgmt_loc.current.location_name_qry_str
        ) {
            if (log_lvl)
                console.log(
                    `Session Page LQ: Fallback search for event: ${event_id}`
                );
            const all = await db_events.session
                .where('event_id')
                .equals(event_id)
                .limit(50)
                .sortBy('name');
            return all.filter((s: any) => {
                if (qry_hidden === 'not_hidden') return !s.hide;
                if (qry_hidden === 'hidden') return !!s.hide;
                return true; // 'all'
            });
        }

        return [];
    });
});

let lq__event_location_obj_li = $derived(
    liveQuery(async () => {
        let results = await db_events.location
            .where('event_id')
            .equals($events_slct.event_id)
            .sortBy('name');
        return results;
    })
);

// Standardized Reactive Search Pattern (Aether UI V3)
let search_params = $derived({
    v: pres_mgmt_loc.current.search_version,
    str: (pres_mgmt_loc.current.fulltext_search_qry_str ?? '')
        .toLowerCase()
        .trim(),
    location: pres_mgmt_loc.current.location_name_qry_str,
    event_id: $events_slct?.event_id,
    remote_first: pres_mgmt_loc.current.qry__remote_first,
    qry_hidden: pres_mgmt_loc.current.qry_hidden,
    enable_time_window: pres_mgmt_loc.current.enable_time_window,
    time_window_before_minutes: pres_mgmt_loc.current.time_window_before_minutes,
    time_window_after_minutes: pres_mgmt_loc.current.time_window_after_minutes
});

$effect(() => {
    const params = search_params;
    if (search_debounce_timer) clearTimeout(search_debounce_timer);
    search_debounce_timer = setTimeout(() => {
        handle_search_refresh(params);
    }, 300);
    return () => {
        if (search_debounce_timer) clearTimeout(search_debounce_timer);
    };
});

async function handle_search_refresh(params: any) {
    // 1. Guard
    const qry_key = JSON.stringify(params);
    if (qry_key === last_executed_key) return;
    last_executed_key = qry_key;

    const current_search_id = ++last_search_id;
    const event_id = params.event_id;
    const remote_first = params.remote_first;

    // Snapshot filter params at search time — avoids stale reads across async boundaries.
    const { enable_time_window, time_window_before_minutes, time_window_after_minutes, qry_hidden } = params;

    // Mirror the API's hide filter in the fast path so IDB results match what the
    // API will return. Without this, hidden sessions briefly appear for trusted users
    // before the API revalidation removes them (the blink).
    //
    // NOTE: only event_session.hide is checked here — NOT hide_event_launcher.
    // hide_event_launcher removes sessions from the Launcher only (used to drop past
    // blocks from the kiosk view). Those sessions remain fully accessible in pres_mgmt.
    function passes_hide_filter(session: { hide?: boolean | null }): boolean {
        if (qry_hidden === 'not_hidden') return !session.hide;
        if (qry_hidden === 'hidden') return !!session.hide;
        return true; // 'all'
    }

    // Sessions with no start_datetime are always shown when the time filter is active
    // (e.g. unscheduled placeholder sessions at BGH).
    function passes_time_window(session: { start_datetime?: Date | string | null }): boolean {
        if (!enable_time_window) return true;
        if (!session.start_datetime) return true;
        const now = Date.now();
        const start = new Date(session.start_datetime).getTime();
        return (
            start >= now - time_window_before_minutes * 60_000 &&
            start <= now + time_window_after_minutes * 60_000
        );
    }

    if (log_lvl)
        console.log(
            `[Session Search #${current_search_id}] Refreshing (remote=${remote_first}, event=${event_id}, str=${params.str})...`
        );

    untrack(() => {
        $events_sess.pres_mgmt.status_qry__search = 'loading';
    });

    const qry_str = params.str;
    const location_name = params.location;

    // 2. FAST PATH: Local IDB Search
    if (!remote_first) {
        try {
            if (event_id) {
                let local_results = await db_events.session
                    .where('event_id')
                    .equals(event_id)
                    .filter((session) => {
                        if (!passes_hide_filter(session)) return false;
                        if (!passes_time_window(session)) return false;

                        if (
                            location_name &&
                            session.event_location_name !== location_name
                        )
                            return false;

                        if (qry_str) {
                            const name = (session.name ?? '').toLowerCase();
                            const code = (session.code ?? '').toLowerCase();
                            const description = (
                                session.description ?? ''
                            ).toLowerCase();
                            const qry_string = (
                                session.default_qry_str ?? ''
                            ).toLowerCase();
                            const presenter_qry = (
                                session.event_presenter_li_qry_str ?? ''
                            ).toLowerCase();
                            const presentation_qry = (
                                session.event_presentation_li_qry_str ?? ''
                            ).toLowerCase();

                            const match =
                                name.includes(qry_str) ||
                                code.includes(qry_str) ||
                                description.includes(qry_str) ||
                                qry_string.includes(qry_str) ||
                                presenter_qry.includes(qry_str) ||
                                presentation_qry.includes(qry_str);

                            if (!match) return false;
                        }
                        return true;
                    })
                    .toArray();

                local_results.sort((a, b) =>
                    (a.name ?? '').localeCompare(b.name ?? '')
                );
                const local_ids = local_results
                    .map((s) => s.id || s.event_session_id)
                    .filter(Boolean);

                if (current_search_id === last_search_id) {
                    if (log_lvl)
                        console.log(
                            `[Session Search #${current_search_id}] Fast Path found ${local_ids.length} items locally.`
                        );
                    untrack(() => {
                        event_session_id_li = local_ids;
                        if (local_ids.length > 0)
                            $events_sess.pres_mgmt.status_qry__search = 'done';
                    });
                }
            }
        } catch (e) {
            if (log_lvl) console.warn('Session Fast Path failed.', e);
        }
    } else {
        untrack(() => {
            event_session_id_li = [];
        });
    }

    // 3. REVALIDATE: API Request
    try {
        const results = await events_func.search__event_session({
            api_cfg: $ae_api,
            event_id: event_id,
            fulltext_search_qry_str: qry_str || null,
            like_search_qry_str: qry_str || null,
            ft_presenter_search_qry_str: qry_str || null,
            ft_presentation_search_qry_str: qry_str || null,
            location_name: location_name || null,
            enabled: pres_mgmt_loc.current.qry_enabled ?? 'enabled',
            hidden: params.qry_hidden ?? 'not_hidden',
            limit: pres_mgmt_loc.current.qry_limit__sessions ?? 100,
            try_cache: true,
            log_lvl: 0
        });

        if (current_search_id === last_search_id) {
            let api_results = results || [];

            // Client-side Filter Guard: Ensure API results match local criteria (Backup filter)
            api_results = api_results.filter((session) => {
                if (!passes_time_window(session)) return false;
                if (
                    location_name &&
                    session.event_location_name !== location_name
                )
                    return false;
                if (qry_str) {
                    const name = (session.name ?? '').toLowerCase();
                    const code = (session.code ?? '').toLowerCase();
                    const description = (
                        session.description ?? ''
                    ).toLowerCase();
                    const qry_string = (
                        session.default_qry_str ?? ''
                    ).toLowerCase();
                    const presenter_qry = (
                        session.event_presenter_li_qry_str ?? ''
                    ).toLowerCase();
                    const presentation_qry = (
                        session.event_presentation_li_qry_str ?? ''
                    ).toLowerCase();
                    const match =
                        name.includes(qry_str) ||
                        code.includes(qry_str) ||
                        description.includes(qry_str) ||
                        qry_string.includes(qry_str) ||
                        presenter_qry.includes(qry_str) ||
                        presentation_qry.includes(qry_str);
                    if (!match) return false;
                }
                return true;
            });

            const api_ids = api_results
                .map((s: any) => s.id || s.event_session_id)
                .filter(Boolean);

            untrack(() => {
                $events_slct.event_session_obj_li = api_results;
                event_session_id_li = api_ids;
                $events_sess.pres_mgmt.status_qry__search = 'done';
            });
            if (log_lvl)
                console.log(
                    `[Session Search #${current_search_id}] Revalidation Complete. Found ${api_ids.length} items.`
                );
        }
    } catch (error) {
        if (current_search_id === last_search_id) {
            console.error('Session revalidation failed:', error);
            untrack(() => {
                $events_sess.pres_mgmt.status_qry__search = 'error';
            });
        }
    }
}

if (
    pres_mgmt_loc.current.save_search_text &&
    pres_mgmt_loc.current.saved_search__session
) {
    pres_mgmt_loc.current.fulltext_search_qry_str =
        pres_mgmt_loc.current.saved_search__session;
}
if (
    pres_mgmt_loc.current.save_search_text &&
    pres_mgmt_loc.current.saved_search__session_location_name
) {
    pres_mgmt_loc.current.location_name_qry_str =
        pres_mgmt_loc.current.saved_search__session_location_name;
}

</script>

<svelte:head>
    <title>
        &AElig;:
        {ae_util.shorten_string({
            string: $lq__event_obj?.name,
            max_length: 12
        })}
        - Pres Mgmt - {EVENTS_MODULE_TITLE}
    </title>
</svelte:head>

<Event_page_menu {lq__event_obj} />

{#if !$lq__event_obj}
    <div class="flex items-center justify-center p-10 opacity-50">
        <LoaderCircle size="1em" class="mx-1 animate-spin text-2xl" />
        <span>Loading event information...</span>
    </div>
{:else if $lq__event_obj?.enable || $ae_loc.trusted_access}
    <header class="ae_module_header">
        <span class="flex flex-row flex-wrap items-center justify-center gap-1">
            <CalendarDays size="1em" class="m-1 text-neutral-800/80" />
            <button
                type="button"
                onclick={() => {
                    if (
                        pres_mgmt_loc.current.show_content__event_view ==
                        'manage_files'
                    ) {
                        pres_mgmt_loc.current.show_content__event_view = null;
                    } else {
                        pres_mgmt_loc.current.show_content__event_view =
                            'manage_files';
                    }
                }}
                class="btn ae_btn_secondary"
                class:preset-filled-secondary-500={pres_mgmt_loc.current.show_content__event_view == 'manage_files'}
                class:preset-filled-tertiary-500={pres_mgmt_loc.current.show_content__event_view != 'manage_files'}
                class:hidden={!$ae_loc.administrator_access}
                title="View event search or manage files for the event">
                {#if pres_mgmt_loc.current.show_content__event_view == 'manage_files'}
                    <Search size="1em" class="m-1" />
                    Event Search?
                {:else}
                    <Archive size="1em" class="m-1" />
                    Event Files?
                    <span
                        class="badge preset-tonal-success"
                        class:hidden={!$lq__event_obj?.file_count}>
                        <FileText size="1em" class="m-1" />
                        {$lq__event_obj?.file_count}×
                    </span>
                {/if}
            </button>
        </span>

        <h2
            class="max-w-xs text-center text-2xl font-bold sm:max-w-lg md:max-w-2xl">
            <span class="sm:inline-block md:hidden">
                {$lq__event_obj.cfg_json?.short_name ?? $lq__event_obj?.name}
            </span>
            <span class="hidden md:inline-block lg:hidden">
                {$lq__event_obj.cfg_json?.med_name ?? $lq__event_obj?.name}
            </span>
            <span class="hidden lg:inline-block">
                {$lq__event_obj.cfg_json?.long_name ?? $lq__event_obj?.name}
            </span>
        </h2>
    </header>

    {#if !pres_mgmt_loc.current.show_content__event_view || pres_mgmt_loc.current.show_content__event_view == 'default'}
        <Comp_pres_mgmt_session_search
            event_location_obj_li={$lq__event_location_obj_li} />

        {#if event_session_id_li.length}
            <Comp_event_session_obj_li_wrapper
                {lq__event_session_obj_li}
                hide__session_location={pres_mgmt_loc.current.hide__session_location ||
                    pres_mgmt_loc.current.hide__session_li_location_field}
                hide__session_poc={pres_mgmt_loc.current.hide__session_poc ||
                    !pres_mgmt_loc.current.show__session_li_poc_field}
                hide__launcher_link={pres_mgmt_loc.current.hide__launcher_link}
                hide__location_link={pres_mgmt_loc.current.hide__location_link}
                log_lvl={1} />
        {:else if $events_sess.pres_mgmt.status_qry__search === 'loading'}
            <div
                class="flex flex-col items-center justify-center p-20 text-center opacity-50">
                <LoaderCircle size="1em" class="mb-4 animate-spin text-4xl" />
                <p class="text-xl">Searching sessions...</p>
            </div>
        {:else}
            <section
                class="preset-tonal-warning mx-auto space-y-4 rounded-xl p-6 shadow-sm lg:max-w-lg"
                role="status"
                aria-live="polite">
                <div class="flex flex-col items-center gap-2 text-center">
                    <Search size="1em" class="text-3xl opacity-50" />
                    <strong class="text-xl">No sessions found</strong>
                    <p class="text-base opacity-80">
                        Use the search bar above to find your session.
                    </p>
                </div>

                {#if pres_mgmt_loc.current.enable_time_window}
                    <!-- Time filter is ON — most common cause of "no results" when
                         sessions exist but are scheduled outside the current window. -->
                    <div class="preset-tonal-error rounded-lg p-3 text-center text-sm font-semibold">
                        ⏰ Time filter is ON — sessions outside the current window are hidden.
                        Disable the clock button in the search bar to see all sessions.<br /><br />
                        This is intended to be used while onsite.
                    </div>
                {/if}
                <div class="bg-surface-50-900/60 rounded-lg p-3">
                    <span
                        class="mb-2 block text-xs font-bold tracking-wide uppercase opacity-50"
                        >Search by any of:</span>
                    <ul class="space-y-1 text-sm">
                        <li class="flex items-center gap-1.5">
                            <ChevronRight
                                size="1em"
                                class="text-xs opacity-50" /> Session name
                        </li>
                        <li class="flex items-center gap-1.5">
                            <ChevronRight
                                size="1em"
                                class="text-xs opacity-50" /> Session description
                        </li>
                        <li class="flex items-center gap-1.5">
                            <ChevronRight
                                size="1em"
                                class="text-xs opacity-50" /> Presentation name
                        </li>
                        <li class="flex items-center gap-1.5">
                            <ChevronRight
                                size="1em"
                                class="text-xs opacity-50" /> Presenter names
                        </li>
                        <li class="flex items-center gap-1.5">
                            <ChevronRight
                                size="1em"
                                class="text-xs opacity-50" /> Presenter ID (member
                            ID)
                        </li>
                    </ul>
                </div>
            </section>
        {/if}
    {:else if pres_mgmt_loc.current.show_content__event_view == 'manage_files' && $ae_loc.trusted_access}
        {#if $lq__event_obj}
            <header>
                <h2 class="h3 text-center">{$lq__event_obj?.name}</h2>
                <h3 class="h4 text-center">Event - Manage Files</h3>
            </header>
        {/if}

        <div>
            <h3 class="h5 text-center">
                <ListChecks size="1em" class="m-1 text-neutral-800/80" />
                <Mails size="1em" class="m-1 text-neutral-800/80" />
                Manage and Upload Event Files:
            </h3>

            <Comp_event_files_upload
                class_li="border border-surface-200-800 rounded-xl p-4 bg-surface-50-900 hover:bg-surface-100-900 transition-colors duration-200"
                link_to_type="event"
                link_to_id={$lq__event_obj?.event_id}>
                {#snippet label()}
                    <span>
                        <div class="text-lg">
                            <Upload size="1em" />
                            <strong class=""
                                >Upload global event files only!</strong>
                        </div>
                        <div class="text-sm italic opacity-60">
                            <strong>Global event files only</strong><br />
                            Recommended: PowerPoint (pptx) or Keynote (key)<br />
                            Media: Audio and videos files should be directly embedded
                            in PowerPoint (PPTX) files<br />
                            Supplemental files: mp4, PDF, Word Doc, Excel, txt, etc
                        </div>
                    </span>
                {/snippet}
            </Comp_event_files_upload>

            <div class="w-max max-w-full overflow-x-auto">
                <Element_manage_event_file_li_wrap
                    link_to_type={'event'}
                    link_to_id={$lq__event_obj?.event_id}
                    allow_basic={events_auth_loc.current.auth__kv.session[
                        $lq__event_obj?.event_id
                    ]}
                    allow_moderator={events_auth_loc.current.auth__kv.session[
                        $lq__event_obj?.event_id
                    ]}
                    container_class_li={''} />
            </div>
        </div>
    {/if}
{:else}
    <div class="rounded-md border border-red-200 bg-red-100 p-4">
        <h2 class="h3">
            <TriangleAlert size="1em" class="m-1 text-red-500" />
            Event Disabled
        </h2>
        <p>
            This event is currently disabled. Please contact the event organizer
            for more information.
        </p>
    </div>
{/if}

<style lang="postcss">
</style>
