<script lang="ts">
import { browser } from '$app/environment';
import { onMount, untrack } from 'svelte';
import { Modal } from 'flowbite-svelte';
import { liveQuery } from 'dexie';

import { api } from '$lib/api/api';
import { ae_loc, ae_sess, ae_api, slct } 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 { key_val } from '$lib/stores/ae_stores';
import type { ae_DataStore } from '$lib/types/ae_types';

interface Props {
    log_lvl?: number;
    expire_minutes?: number;
    mount_reload_sec?: number;
    ds_code: string;
    ds_name?: null | string;
    ds_type?: string;
    for_type?: null | string;
    for_id?: null | string;
    class_li?: string;
    display?: string;
    try_cache?: boolean;
    hide?: boolean;
    show_edit?: boolean;
    show_edit_btn?: boolean;
    show_view?: boolean;
    ds_loaded?: boolean;
    debug?: boolean;
    ds_loading_status?: string;
    val_sql?: null | any;
}

let {
    log_lvl = 0,
    expire_minutes = 15,
    mount_reload_sec = 0,
    ds_code,
    ds_name = null,
    ds_type = 'text',
    for_type = null,
    for_id = null,
    class_li = '',
    display = undefined as string | undefined,
    try_cache = true,
    hide = false,
    show_edit = $bindable(false),
    show_edit_btn = false,
    show_view = $bindable(true),
    ds_loaded = $bindable(false),
    debug = false,
    ds_loading_status = $bindable('starting'),
    val_sql = $bindable(null)
}: Props = $props();

// Local reactive state
let trigger: null | string = $state(null);
let ds_submit_results: Promise<any> | key_val | undefined = $state();

// Dexie LiveQuery for data store
// This derived observable will automatically update when dependencies change
let lq__ds_obj = $derived(
    liveQuery(async () => {
        const current_code = ds_code;
        const account_id = $ae_loc.account_id;
        const current_for_type = for_type;
        const current_for_id = for_id;

        if (!current_code) return null;

        if (log_lvl)
            console.log(`ae_e_data_store [${current_code}]: LQ Lookup...`, {
                account_id,
                current_for_type,
                current_for_id
            });

        // Hierarchical Local Lookup (Specific -> Account -> Global)
        // Mimics backend SQL priority: WHERE code = :code ORDER BY for_id DESC, account_id DESC
        if (log_lvl)
            console.log(
                `ae_e_data_store [${current_code}]: Fetching all matching codes for priority sorting...`
            );

        const results = await db_core.data_store
            .where('code')
            .equals(current_code)
            .toArray();

        if (!results || results.length === 0) return null;

        // Sort by specificity
        results.sort((a, b) => {
            // 1. Priority: Specific Context match (for_type + for_id)
            const a_context =
                current_for_id &&
                a.for_id === current_for_id &&
                a.for_type === current_for_type
                    ? 1
                    : 0;
            const b_context =
                current_for_id &&
                b.for_id === current_for_id &&
                b.for_type === current_for_type
                    ? 1
                    : 0;
            if (a_context !== b_context) return b_context - a_context;

            // 2. Priority: Account-specific match
            const a_account = account_id && a.account_id === account_id ? 1 : 0;
            const b_account = account_id && b.account_id === account_id ? 1 : 0;
            if (a_account !== b_account) return b_account - a_account;

            // 3. Tie-breaker: Newest updated
            const a_time = new Date(
                a.updated_on || a.created_on || 0
            ).getTime();
            const b_time = new Date(
                b.updated_on || b.created_on || 0
            ).getTime();
            return b_time - a_time;
        });

        if (log_lvl)
            console.log(
                `ae_e_data_store [${current_code}]: Best match found (ID: ${results[0].id}, Account: ${results[0].account_id})`
            );
        return results[0];
    })
);

// Sync status and bound props when the live data changes
$effect(() => {
    const entry = $lq__ds_obj as ae_DataStore | null;

    untrack(() => {
        ds_loaded = !!entry;
        if (entry) {
            ds_loading_status = 'loaded';
            // Handle val_sql binding if type is sql
            if (ds_type === 'sql') {
                val_sql = entry.text || entry.html || null;
            }
        }
    });
});

// Context Change Guard: reset loading status when identity props change.
// WHY: ds_loading_status persists after the initial load cycle ('loaded', 'not found', etc.).
// When for_id/for_type/ds_code change after mount (e.g. for_id resolves from undefined
// to a real event_id), the trigger effect below won't re-fire unless we reset to 'starting'.
// untrack() prevents a circular dep — we write ds_loading_status but don't subscribe to it here.
$effect(() => {
    void for_id;
    void for_type;
    void ds_code;
    untrack(() => {
        ds_loading_status = 'starting';
    });
});

// Initial Trigger & Context Change Guard
$effect(() => {
    const account_id = $slct.account_id;
    const api_ready = !!$ae_api?.base_url;
    const entry = $lq__ds_obj as ae_DataStore | null | undefined;

    // Don't fire until the bootstrap Sync Effect has set a real account_id.
    // Without this guard, the fetch runs with null account_id and the
    // localStorage scavenge in get_object picks up a stale account_id from a
    // previous session, returning the wrong account's record.
    if (!browser || !account_id || !api_ready || ds_loading_status !== 'starting') return;

    // Also reload when IDB has a record but it belongs to a different account
    // (not null/global and not the current account). This handles the case where
    // a previous dev/demo session left account-specific rows in IDB that score
    // as the "best" liveQuery match even though they are for the wrong account.
    const entry_is_stale_account =
        entry !== undefined &&
        entry !== null &&
        entry.account_id !== null &&
        entry.account_id !== account_id;

    if (!entry || entry_is_stale_account) {
        trigger = 'load__ds__code';
    }
});

// Fetch handler
$effect(() => {
    if (trigger === 'load__ds__code') {
        untrack(() => {
            trigger = null;
            load_data_store();
        });
    }
});

// Mount reload logic
onMount(() => {
    if (mount_reload_sec > 0) {
        const random_ms = Math.floor(Math.random() * mount_reload_sec * 1000);
        setTimeout(() => {
            trigger = 'load__ds__code';
        }, random_ms);
    }
});

async function load_data_store() {
    if (ds_loading_status === 'loading') return;
    ds_loading_status = 'loading';
    const api_cfg = untrack(() => $ae_api);

    if (log_lvl) console.log(`ae_e_data_store [${ds_code}]: Fetching...`);

    try {
        // Attempt 1: Context-specific fetch
        let ds_results = await api.get_data_store({
            api_cfg,
            code: ds_code,
            for_type: for_type,
            for_id: for_id,
            log_lvl: log_lvl
        });

        // V3 API structured check
        const is_error = ds_results?.meta?.success === false;
        const status_code =
            ds_results?.meta?.status_code || (ds_results === false ? 500 : 200);

        // Fallback to Global if not found (404), unauthorized (403/401), or explicitly failed
        if (
            !ds_results ||
            is_error ||
            status_code === 404 ||
            status_code === 403 ||
            status_code === 401
        ) {
            if (log_lvl)
                console.log(
                    `ae_e_data_store [${ds_code}]: Not found in context (Status ${status_code}). Trying global fallback.`
                );

            // TEMPORARY: same global-default fallback as core__data_store.ts.
            // This should go away once the backend can answer with JWT-backed,
            // account-scoped defaults only.
            ds_results = await api.get_data_store({
                api_cfg,
                code: ds_code,
                no_account_id: true,
                log_lvl: log_lvl
            });
        }

        const ds_id = ds_results?.data_store_id || ds_results?.id;

        if (ds_results && ds_id) {
            // Map fields correctly for V3 alignment
            const text_val = ds_results.text || '';
            const json_val =
                ds_results.json ||
                (ds_results.json_str ? JSON.parse(ds_results.json_str) : null);

            // Save to Dexie
            const ds_to_save: ae_DataStore = {
                ...ds_results,
                id: ds_id,
                data_store_id: ds_results.data_store_id || ds_id,
                // data_store_id: ds_id,
                account_id: ds_results.account_id || ds_results.account_id,
                // account_id: ds_results.account_id || ds_results.account_id,
                updated_on: ds_results.updated_on || new Date().toISOString(),
                text: text_val,
                html: text_val, // Default map text to html
                json: json_val
            };

            await db_core.data_store.put(ds_to_save);
            if (log_lvl)
                console.log(
                    `ae_e_data_store [${ds_code}]: Saved to Dexie. ID: ${ds_id}`
                );
        } else {
            ds_loading_status = 'not found';
            if (log_lvl)
                console.warn(
                    `ae_e_data_store [${ds_code}]: Result had no valid ID.`
                );
        }
    } catch (err) {
        console.error(`ae_e_data_store [${ds_code}]: Fetch failed.`, err);
        ds_loading_status = 'error';
    }
}

async function handle_submit_form(event: Event) {
    const target = event.target as HTMLFormElement;
    $ae_sess.ds.submit_status = 'processing';

    const form_data = new FormData(target);
    const data_store_di = ae_util.extract_prefixed_form_data({
        prefix: null,
        form_data,
        trim_values: true,
        bool_tf_str: true
    });

    const data_store_do: key_val = {
        code: data_store_di.ds_code ?? ds_code,
        name: data_store_di.ds_name ?? ds_name,
        type: data_store_di.ds_type ?? ds_type,
        for_type: data_store_di.ds_for_type ?? null,
        for_id: data_store_di.ds_for_id ?? null,
        access_read: data_store_di.ds_access_read,
        access_write: data_store_di.ds_access_write,
        access_delete: data_store_di.ds_access_delete,
        enable: data_store_di.ds_enable ?? true,
        account_id: data_store_di.ds_use_account_id
            ? (data_store_di.ds_account_id ?? $slct.account_id)
            : null
    };

    const content_val = data_store_di.ds_value;
    if (data_store_do.type === 'json') {
        data_store_do.json = content_val;
        try {
            // Ensure it's valid JSON if stringified
            if (typeof content_val === 'string') JSON.parse(content_val);
        } catch (e) {
            console.error('Invalid JSON content');
        }
    } else {
        data_store_do.text = content_val;
    }

    const api_cfg = untrack(() => $ae_api);

    if ($lq__ds_obj?.id) {
        ds_submit_results = api
            .update_ae_obj({
                api_cfg,
                obj_type: 'data_store',
                obj_id: $lq__ds_obj.id,
                fields: data_store_do
            })
            .then((res) => {
                if (res) {
                    $ae_sess.ds.submit_status = 'updated';
                    trigger = 'load__ds__code';
                }
                return res;
            });
    } else {
        ds_submit_results = api
            .create_ae_obj({
                api_cfg,
                obj_type: 'data_store',
                fields: data_store_do
            })
            .then((res) => {
                if (res) {
                    $ae_sess.ds.submit_status = 'created';
                    trigger = 'load__ds__code';
                }
                return res;
            });
    }
}

async function handle_delete() {
    if (
        !$lq__ds_obj?.id ||
        !confirm('Are you sure you want to delete this data store?')
    )
        return;

    const api_cfg = untrack(() => $ae_api);
    const res = await api.delete_ae_obj({
        api_cfg,
        obj_type: 'data_store',
        obj_id: $lq__ds_obj.id,
        method: 'delete'
    });

    if (res) {
        await db_core.data_store.delete($lq__ds_obj.id);
        ds_loading_status = 'not found';
        show_edit = false;
    }
}
</script>

<div
    class="ae__elem__data_store relative {class_li}"
    class:hidden={hide}
    style={display ? `display: ${display}` : undefined}>
    {#if $lq__ds_obj}
        {#if debug || $ae_loc.debug === 'debug'}
            Debug is ON!
            <pre
                class="mb-2 overflow-x-auto rounded bg-black/10 p-2 text-[10px]">
    ID: {$lq__ds_obj.id}
    Code: {$lq__ds_obj.code}
    Name: {$lq__ds_obj.name}
    Type: {$lq__ds_obj.type}
    Account: {$lq__ds_obj.account_id || 'Global / NULL'}
    Created: {$lq__ds_obj.created_on}
    Updated: {$lq__ds_obj.updated_on}
            </pre>

            <hr />
        {/if}

        <Modal
            title="{$lq__ds_obj.name || 'Unnamed'} - {$lq__ds_obj.code}"
            bind:open={show_edit}
            autoclose={false}
            size="xl"
            class="w-full max-w-6xl">
            <form
                class="flex flex-col gap-4"
                onsubmit={(e) => {
                    e.preventDefault();
                    handle_submit_form(e);
                }}>
                <input
                    type="hidden"
                    name="ds_id_random"
                    value={$lq__ds_obj.id} />

                <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
                    <div class="space-y-2">
                        <label class="label">
                            <span class="text-xs font-bold opacity-70"
                                >Code</span>
                            <input
                                type="text"
                                name="ds_code"
                                class="input font-mono"
                                value={$lq__ds_obj.code}
                                readonly={!$ae_loc.manager_access}
                                required />
                        </label>
                        <label class="label">
                            <span class="text-xs font-bold opacity-70"
                                >Name</span>
                            <input
                                type="text"
                                name="ds_name"
                                class="input"
                                value={$lq__ds_obj.name}
                                required />
                        </label>
                    </div>

                    <div class="space-y-2">
                        <label class="label">
                            <span class="text-xs font-bold opacity-70"
                                >Type</span>
                            <select
                                name="ds_type"
                                class="select"
                                value={$lq__ds_obj.type}>
                                <option value="text">Text</option>
                                <option value="html">HTML</option>
                                <option value="json">JSON</option>
                                <option value="md">Markdown</option>
                                <option value="sql">SQL</option>
                            </select>
                        </label>
                        <div class="flex items-center gap-2 pt-6">
                            <input
                                type="checkbox"
                                name="ds_use_account_id"
                                class="checkbox"
                                checked={!!$lq__ds_obj.account_id} />
                            <span class="text-xs">Account Specific</span>
                        </div>
                    </div>
                </div>

                <div class="space-y-2">
                    <span class="text-xs font-bold opacity-70">Content</span>
                    <textarea
                        name="ds_value"
                        class="textarea font-mono text-sm"
                        rows="15"
                        placeholder="Enter content here..."
                        >{$lq__ds_obj.type === 'json'
                            ? typeof $lq__ds_obj.json === 'string'
                                ? $lq__ds_obj.json
                                : JSON.stringify($lq__ds_obj.json, null, 2)
                            : $lq__ds_obj.text ||
                              $lq__ds_obj.html ||
                              ''}</textarea>
                </div>

                <div class="text-surface-500 text-xs">
                    Created on: {$lq__ds_obj.created_on} | Last Updated: {$lq__ds_obj.updated_on}
                </div>

                <div class="flex items-center justify-between pt-4">
                    <button
                        type="button"
                        class="btn variant-filled-error"
                        onclick={handle_delete}>
                        <span class="fas fa-trash mr-2"></span> Delete
                    </button>

                    <div class="flex gap-2">
                        <button
                            type="button"
                            class="btn variant-soft"
                            onclick={() => (show_edit = false)}>Cancel</button>
                        <button
                            type="submit"
                            class="btn variant-filled-primary">
                            <span class="fas fa-save mr-2"></span> Save
                        </button>
                    </div>
                </div>
            </form>
        </Modal>

        {#if show_view}
            {#if $lq__ds_obj.type === 'html' && $lq__ds_obj.html}
                {@html $lq__ds_obj.html}
            {:else if $lq__ds_obj.type === 'text' && $lq__ds_obj.text}
                <div class="whitespace-pre-wrap">{$lq__ds_obj.text}</div>
            {:else if $lq__ds_obj.type === 'sql' && $lq__ds_obj.text}
                {#if debug}<div class="font-mono text-xs opacity-50">
                        SQL: {$lq__ds_obj.text}
                    </div>{/if}
            {/if}
        {/if}

        {#if $ae_loc.edit_mode && ($ae_loc.manager_access || (show_edit_btn && $ae_loc.administrator_access))}
            <button
                type="button"
                class="btn btn-sm variant-soft-warning absolute top-0 right-0 z-10 opacity-20 hover:opacity-100"
                ondblclick={() => {
                    show_edit = true;
                    show_view = false;
                }}
                title="Edit Data Store: {ds_code}">
                <span class="fas fa-edit"></span>
            </button>
        {/if}
    {:else if ds_loading_status === 'not found'}
        <!-- Only show diagnostic to administrator+ (no edit_mode needed) or trusted staff in edit mode.
             Anonymous/user/public visitors must never see internal data store codes or gaps. -->
        {#if $ae_loc.administrator_access || ($ae_loc.trusted_access && $ae_loc.edit_mode)}
            <div
                class="border-surface-500/30 rounded border border-dashed p-2 text-xs opacity-50">
                Data Store not found: {ds_code}
            </div>
        {/if}
    {/if}

    {#if ds_loading_status === 'loading'}
        <div class="absolute bottom-0 left-0 p-1 opacity-50">
            <span class="fas fa-spinner fa-spin text-xs"></span>
        </div>
    {/if}
</div>
