<script lang="ts">
interface Props {
    log_lvl?: number;
    lq__event_obj: any;
    event_file_obj_li?: any[];
    qry__status?: string;
    qry__count?: number;
    qry__trigger?: boolean;
    rpt__limit?: number;
}

let {
    log_lvl = $bindable(0),
    lq__event_obj,
    event_file_obj_li = $bindable([]),
    qry__status = $bindable(''),
    qry__count = $bindable(0),
    qry__trigger = $bindable(true),
    rpt__limit = $bindable(500)
}: Props = $props();

import { ae_util } from '$lib/ae_utils/ae_utils';
import { ae_api } from '$lib/stores/ae_stores';
import { events_slct, events_sess } from '$lib/stores/ae_events_stores';
import { events_func } from '$lib/ae_events/ae_events_functions';
import MyClipboard from '$lib/app_components/e_app_clipboard.svelte';
import {
    ArrowUpDown,
    Download,
    ExternalLink,
    FileText,
    LoaderCircle,
    RefreshCw,
    TriangleAlert,
    User
} from '@lucide/svelte';

// ---------------------------------------------------------------------------
// Filename helpers
// ---------------------------------------------------------------------------

let prefix_val = $state('');
let suffix_val = $state('');

// For prefix/suffix: clean OS-unsafe chars and periods, but preserve leading/trailing
// underscores — users intentionally use them as separators (e.g. "2026_06__").
function clean_affix(s: string): string {
    if (!s) return '';
    return ae_util.clean_filename(s).replace(/\./g, '_');
}

// For filename parts: also strip leading/trailing underscores and collapse runs.
function clean_part(s: any, max: number | undefined = undefined): string {
    if (!s) return '';
    const out = ae_util
        .clean_filename(String(s))
        .replace(/\./g, '_')
        .replace(/_+/g, '_')
        .replace(/^_|_$/g, '');
    return max ? out.substring(0, max) : out;
}

function date_part(dt: string | null | undefined): string {
    return dt ? String(dt).substring(0, 10) : ''; // 'YYYY-MM-DD'
}

function time_part(dt: string | null | undefined): string {
    if (!dt) return '';
    return String(dt).substring(11, 16).replace(':', '-'); // 'HH-MM'
}

type FormatKey =
    | 'original'
    | 'session_code'
    | 'session_code_name'
    | 'session_code_date'
    | 'session_code_date_time'
    | 'session_code_date_name'
    | 'session_code_date_time_name'
    | 'session_presenter'
    | 'session_date_presenter'
    | 'session_presentation_presenter'
    | 'session_date_presentation_presenter'
    | 'session_date_stime_ptime_presenter_full';

const FORMAT_PRESETS: Record<FormatKey, { label: string; group: string }> = {
    original:                             { label: 'Original filename',                                                         group: 'Universal' },
    session_code:                         { label: 'Session Code',                                                              group: 'Session' },
    session_code_name:                    { label: 'Session Code + Name',                                                       group: 'Session' },
    session_code_date:                    { label: 'Session Code + Date',                                                       group: 'Session' },
    session_code_date_time:               { label: 'Session Code + Date + Time',                                                group: 'Session' },
    session_code_date_name:               { label: 'Session Code + Date + Name',                                                group: 'Session' },
    session_code_date_time_name:          { label: 'Session Code + Date + Time + Name',                                         group: 'Session' },
    session_presenter:                    { label: 'Session Code + Presenter (Family, Given)',                                   group: 'Presenter' },
    session_date_presenter:               { label: 'Session Code + Date + Presenter',                                           group: 'Presenter' },
    session_presentation_presenter:       { label: 'Session Code + Presentation + Presenter',                                   group: 'Presenter' },
    session_date_presentation_presenter:  { label: 'Session Code + Date + Presentation + Presenter (Full)',                     group: 'Presenter' },
    session_date_stime_ptime_presenter_full: { label: 'Session Code + Date + Session Time + Presentation Time + Presenter Full', group: 'Presenter' }
};

const FORMAT_GROUPS: Record<string, FormatKey[]> = {
    Universal:  ['original'],
    Session:    ['session_code', 'session_code_name', 'session_code_date', 'session_code_date_time', 'session_code_date_name', 'session_code_date_time_name'],
    Presenter:  ['session_presenter', 'session_date_presenter', 'session_presentation_presenter', 'session_date_presentation_presenter', 'session_date_stime_ptime_presenter_full']
};

let selected_format = $state<FormatKey>('session_code_name');

const FILENAME_WARN_LEN = 120; // show warning above this char count

function build_filename(file: any, fmt: FormatKey): string {
    const ext  = file.extension ?? 'file';
    const pre  = clean_affix(prefix_val);
    const suf  = clean_affix(suffix_val);

    let stem: string;
    if (fmt === 'original') {
        // Preserve the original stem as-is; only the prefix/suffix are user-controlled.
        stem = file.filename_no_ext ?? (file.filename ?? 'file').replace(/\.[^.]+$/, '');
    } else {
        const session_code    = clean_part(file.event_session_code);
        const session_name    = clean_part(file.event_session_name, 30);
        const session_date    = date_part(file.event_session_start_datetime);
        const session_time    = time_part(file.event_session_start_datetime);
        const pres_name       = clean_part(file.event_presentation_name ?? file.event_session_name, 25);
        const pres_time       = time_part(file.event_presentation_start_datetime);
        const fam             = clean_part(file.event_presenter_family_name);
        const given           = clean_part(file.event_presenter_given_name);
        const presenter_full  = clean_part(file.event_presenter_full_name);

        let parts: string[];
        switch (fmt) {
            case 'session_code':                           parts = [session_code]; break;
            case 'session_code_name':                      parts = [session_code, session_name]; break;
            case 'session_code_date':                      parts = [session_code, session_date]; break;
            case 'session_code_date_time':                 parts = [session_code, session_date, session_time]; break;
            case 'session_code_date_name':                 parts = [session_code, session_date, session_name]; break;
            case 'session_code_date_time_name':            parts = [session_code, session_date, session_time, session_name]; break;
            case 'session_presenter':                      parts = [session_code, fam, given]; break;
            case 'session_date_presenter':                 parts = [session_code, session_date, fam, given]; break;
            case 'session_presentation_presenter':         parts = [session_code, pres_name, fam, given]; break;
            case 'session_date_presentation_presenter':    parts = [session_code, session_date, pres_name, fam, given]; break;
            case 'session_date_stime_ptime_presenter_full': parts = [session_code, session_date, session_time, pres_time, presenter_full]; break;
            default: stem = file.filename_no_ext ?? 'file'; return `${pre}${stem}${suf}.${ext}`;
        }
        stem = parts.filter(Boolean).join('_').replace(/_+/g, '_').replace(/^_|_$/, '');
    }

    return `${pre}${stem}${suf}.${ext}`;
}

// Returns the largest created_on string in a file list — identifies the newest upload per group.
function newest_in_group(files: { created_on?: string | null }[]): string {
    return files.reduce((max, f) => ((f.created_on ?? '') > max ? (f.created_on ?? '') : max), '');
}

// Strip :443 from https URLs — redundant and clutters shareable links.
let base_url = $derived($ae_api.base_url.replace(/^(https:\/\/[^/:]+):443(\/|$)/, '$1$2'));

function build_download_url(file: any, fmt: FormatKey): string {
    const fname = build_filename(file, fmt);
    return encodeURI(
        `${base_url}/v3/action/event_file/${file.event_file_id}/download?filename=${ae_util.clean_filename(fname)}&key=${$ae_api.account_id}`
    );
}

// Longest computed filename across all files — reactive to format + prefix + suffix changes.
let max_computed_len = $derived.by(() => {
    let max = 0;
    for (const sg of session_groups) {
        for (const f of sg.session_files) {
            const len = build_filename(f, selected_format).length;
            if (len > max) max = len;
        }
        for (const pg of sg.presenter_groups) {
            for (const f of pg.files) {
                const len = build_filename(f, selected_format).length;
                if (len > max) max = len;
            }
        }
    }
    return max;
});

// ---------------------------------------------------------------------------
// Grouping
// ---------------------------------------------------------------------------

type PresenterGroup = {
    presenter_id: string;
    presenter_full_name: string;
    presenter_family_name: string;
    presenter_given_name: string;
    presentation_name: string | null;
    files: any[];
};

type SessionGroup = {
    session_id: string;
    session_code: string;
    session_name: string;
    session_start_datetime: string | null;
    session_files: any[];
    presenter_groups: PresenterGroup[];
    _presenter_map: Record<string, PresenterGroup>;
};

let session_groups = $derived.by((): SessionGroup[] => {
    const map: Record<string, SessionGroup> = {};

    for (const file of event_file_obj_li ?? []) {
        if (file.for_type !== 'event_session' && file.for_type !== 'event_presenter') continue;

        const sid = file.event_session_id ?? '__no_session__';
        if (!map[sid]) {
            map[sid] = {
                session_id: sid,
                session_code: file.event_session_code ?? '',
                session_name: file.event_session_name ?? '',
                session_start_datetime: file.event_session_start_datetime ?? null,
                session_files: [],
                presenter_groups: [],
                _presenter_map: {}
            };
        }
        const sg = map[sid];

        if (file.for_type === 'event_session') {
            sg.session_files.push(file);
        } else {
            const pid = file.event_presenter_id ?? '__unknown__';
            if (!sg._presenter_map[pid]) {
                const pg: PresenterGroup = {
                    presenter_id: pid,
                    presenter_full_name: file.event_presenter_full_name ?? '',
                    presenter_family_name: file.event_presenter_family_name ?? '',
                    presenter_given_name: file.event_presenter_given_name ?? '',
                    presentation_name: file.event_presentation_name ?? null,
                    files: []
                };
                sg._presenter_map[pid] = pg;
                sg.presenter_groups.push(pg);
            }
            sg._presenter_map[pid].files.push(file);
        }
    }

    const sorted = Object.values(map).sort((a, b) => {
        if (a.session_start_datetime && b.session_start_datetime)
            return a.session_start_datetime.localeCompare(b.session_start_datetime);
        return a.session_name.localeCompare(b.session_name);
    });

    // Sort presenter groups within each session client-side (family name, then given name).
    // Presenter-level columns are view-only joined fields — per API guide §3B they are
    // silently dropped from ORDER BY, so sorting must be done client-side.
    for (const sg of sorted) {
        sg.presenter_groups.sort((a, b) => {
            const fam = a.presenter_family_name.localeCompare(b.presenter_family_name);
            if (fam !== 0) return fam;
            return a.presenter_given_name.localeCompare(b.presenter_given_name);
        });
    }

    return sorted;
});

let total_sessions   = $derived(session_groups.length);
let total_presenters = $derived(session_groups.reduce((n, sg) => n + sg.presenter_groups.length, 0));

// ---------------------------------------------------------------------------
// Query
// ---------------------------------------------------------------------------

$effect(() => {
    if (qry__trigger) {
        qry__trigger = false;
        handle_qry();
    }
});

async function handle_qry() {
    qry__status = 'loading';
    qry__count = 0;
    $events_sess.pres_mgmt.status_qry__search = 'loading';
    $events_sess.pres_mgmt.status_rpt.file_downloads = 'loading';
    event_file_obj_li = [];

    try {
        // ORDER BY only uses raw event_file table columns + session-join columns.
        // Presenter-level columns (event_presenter_family_name etc.) are view-only joined
        // fields — per API guide §3B they are silently dropped from ORDER BY.
        // Sort presenter groups client-side for predictable ordering.
        const results = await events_func.qry__event_file({
            api_cfg: $ae_api,
            event_id: $events_slct.event_id,
            enabled: 'enabled',
            hidden: 'not_hidden',
            view: 'alt',  // required for joined fields: session code/name, presenter name, presentation name
            limit: rpt__limit,
            order_by_li: {
                event_session_start_datetime: 'ASC',
                event_session_name: 'ASC',
                priority: 'DESC',
                sort: 'DESC',
                created_on: 'DESC'
            },
            log_lvl
        }) ?? [];

        event_file_obj_li = results;
        qry__count = results.length;
        qry__status = 'done';
    } catch {
        qry__status = 'error';
    } finally {
        $events_sess.pres_mgmt.status_qry__search = 'done';
        $events_sess.pres_mgmt.status_rpt.file_downloads = 'done';
    }
}
</script>

<!-- ------------------------------------------------------------------ -->
<!-- Header / controls                                                    -->
<!-- ------------------------------------------------------------------ -->
<header class="flex w-full flex-col gap-2">
    <div class="flex flex-row flex-wrap items-center justify-between gap-2">
        <h3 class="h4 flex flex-row items-center gap-1">
            {#if qry__status === 'loading'}
                <LoaderCircle size="1em" class="animate-spin" />
            {:else}
                <Download size="1em" />
            {/if}
            File Downloads
            {#if qry__count}
                <span class="badge preset-tonal-surface text-sm">{qry__count} files</span>
            {/if}
            <span class="text-sm font-normal opacity-60">
                &nbsp;({total_sessions} session{total_sessions !== 1 ? 's' : ''} &nbsp;/ {total_presenters} presenter{total_presenters !== 1 ? 's' : ''})
            </span>
        </h3>

        <button
            type="button"
            class="btn btn-sm preset-tonal-tertiary border-tertiary-500/30 border"
            title="Re-fetch files from the API"
            onclick={() => { qry__trigger = true; }}>
            <RefreshCw size="1em" class="mr-1" />
            Refresh
        </button>
    </div>

    <!-- Format selector -->
    <div class="flex flex-row flex-wrap items-center gap-2">
        <label class="flex flex-row items-center gap-2 text-sm font-semibold">
            <ArrowUpDown size="1em" />
            Format:
            <select
                class="select ae_btn_info w-auto max-w-xs text-sm"
                bind:value={selected_format}>
                {#each Object.entries(FORMAT_GROUPS) as [group_label, keys] (group_label)}
                    <optgroup label={group_label}>
                        {#each keys as key (key)}
                            <option value={key}>{FORMAT_PRESETS[key].label}</option>
                        {/each}
                    </optgroup>
                {/each}
            </select>
        </label>
    </div>

    <!-- Prefix / suffix -->
    <div class="flex flex-row flex-wrap items-center gap-3 text-sm">
        <label class="flex flex-row items-center gap-1 font-semibold">
            Prefix:
            <input
                type="text"
                class="input text-sm w-48"
                placeholder="e.g. 2026_06__"
                bind:value={prefix_val} />
        </label>
        <label class="flex flex-row items-center gap-1 font-semibold">
            Suffix:
            <input
                type="text"
                class="input text-sm w-36"
                placeholder="e.g. __final"
                bind:value={suffix_val} />
        </label>
        {#if qry__count > 0}
            <span
                class="text-xs"
                class:text-warning-600={max_computed_len > FILENAME_WARN_LEN}
                class:opacity-50={max_computed_len <= FILENAME_WARN_LEN}
                title="Longest computed filename across all files">
                {#if max_computed_len > FILENAME_WARN_LEN}
                    <TriangleAlert size="0.9em" class="mr-0.5 inline-block" />
                {/if}
                longest: {max_computed_len} chars
            </span>
        {/if}
    </div>
</header>

<!-- ------------------------------------------------------------------ -->
<!-- Results                                                              -->
<!-- ------------------------------------------------------------------ -->

{#if qry__status === 'loading'}
    <div class="flex items-center gap-2 p-4">
        <LoaderCircle size="1em" class="animate-spin" />
        <span>Loading files…</span>
    </div>
{:else if qry__status === 'error'}
    <div class="preset-tonal-error rounded-md p-4">
        Failed to load files. Check your connection and try refreshing.
    </div>
{:else if session_groups.length === 0 && qry__status === 'done'}
    <p class="text-surface-500 p-4 text-center italic">
        No session or presenter files found for this event.
    </p>
{:else}
    {#each session_groups as sg (sg.session_id)}
        {@const has_any = sg.session_files.length > 0 || sg.presenter_groups.length > 0}
        {@const newest_session_ts = newest_in_group(sg.session_files)}
        {#if has_any}
            <!-- Session card -->
            <section class="border-surface-300-700 bg-surface-50-900 my-2 rounded-lg border">
                <!-- Session header -->
                <div class="bg-surface-200-800 flex flex-row flex-wrap items-baseline gap-2 rounded-t-lg px-3 py-2">
                    {#if sg.session_code}
                        <span class="badge preset-tonal-primary font-mono text-xs font-bold">{sg.session_code}</span>
                    {/if}
                    <span class="font-semibold">{sg.session_name || '— session name not set —'}</span>
                    {#if sg.session_start_datetime}
                        <span class="text-surface-500 text-xs">
                            {ae_util.iso_datetime_formatter(sg.session_start_datetime, 'date_iso')}
                            &nbsp;{ae_util.iso_datetime_formatter(sg.session_start_datetime, 'time_12_short_no_leading')}
                        </span>
                    {/if}
                    <span class="text-surface-400 ml-auto text-xs">
                        {sg.session_files.length} session file{sg.session_files.length !== 1 ? 's' : ''},
                        &nbsp;{sg.presenter_groups.reduce((n, pg) => n + pg.files.length, 0)} presenter file{sg.presenter_groups.reduce((n, pg) => n + pg.files.length, 0) !== 1 ? 's' : ''}
                    </span>
                    {#if sg.session_id !== '__no_session__'}
                        <a
                            href="/events/{$events_slct.event_id}/session/{sg.session_id}"
                            class="btn btn-xs preset-tonal-surface shrink-0 opacity-50 hover:opacity-100 transition-opacity"
                            title="View session: {sg.session_name}">
                            <ExternalLink size="0.8em" class="mr-0.5" />
                            Session
                        </a>
                    {/if}
                </div>

                <div class="divide-surface-200-800 divide-y px-2 py-1">

                    <!-- ---- Session-level files ---- -->
                    {#if sg.session_files.length > 0}
                        <div class="py-2">
                            <h4 class="mb-1 flex items-center gap-1 text-xs font-bold tracking-wider opacity-60">
                                <FileText size="0.9em" />
                                Session Files ({sg.session_files.length})
                            </h4>
                            <div class="flex flex-col">
                                {#each sg.session_files as file (file.event_file_id)}
                                    {@const ExtIcon = ae_util.file_extension_icon_lucide(file.extension)}
                                    {@const computed_name = build_filename(file, selected_format)}
                                    {@const dl_url = build_download_url(file, selected_format)}
                                    {@const is_long = computed_name.length > FILENAME_WARN_LEN}
                                    {@const is_newest = !!file.created_on && file.created_on === newest_session_ts}
                                    <div class="border-surface-100-900 hover:bg-surface-100-900 flex flex-row items-center gap-2 border-t py-1.5 transition-colors">
                                        <!-- Left: filenames (grows, min-w-0 allows truncation) -->
                                        <div class="flex min-w-0 grow flex-col gap-0.5">
                                            <span class="flex items-center gap-1 truncate text-xs opacity-50" title={file.filename}>
                                                <ExtIcon size="0.9em" class="shrink-0" />
                                                {ae_util.shorten_filename({ filename: file.filename, max_length: 40 })}
                                            </span>
                                            <span
                                                class="truncate font-mono text-xs"
                                                class:opacity-70={!is_long}
                                                class:text-warning-600={is_long}
                                                title="{computed_name}{is_long ? ` (${computed_name.length} chars — may be long for some systems)` : ''}">
                                                {computed_name}
                                            </span>
                                            {#if file.created_on}
                                                <span
                                                    class="tabular-nums text-xs"
                                                    class:opacity-40={!is_newest}
                                                    class:font-bold={is_newest}
                                                    title="Uploaded: {file.created_on}">
                                                    {ae_util.iso_datetime_formatter(file.created_on, 'date_iso')}
                                                    &nbsp;{ae_util.iso_datetime_formatter(file.created_on, 'time_12_short_no_leading')}
                                                </span>
                                            {/if}
                                        </div>
                                        <!-- File size -->
                                        <span class="shrink-0 whitespace-nowrap text-xs opacity-50">
                                            {ae_util.format_bytes(file.file_size)}
                                        </span>
                                        <!-- Buttons -->
                                        <div class="flex shrink-0 flex-row items-center gap-1">
                                            <a
                                                href={dl_url}
                                                download={computed_name}
                                                class="btn btn-xs preset-tonal-primary border-primary-500/30 border"
                                                title="Download as: {computed_name}">
                                                <Download size="0.9em" class="mr-0.5" />
                                                Download
                                            </a>
                                            <MyClipboard
                                                value={dl_url}
                                                btn_text="Copy Link"
                                                btn_title="Copy direct download link: {computed_name}"
                                                btn_class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 border-warning-500/30 border"
                                            />
                                        </div>
                                    </div>
                                {/each}
                            </div>
                        </div>
                    {/if}

                    <!-- ---- Presenter-level files ---- -->
                    {#each sg.presenter_groups as pg (pg.presenter_id)}
                        {@const newest_presenter_ts = newest_in_group(pg.files)}
                        <div class="py-2">
                            <!-- Wrap h4 + link so the link sits outside the opacity-60 container -->
                            <div class="mb-1 flex items-center gap-2">
                                <h4 class="flex grow items-center gap-1 text-xs font-bold tracking-wider opacity-60">
                                    <User size="0.9em" />
                                    {pg.presenter_full_name || '— presenter name not set —'}
                                    {#if pg.presentation_name}
                                        <span class="font-normal normal-case opacity-80">— {pg.presentation_name}</span>
                                    {/if}
                                    <span class="font-normal normal-case">({pg.files.length} file{pg.files.length !== 1 ? 's' : ''})</span>
                                </h4>
                                {#if pg.presenter_id !== '__unknown__'}
                                    <a
                                        href="/events/{$events_slct.event_id}/presenter/{pg.presenter_id}"
                                        class="btn btn-xs preset-tonal-surface shrink-0 opacity-50 hover:opacity-100 transition-opacity"
                                        title="View presenter: {pg.presenter_full_name}">
                                        <ExternalLink size="0.75em" class="mr-0.5" />
                                        Presenter
                                    </a>
                                {/if}
                            </div>
                            <div class="flex flex-col">
                                {#each pg.files as file (file.event_file_id)}
                                    {@const ExtIcon = ae_util.file_extension_icon_lucide(file.extension)}
                                    {@const computed_name = build_filename(file, selected_format)}
                                    {@const dl_url = build_download_url(file, selected_format)}
                                    {@const is_long = computed_name.length > FILENAME_WARN_LEN}
                                    {@const is_newest = !!file.created_on && file.created_on === newest_presenter_ts}
                                    <div class="border-surface-100-900 hover:bg-surface-100-900 flex flex-row items-center gap-2 border-t py-1.5 transition-colors">
                                        <!-- Left: filenames (grows, min-w-0 allows truncation) -->
                                        <div class="flex min-w-0 grow flex-col gap-0.5">
                                            <span class="flex items-center gap-1 truncate text-xs opacity-50" title={file.filename}>
                                                <ExtIcon size="0.9em" class="shrink-0" />
                                                {ae_util.shorten_filename({ filename: file.filename, max_length: 40 })}
                                            </span>
                                            <span
                                                class="truncate font-mono text-xs"
                                                class:opacity-70={!is_long}
                                                class:text-warning-600={is_long}
                                                title="{computed_name}{is_long ? ` (${computed_name.length} chars — may be long for some systems)` : ''}">
                                                {computed_name}
                                            </span>
                                            {#if file.created_on}
                                                <span
                                                    class="tabular-nums text-xs"
                                                    class:opacity-40={!is_newest}
                                                    class:font-bold={is_newest}
                                                    title="Uploaded: {file.created_on}">
                                                    {ae_util.iso_datetime_formatter(file.created_on, 'date_iso')}
                                                    &nbsp;{ae_util.iso_datetime_formatter(file.created_on, 'time_12_short_no_leading')}
                                                </span>
                                            {/if}
                                        </div>
                                        <!-- File size -->
                                        <span class="shrink-0 whitespace-nowrap text-xs opacity-50">
                                            {ae_util.format_bytes(file.file_size)}
                                        </span>
                                        <!-- Buttons -->
                                        <div class="flex shrink-0 flex-row items-center gap-1">
                                            <a
                                                href={dl_url}
                                                download={computed_name}
                                                class="btn btn-xs preset-tonal-primary border-primary-500/30 border"
                                                title="Download as: {computed_name}">
                                                <Download size="0.9em" class="mr-0.5" />
                                                Download
                                            </a>
                                            <MyClipboard
                                                value={dl_url}
                                                btn_text="Copy Link"
                                                btn_title="Copy direct download link: {computed_name}"
                                                btn_class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 border-warning-500/30 border"
                                            />
                                        </div>
                                    </div>
                                {/each}
                            </div>
                        </div>
                    {/each}

                </div>
            </section>
        {/if}
    {/each}
{/if}
