<script lang="ts">
interface Props {
    badge_li: any[];
    event_id: string;
    event_name?: string;
    log_lvl?: number;
}
let { badge_li, event_id, event_name = '', log_lvl = 0 }: Props = $props();

import { Download, FileSpreadsheet } from '@lucide/svelte';
import { ae_util } from '$lib/ae_utils/ae_utils';

// ---------------------------------------------------------------------------
// Filter
// ---------------------------------------------------------------------------
type FilterMode = 'printed_clean' | 'printed' | 'all';
let filter_mode: FilterMode = $state('printed_clean');

// ---------------------------------------------------------------------------
// Timezone
// ---------------------------------------------------------------------------
type TzMode = 'eastern' | 'local' | 'utc';
let tz_mode: TzMode = $state('eastern');

const TZ_LABEL: Record<TzMode, string> = {
    eastern: 'Eastern (US)',
    local: 'Local (browser)',
    utc: 'UTC'
};

function get_tz(): string | undefined {
    if (tz_mode === 'eastern') return 'America/New_York';
    if (tz_mode === 'utc') return 'UTC';
    return undefined; // browser local
}

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

// Backend returns naive UTC strings with no TZ indicator — append Z so
// the browser parses them as UTC before converting to the chosen timezone.
function parse_utc(dt: string | null | undefined): Date | null {
    if (!dt) return null;
    const s = dt.match(/Z$|[+-]\d{2}:?\d{2}$/) ? dt : dt + 'Z';
    const d = new Date(s);
    return isNaN(d.getTime()) ? null : d;
}

function fmt_dt(dt: string | null | undefined): string {
    const d = parse_utc(dt);
    if (!d) return '';
    const tz = get_tz();
    try {
        return new Intl.DateTimeFormat('en-US', {
            year: 'numeric',
            month: '2-digit',
            day: '2-digit',
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit',
            hour12: false,
            ...(tz ? { timeZone: tz } : {})
        })
            .format(d)
            .replace(',', '');
    } catch {
        return d.toISOString();
    }
}

function csv_cell(v: any): string {
    if (v === null || v === undefined) return '';
    const s = String(v);
    if (s.includes(',') || s.includes('\n') || s.includes('"')) {
        return '"' + s.replace(/"/g, '""') + '"';
    }
    return s;
}

// ---------------------------------------------------------------------------
// Filter
// ---------------------------------------------------------------------------
let filtered_badges = $derived.by(() => {
    if (!badge_li?.length) return [];
    switch (filter_mode) {
        case 'printed_clean':
            // Mirrors what was sent to Axonius: printed, not hidden, not test badge type
            return badge_li.filter(
                (b) =>
                    (b.print_count ?? 0) >= 1 &&
                    !b.hide &&
                    (b.badge_type_code ?? '').toLowerCase() !== 'test'
            );
        case 'printed':
            return badge_li.filter((b) => (b.print_count ?? 0) >= 1);
        default:
            return badge_li;
    }
});

let print_count_total = $derived(
    filtered_badges.filter((b) => (b.print_count ?? 0) >= 1).length
);

// ---------------------------------------------------------------------------
// CSV columns
// ---------------------------------------------------------------------------
interface ColDef {
    header: string;
    value: (b: any) => any;
}

const COLUMNS: ColDef[] = [
    { header: 'external_id', value: (b) => b.external_id ?? '' },
    { header: 'given_name', value: (b) => b.given_name ?? '' },
    { header: 'family_name', value: (b) => b.family_name ?? '' },
    { header: 'full_name', value: (b) => b.full_name ?? '' },
    { header: 'full_name_override', value: (b) => b.full_name_override ?? '' },
    { header: 'professional_title', value: (b) => b.professional_title ?? '' },
    { header: 'professional_title_override', value: (b) => b.professional_title_override ?? '' },
    { header: 'affiliations', value: (b) => b.affiliations ?? '' },
    { header: 'affiliations_override', value: (b) => b.affiliations_override ?? '' },
    { header: 'email', value: (b) => b.email ?? '' },
    { header: 'city', value: (b) => b.city ?? '' },
    { header: 'state_province', value: (b) => b.state_province ?? '' },
    { header: 'country', value: (b) => b.country ?? '' },
    { header: 'location', value: (b) => b.location ?? '' },
    { header: 'location_override', value: (b) => b.location_override ?? '' },
    { header: 'badge_type_code', value: (b) => b.badge_type_code ?? '' },
    { header: 'badge_type', value: (b) => b.badge_type ?? '' },
    { header: 'allow_tracking', value: (b) => (b.allow_tracking ? 1 : 0) },
    { header: 'agree_to_tc', value: (b) => (b.agree_to_tc ? 1 : 0) },
    { header: 'print_first_datetime', value: (b) => fmt_dt(b.print_first_datetime) },
    { header: 'print_last_datetime', value: (b) => fmt_dt(b.print_last_datetime) },
    { header: 'print_count', value: (b) => b.print_count ?? 0 },
    { header: 'created_on', value: (b) => fmt_dt(b.created_on) },
    { header: 'updated_on', value: (b) => fmt_dt(b.updated_on) },
    { header: 'event_badge_id', value: (b) => b.event_badge_id ?? '' }
];

// ---------------------------------------------------------------------------
// Download
// ---------------------------------------------------------------------------
let is_downloading = $state(false);

function build_filename(): string {
    const name_slug = ae_util
        .clean_filename(event_name || 'event')
        .replace(/\./g, '_')
        .replace(/_+/g, '_')
        .replace(/^_|_$/g, '')
        .substring(0, 30);
    const today = new Date().toISOString().substring(0, 10);
    const filter_tag =
        filter_mode === 'printed_clean'
            ? '_badges_printed'
            : filter_mode === 'printed'
              ? '_badges_printed_all'
              : '_badges_all';
    return `${name_slug}${filter_tag}_${today}.csv`;
}

function download_csv() {
    if (is_downloading) return;
    is_downloading = true;
    try {
        const rows = filtered_badges;
        const header_row = COLUMNS.map((c) => csv_cell(c.header)).join(',');
        const data_rows = rows.map((b) => COLUMNS.map((c) => csv_cell(c.value(b))).join(','));
        // UTF-8 BOM so Excel opens it correctly without needing an import wizard
        const csv_content = '﻿' + [header_row, ...data_rows].join('\r\n');
        const blob = new Blob([csv_content], { type: 'text/csv;charset=utf-8;' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = build_filename();
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    } finally {
        is_downloading = false;
    }
}
</script>

<div class="space-y-4">
    <!-- Filter options -->
    <div class="flex flex-row flex-wrap items-start gap-4">
        <div class="space-y-1">
            <p class="text-xs font-semibold uppercase tracking-wide text-surface-500-400">Records</p>
            <div class="flex flex-row items-center gap-1">
                {#each ([['printed_clean', 'Printed + Clean'], ['printed', 'Printed (all)'], ['all', 'All badges']] as [FilterMode, string][]) as [mode, label] (mode)}
                    <button
                        type="button"
                        onclick={() => (filter_mode = mode)}
                        class="btn btn-sm border transition-all"
                        class:preset-filled-primary={filter_mode === mode}
                        class:border-primary-600={filter_mode === mode}
                        class:preset-tonal-surface={filter_mode !== mode}
                        class:border-surface-300-700={filter_mode !== mode}
                        title={mode === 'printed_clean'
                            ? 'Printed badges, excluding hidden records and test badge types — matches the standard client export'
                            : mode === 'printed'
                              ? 'All printed badges regardless of hide flag or badge type'
                              : 'All badge records in local cache, including unprinted'}>
                        {label}
                    </button>
                {/each}
            </div>
        </div>

        <div class="space-y-1">
            <p class="text-xs font-semibold uppercase tracking-wide text-surface-500-400">Timestamps</p>
            <div class="flex flex-row items-center gap-1">
                {#each (['eastern', 'local', 'utc'] as TzMode[]) as tz (tz)}
                    <button
                        type="button"
                        onclick={() => (tz_mode = tz)}
                        class="btn btn-sm border transition-all"
                        class:preset-filled-primary={tz_mode === tz}
                        class:border-primary-600={tz_mode === tz}
                        class:preset-tonal-surface={tz_mode !== tz}
                        class:border-surface-300-700={tz_mode !== tz}>
                        {TZ_LABEL[tz]}
                    </button>
                {/each}
            </div>
        </div>
    </div>

    <!-- Summary + download button -->
    <div class="flex flex-row flex-wrap items-center gap-3">
        <div class="text-sm text-surface-600-400">
            {#if filtered_badges.length === 0}
                No records match this filter.
            {:else}
                {filtered_badges.length} record{filtered_badges.length !== 1 ? 's' : ''}
                {#if filter_mode !== 'all'}
                    ({print_count_total} printed)
                {/if}
                · {COLUMNS.length} columns · timestamps in {TZ_LABEL[tz_mode]}
            {/if}
        </div>

        <button
            type="button"
            onclick={download_csv}
            disabled={is_downloading || filtered_badges.length === 0}
            class="btn btn-sm preset-filled-primary border border-primary-600 flex items-center gap-1.5 disabled:opacity-50">
            <FileSpreadsheet size="1em" />
            Download CSV
            {#if filtered_badges.length > 0}
                <span class="opacity-75">({filtered_badges.length})</span>
            {/if}
        </button>
    </div>

    <!-- Column list for reference -->
    <details class="text-xs text-surface-500-400">
        <summary class="cursor-pointer hover:text-surface-700-300 transition-colors">
            Columns included ({COLUMNS.length})
        </summary>
        <div class="mt-1.5 flex flex-row flex-wrap gap-1 pl-2">
            {#each COLUMNS as col (col.header)}
                <span
                    class="rounded border border-surface-200 dark:border-surface-700 bg-surface-50 dark:bg-surface-800/50 px-1.5 py-0.5 font-mono"
                    class:text-primary-600={col.header.endsWith('_override')}
                    class:dark:text-primary-400={col.header.endsWith('_override')}
                    title={col.header.endsWith('_override') ? 'Override field — value set by staff or attendee edit' : ''}>
                    {col.header}
                </span>
            {/each}
        </div>
        <p class="mt-1 pl-2 text-primary-600 dark:text-primary-400">
            Fields highlighted in primary color are override fields — values set by staff or attendee before printing.
        </p>
    </details>

    <!-- Filter explanation -->
    <p class="text-xs text-surface-500-400">
        {#if filter_mode === 'printed_clean'}
            <strong>Printed + Clean:</strong> Printed badges with non-hidden records and no test badge types. This is the standard export sent to clients.
        {:else if filter_mode === 'printed'}
            <strong>Printed (all):</strong> All badges with at least one print, including hidden records and test types.
        {:else}
            <strong>All badges:</strong> Every badge record in the local cache, including unprinted, hidden, and test records.
        {/if}
        Rows without a value in <code>external_id</code> were added manually (not via CSV import).
    </p>
</div>
