<script lang="ts">
import { onMount, untrack } from 'svelte';
import { SvelteMap } from 'svelte/reactivity';
import { goto } from '$app/navigation';
import {
    ArrowRight,
    ArrowUpDown,
    Check,
    ChevronDown,
    ChevronLeft,
    ChevronRight,
    ChevronUp,
    Database,
    Funnel,
    LoaderCircle,
    Pencil,
    Plus,
    SquarePen,
    RefreshCw,
    Search,
    X
} from '@lucide/svelte';

import { api } from '$lib/ae_api/api';
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
import { db_core } from '$lib/ae_core/db_core';
import { ae_util } from '$lib/ae_utils/ae_utils';
import type { ae_DataStore } from '$lib/types/ae_types';
import AE_DataStore_Form from '$lib/elements/element_data_store_form.svelte';
import AE_Field_Editor from '$lib/elements/element_ae_obj_field_editor.svelte';

let account_map = new SvelteMap<string, string>();

onMount(async () => {
    if (!$ae_loc.manager_access) {
        goto('/core');
        return;
    }
    const accts = await db_core.account.toArray();
    account_map.clear();
    for (const a of accts) {
        if (a.id) account_map.set(a.id, a.code ?? a.short_name ?? a.name ?? a.id.slice(0, 8) + '…');
    }
});

// ── Filter state ──────────────────────────────────────────────────────────────
let qry_code = $state('');
let qry_account_id = $state('');
let qry_for_type = $state('');
let qry_for_id = $state('');
let qry_enabled = $state<'all' | 'enabled' | 'not_enabled'>('all');
let page_limit = $state(50);
let page_offset = $state(0);
let sort_col = $state<string>('updated_on');
let sort_dir = $state<'ASC' | 'DESC'>('DESC');

// ── Results ───────────────────────────────────────────────────────────────────
let results: ae_DataStore[] = $state([]);
let loading = $state(false);
let searched = $state(false);

// ── Edit modal ────────────────────────────────────────────────────────────────
let show_edit = $state(false);
let editing_obj: ae_DataStore | null = $state(null);

// ── Bulk rename state ─────────────────────────────────────────────────────────
let show_bulk_rename = $state(false);
let rename_filter = $state('');       // code LIKE filter — use % as wildcard
let rename_find_text = $state('');    // literal substring to find within matching codes
let rename_replace_text = $state(''); // literal replacement for find_text
let rename_preview: { id: string; old_code: string; new_code: string }[] = $state([]);
let rename_loading = $state(false);
let rename_apply_status = $state<'idle' | 'applying' | 'done' | 'error'>('idle');
let rename_applied = $state(0);

// Auto-load on first render once the API config is available
$effect(() => {
    if (!$ae_api?.base_url || searched || loading) return;
    untrack(() => do_search());
});

// ── Search ────────────────────────────────────────────────────────────────────
async function do_search(reset = true) {
    if (reset) page_offset = 0;
    loading = true;

    const search_query: any = { and: [] };

    if (qry_code.trim()) {
        search_query.and.push({ field: 'code', op: 'like', value: qry_code.trim() });
    }
    if (qry_account_id.trim() === 'global') {
        search_query.and.push({ field: 'account_id', op: 'is', value: null });
    } else if (qry_account_id.trim()) {
        search_query.and.push({ field: 'account_id_random', op: 'eq', value: qry_account_id.trim() });
    }
    if (qry_for_type.trim()) {
        search_query.and.push({ field: 'for_type', op: 'like', value: qry_for_type.trim() });
    }
    if (qry_for_id.trim()) {
        search_query.and.push({ field: 'for_id_random', op: 'eq', value: qry_for_id.trim() });
    }
    const result_li = await api.search_ae_obj({
        api_cfg: $ae_api,
        obj_type: 'data_store',
        search_query,
        enabled: qry_enabled,
        hidden: 'all',
        order_by_li: { [sort_col]: sort_dir },
        limit: page_limit,
        offset: page_offset,
        log_lvl: 0
    });

    results = result_li ?? [];
    searched = true;
    loading = false;
}

function clear_filters() {
    qry_code = '';
    qry_account_id = '';
    qry_for_type = '';
    qry_for_id = '';
    qry_enabled = 'all';
    results = [];
    searched = false;
}

function toggle_sort(col: string) {
    if (sort_col === col) {
        sort_dir = sort_dir === 'ASC' ? 'DESC' : 'ASC';
    } else {
        sort_col = col;
        sort_dir = 'ASC';
    }
    do_search(false);
}

// ── Open edit / new ───────────────────────────────────────────────────────────
function open_edit(obj: ae_DataStore) {
    editing_obj = obj;
    show_edit = true;
}

function open_new() {
    editing_obj = null;
    show_edit = true;
}

// ── Bulk rename ───────────────────────────────────────────────────────────────
async function do_rename_preview() {
    if (!rename_filter.trim()) return;
    rename_loading = true;
    rename_preview = [];
    rename_apply_status = 'idle';
    rename_applied = 0;

    const result_li = await api.search_ae_obj({
        api_cfg: $ae_api,
        obj_type: 'data_store',
        search_query: { and: [{ field: 'code', op: 'like', value: rename_filter.trim() }] },
        order_by_li: [{ code: 'ASC' }],
        limit: 200,
        offset: 0,
        log_lvl: 0
    });

    if (result_li) {
        const find = rename_find_text.trim();
        const repl = rename_replace_text.trim();
        rename_preview = result_li
            .map((ds: ae_DataStore) => {
                const old_code = ds.code ?? '';
                const new_code = find ? old_code.split(find).join(repl) : old_code;
                return { id: (ds.id ?? (ds as any).data_store_id) as string, old_code, new_code };
            })
            .filter((r: { id: string; old_code: string; new_code: string }) => r.old_code !== r.new_code);
    }

    rename_loading = false;
}

async function do_rename_apply() {
    if (!rename_preview.length) return;
    if (!confirm(`Rename ${rename_preview.length} data store code(s)?\n\nThis cannot be undone.`)) return;

    rename_apply_status = 'applying';
    rename_applied = 0;
    const api_cfg = untrack(() => $ae_api);

    for (const item of rename_preview) {
        const result = await api.update_ae_obj({
            api_cfg,
            obj_type: 'data_store',
            obj_id: item.id,
            fields: { code: item.new_code }
        });
        if (result) {
            rename_applied++;
            await db_core.data_store.update(item.id, { code: item.new_code });
        }
    }

    rename_apply_status = rename_applied === rename_preview.length ? 'done' : 'error';
    if (rename_apply_status === 'done') {
        rename_preview = [];
        do_search(false);
    }
}

// ── Helpers ───────────────────────────────────────────────────────────────────
const ds_type_options = { text: 'Text', html: 'HTML', json: 'JSON', sql: 'SQL', md: 'Markdown' };

function type_badge(type: string | null | undefined) {
    switch (type) {
        case 'json': return 'preset-tonal-warning';
        case 'html': return 'preset-tonal-primary';
        case 'sql': return 'preset-tonal-error';
        case 'md':  return 'preset-tonal-success';
        default:    return 'preset-tonal-surface';
    }
}

function fmt_account(account_id: string | null | undefined): string {
    if (!account_id) return 'global';
    return account_map.get(account_id) ?? account_id.slice(0, 8) + '…';
}

function fmt_date(val: string | Date | null | undefined) {
    if (!val) return '—';
    return ae_util.iso_datetime_formatter(val as any, 'datetime_12_short');
}

function content_preview(ds: ae_DataStore): string {
    if (ds.type === 'json') return ds.json ? JSON.stringify(ds.json).slice(0, 60) : '';
    return (ds.text ?? ds.html ?? '').slice(0, 60).replace(/\s+/g, ' ');
}
</script>

<div class="space-y-6">

    <!-- ── Header ──────────────────────────────────────────────────────────── -->
    <header class="bg-surface-100-900 border-surface-500/10 flex flex-wrap items-center justify-between gap-4 rounded-xl border p-4 shadow-lg">
        <div class="flex items-center gap-3">
            <div class="bg-primary-500/10 rounded-lg p-2">
                <Database size={24} class="text-primary-500" />
            </div>
            <div>
                <h1 class="h2 font-black tracking-tight">Data Stores</h1>
                <p class="text-xs font-bold tracking-widest uppercase opacity-50">Content &amp; Configuration Storage</p>
            </div>
        </div>
        <div class="flex gap-2">
            <button
                type="button"
                onclick={() => { show_bulk_rename = !show_bulk_rename; }}
                class="btn preset-tonal-surface font-bold"
                title="Bulk rename data store codes">
                <Pencil size={14} class="mr-1" /> Bulk Rename
            </button>
            <button
                type="button"
                onclick={open_new}
                class="btn preset-filled-primary font-bold shadow-lg"
                class:hidden={!$ae_loc.manager_access}>
                <Plus size={16} class="mr-2" /> New Data Store
            </button>
        </div>
    </header>

    <!-- ── Filter bar ──────────────────────────────────────────────────────── -->
    <div class="card preset-tonal-surface border-surface-500/10 space-y-3 border p-4 shadow-xl">
        <div class="flex items-center gap-2 text-[10px] font-bold tracking-widest uppercase opacity-50">
            <Funnel size={11} /> Filters
        </div>

        <div class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4">
            <label class="space-y-1">
                <span class="text-xs font-bold opacity-70">
                    Code <span class="font-normal opacity-60">— use <code>%</code> as wildcard</span>
                </span>
                <input
                    type="search"
                    bind:value={qry_code}
                    placeholder="event__pres_mgmt__%"
                    class="input input-sm w-full font-mono text-xs"
                    onkeydown={(e) => e.key === 'Enter' && do_search()} />
            </label>
            <div class="space-y-1">
                <label class="space-y-1">
                    <span class="text-xs font-bold opacity-70">
                        Account ID <span class="font-normal opacity-60">— <code>global</code> for null</span>
                    </span>
                    <input
                        type="search"
                        bind:value={qry_account_id}
                        placeholder="random ID or 'global'"
                        class="input input-sm w-full font-mono text-xs"
                        onkeydown={(e) => e.key === 'Enter' && do_search()} />
                </label>
                <p class="text-[10px] opacity-40">Results are scoped to the active account by the API.</p>
            </div>
            <label class="space-y-1">
                <span class="text-xs font-bold opacity-70">For Type</span>
                <input
                    type="search"
                    bind:value={qry_for_type}
                    placeholder="event, event_session, person…"
                    class="input input-sm w-full font-mono text-xs"
                    onkeydown={(e) => e.key === 'Enter' && do_search()} />
            </label>
            <label class="space-y-1">
                <span class="text-xs font-bold opacity-70">For ID</span>
                <input
                    type="search"
                    bind:value={qry_for_id}
                    placeholder="random string ID"
                    class="input input-sm w-full font-mono text-xs"
                    onkeydown={(e) => e.key === 'Enter' && do_search()} />
            </label>
        </div>

        <div class="flex flex-wrap items-center gap-3 pt-1">
            <label class="space-y-1">
                <span class="text-xs font-bold opacity-70">Status</span>
                <select class="select select-sm text-xs" bind:value={qry_enabled}>
                    <option value="all">All</option>
                    <option value="enabled">Enabled</option>
                    <option value="not_enabled">Disabled</option>
                </select>
            </label>
            <label class="space-y-1">
                <span class="text-xs font-bold opacity-70">Per page</span>
                <select class="select select-sm text-xs" bind:value={page_limit}>
                    <option value={25}>25</option>
                    <option value={50}>50</option>
                    <option value={100}>100</option>
                    <option value={200}>200</option>
                </select>
            </label>
            <div class="flex flex-1 items-end justify-end gap-2">
                <button
                    type="button"
                    onclick={clear_filters}
                    class="btn btn-sm preset-tonal-surface text-xs"
                    title="Clear all filters">
                    <X size={13} class="mr-1" /> Clear
                </button>
                <button
                    type="button"
                    onclick={() => do_search()}
                    class="btn preset-filled-primary"
                    disabled={loading}>
                    {#if loading}
                        <LoaderCircle size={16} class="mr-2 animate-spin" />
                    {:else}
                        <Search size={16} class="mr-2" />
                    {/if}
                    Search
                </button>
            </div>
        </div>
    </div>

    <!-- ── Bulk Rename Panel ─────────────────────────────────────────────────── -->
    {#if show_bulk_rename}
        <div class="card border-surface-500/10 space-y-4 border p-4 shadow-xl preset-tonal-surface">
            <div class="flex items-center gap-2 text-[10px] font-bold tracking-widest uppercase opacity-50">
                <Pencil size={11} /> Bulk Rename Codes
            </div>

            <div class="grid grid-cols-1 gap-3 md:grid-cols-3">
                <label class="space-y-1">
                    <span class="text-xs font-bold opacity-70">
                        Code filter <span class="font-normal opacity-60">— use <code>%</code> as wildcard</span>
                    </span>
                    <input
                        type="text"
                        bind:value={rename_filter}
                        placeholder="event__old_code__%"
                        class="input input-sm w-full font-mono text-xs"
                        onkeydown={(e) => e.key === 'Enter' && do_rename_preview()} />
                </label>
                <label class="space-y-1">
                    <span class="text-xs font-bold opacity-70">Find text in code</span>
                    <input
                        type="text"
                        bind:value={rename_find_text}
                        placeholder="old_code"
                        class="input input-sm w-full font-mono text-xs" />
                </label>
                <label class="space-y-1">
                    <span class="text-xs font-bold opacity-70">Replace with</span>
                    <input
                        type="text"
                        bind:value={rename_replace_text}
                        placeholder="new_code"
                        class="input input-sm w-full font-mono text-xs" />
                </label>
            </div>

            <div class="flex items-center gap-2">
                <button
                    type="button"
                    onclick={do_rename_preview}
                    class="btn btn-sm preset-tonal-primary"
                    disabled={rename_loading || !rename_filter.trim()}>
                    {#if rename_loading}
                        <LoaderCircle size={13} class="mr-1 animate-spin" />
                    {:else}
                        <Search size={13} class="mr-1" />
                    {/if}
                    Preview Changes
                </button>
                {#if rename_preview.length > 0}
                    <button
                        type="button"
                        onclick={do_rename_apply}
                        class="btn btn-sm preset-filled-warning font-bold"
                        disabled={rename_apply_status === 'applying'}>
                        {#if rename_apply_status === 'applying'}
                            <LoaderCircle size={13} class="mr-1 animate-spin" /> Applying {rename_applied}/{rename_preview.length}…
                        {:else if rename_apply_status === 'done'}
                            <Check size={13} class="mr-1" /> Done
                        {:else}
                            <Pencil size={13} class="mr-1" /> Apply to {rename_preview.length} record{rename_preview.length !== 1 ? 's' : ''}
                        {/if}
                    </button>
                {/if}
                {#if rename_apply_status === 'error'}
                    <span class="text-xs text-error-500">Applied {rename_applied}/{rename_preview.length} — check console for errors.</span>
                {/if}
            </div>

            {#if rename_preview.length > 0}
                <div class="overflow-x-auto rounded-lg border border-surface-500/20">
                    <table class="w-full text-xs">
                        <thead>
                            <tr class="border-b border-surface-500/20 text-left text-[10px] font-bold uppercase tracking-wider opacity-50">
                                <th class="px-3 py-2">Current Code</th>
                                <th class="px-3 py-2"></th>
                                <th class="px-3 py-2">New Code</th>
                            </tr>
                        </thead>
                        <tbody>
                            {#each rename_preview as item (item.id)}
                                <tr class="border-b border-surface-500/10">
                                    <td class="px-3 py-1.5 font-mono opacity-60">{item.old_code}</td>
                                    <td class="px-2 py-1.5 opacity-40"><ArrowRight size={12} /></td>
                                    <td class="px-3 py-1.5 font-mono font-bold text-success-600 dark:text-success-400">{item.new_code}</td>
                                </tr>
                            {/each}
                        </tbody>
                    </table>
                </div>
            {:else if !rename_loading && rename_filter.trim()}
                <p class="text-xs opacity-40">No changes to preview — either no records matched or find/replace text produced no differences.</p>
            {/if}
        </div>
    {/if}

    <!-- ── Results ─────────────────────────────────────────────────────────── -->
    {#if results.length > 0}
        <div class="card preset-tonal-surface border-surface-500/10 border shadow-xl">

            <!-- table header row -->
            <div class="border-surface-500/20 flex items-center justify-between border-b px-4 py-2">
                <span class="text-xs font-bold opacity-60">
                    {results.length} record{results.length !== 1 ? 's' : ''}
                    {#if results.length === page_limit}
                        <span class="opacity-50"> (may be more — increase per-page or paginate)</span>
                    {/if}
                </span>
                <div class="flex items-center gap-2">
                    {#if page_offset > 0}
                        <button type="button" class="btn btn-sm preset-tonal-surface"
                            onclick={() => { page_offset = Math.max(0, page_offset - page_limit); do_search(false); }}>
                            <ChevronLeft size={14} />
                        </button>
                    {/if}
                    {#if results.length === page_limit}
                        <button type="button" class="btn btn-sm preset-tonal-surface"
                            onclick={() => { page_offset += page_limit; do_search(false); }}>
                            <ChevronRight size={14} />
                        </button>
                    {/if}
                    <button type="button" onclick={() => do_search(false)} class="btn btn-sm preset-tonal-surface" title="Refresh">
                        <RefreshCw size={13} />
                    </button>
                </div>
            </div>

            <div class="overflow-x-auto">
                <table class="w-full text-xs">
                    <thead>
                        <tr class="border-surface-500/20 border-b text-left text-[10px] font-bold uppercase tracking-wider opacity-50">
                            <th class="px-3 py-2">
                                <button type="button" class="flex items-center gap-1 hover:opacity-100" onclick={() => toggle_sort('code')}>
                                    Code
                                    {#if sort_col === 'code'}
                                        {#if sort_dir === 'ASC'}<ChevronUp size={10} />{:else}<ChevronDown size={10} />{/if}
                                    {:else}
                                        <ArrowUpDown size={10} class="opacity-30" />
                                    {/if}
                                </button>
                            </th>
                            <th class="px-3 py-2">
                                <button type="button" class="flex items-center gap-1 hover:opacity-100" onclick={() => toggle_sort('name')}>
                                    Name
                                    {#if sort_col === 'name'}
                                        {#if sort_dir === 'ASC'}<ChevronUp size={10} />{:else}<ChevronDown size={10} />{/if}
                                    {:else}
                                        <ArrowUpDown size={10} class="opacity-30" />
                                    {/if}
                                </button>
                            </th>
                            <th class="px-3 py-2">Type</th>
                            <th class="px-3 py-2">Account</th>
                            <th class="px-3 py-2">For</th>
                            <th class="hidden px-3 py-2">Preview</th>
                            <th class="px-3 py-2 hidden lg:table-cell">
                                <button type="button" class="flex items-center gap-1 hover:opacity-100" onclick={() => toggle_sort('updated_on')}>
                                    Updated
                                    {#if sort_col === 'updated_on'}
                                        {#if sort_dir === 'ASC'}<ChevronUp size={10} />{:else}<ChevronDown size={10} />{/if}
                                    {:else}
                                        <ArrowUpDown size={10} class="opacity-30" />
                                    {/if}
                                </button>
                            </th>
                            <th class="px-3 py-2"></th>
                        </tr>
                    </thead>
                    <tbody>
                        {#each results as ds (ds.id ?? ds.data_store_id)}
                            {@const ds_id = ds.id ?? ds.data_store_id}
                            <tr class="border-surface-500/10 hover:bg-surface-500/5 border-b transition-colors duration-200">
                                <td class="px-3 py-2">
                                    <AE_Field_Editor
                                        object_type="data_store"
                                        object_id={ds_id}
                                        field_name="code"
                                        edit_label="Code"
                                        current_value={ds.code ?? ''}
                                        placeholder="store_code"
                                        on_success={() => do_search(false)}>
                                        <span class="font-mono" class:opacity-40={!ds.enable}>{ds.code}</span>
                                        {#if !ds.enable}<span class="badge preset-tonal-error ml-1 text-[9px]">off</span>{/if}
                                        {#if ds.hide}<span class="badge preset-tonal-surface ml-1 text-[9px]">hidden</span>{/if}
                                    </AE_Field_Editor>
                                </td>
                                <td class="px-3 py-2">
                                    <AE_Field_Editor
                                        object_type="data_store"
                                        object_id={ds_id}
                                        field_name="name"
                                        edit_label="Name"
                                        current_value={ds.name ?? ''}
                                        placeholder="Display name"
                                        on_success={() => do_search(false)} />
                                </td>
                                <td class="px-3 py-2">
                                    <AE_Field_Editor
                                        object_type="data_store"
                                        object_id={ds_id}
                                        field_name="type"
                                        edit_label="Type"
                                        current_value={ds.type ?? 'text'}
                                        field_type="select"
                                        select_options={ds_type_options}
                                        on_success={() => do_search(false)}>
                                        <span class="badge {type_badge(ds.type)} font-mono text-[9px] uppercase">{ds.type ?? '?'}</span>
                                    </AE_Field_Editor>
                                </td>
                                <td class="px-3 py-2 font-mono opacity-60" title={ds.account_id ?? 'global'}>
                                    {#if ds.account_id}
                                        {fmt_account(ds.account_id)}
                                    {:else}
                                        <span class="italic opacity-50">global</span>
                                    {/if}
                                </td>
                                <td class="px-3 py-2 font-mono opacity-60">
                                    {#if ds.for_type}
                                        {ds.for_type}
                                        {#if ds.for_id}
                                            <span class="opacity-50"> / {ds.for_id.slice(0, 6)}…</span>
                                        {/if}
                                    {:else}
                                        —
                                    {/if}
                                </td>
                                <td class="hidden px-3 py-2 opacity-40">
                                    {content_preview(ds)}
                                </td>
                                <td class="hidden px-3 py-2 opacity-50 lg:table-cell whitespace-nowrap">
                                    {fmt_date(ds.updated_on)}
                                </td>
                                <td class="px-3 py-2">
                                    <button
                                        type="button"
                                        class="btn btn-sm preset-tonal-primary text-xs"
                                        onclick={() => open_edit(ds)}
                                        title="Edit full record">
                                        <SquarePen size={12} class="mr-1" /> Edit
                                    </button>
                                </td>
                            </tr>
                        {/each}
                    </tbody>
                </table>
            </div>
        </div>

    {:else if !loading}
        <div class="py-12 text-center opacity-50">
            <Database size={48} class="mx-auto mb-4 opacity-30" />
            <p class="text-lg font-bold">No data stores found</p>
            <p class="text-sm">Try different filters, or create one above.</p>
        </div>
    {/if}

</div>

<!-- ── Edit / Create Modal ───────────────────────────────────────────────────── -->
<AE_DataStore_Form
    bind:open={show_edit}
    editing_obj={editing_obj}
    default_account_id={$ae_loc.account_id ?? ''}
    on_saved={() => do_search(false)}
    on_deleted={() => do_search(false)} />
