/**
 * Reactive state for database table pagination and data fetching.
 *
 * Holds one `AsyncSlot` — `list` (the paginated row fetch). Per-row delete
 * uses plain try/catch + scalar `deleting` / `delete_error` fields (no slot
 * — `delete_error` must persist past `list.run()` retries so the failure
 * message stays visible while the user refetches).
 *
 * @example
 * ```ts
 * const table = new TableState();
 * await table.fetch('accounts', 0, 50);
 *
 * // pagination
 * if (table.has_next) table.go_next();
 * await table.fetch(table.table_name, table.offset, table.limit);
 *
 * // deletion
 * const deleted = await table.delete_row(table.rows[0]);
 * ```
 *
 * @example
 * ```svelte
 * <script lang="ts">
 * 	import {TableState} from '@fuzdev/fuz_app/ui/table_state.svelte.js';
 *
 * 	const table = new TableState();
 * 	table.fetch('accounts');
 * </script>
 *
 * {#if table.list.loading}
 * 	<p>loading…</p>
 * {:else if table.list.error}
 * 	<p>{table.list.error}</p>
 * {:else}
 * 	<p>showing {table.showing_start}–{table.showing_end} of {table.total}</p>
 * {/if}
 * ```
 *
 * @module
 */
import { AsyncSlot } from './async_slot.svelte.js';
import { parse_response_error, ui_fetch } from './ui_fetch.js';
import { format_value } from './ui_format.js';
/** Maximum number of rows that can be fetched in a single page. */
export const TABLE_LIMIT_MAX = 1000;
export class TableState {
    list = new AsyncSlot();
    table_name = $state.raw('');
    columns = $state.raw([]);
    rows = $state.raw([]);
    total = $state.raw(0);
    offset = $state.raw(0);
    limit = $state.raw(100);
    primary_key = $state.raw(null);
    deleting = $state.raw(null);
    delete_error = $state.raw(null);
    // Pagination computed values
    showing_start = $derived(this.total === 0 ? 0 : this.offset + 1);
    showing_end = $derived(Math.min(this.offset + this.rows.length, this.total));
    has_prev = $derived(this.offset > 0);
    has_next = $derived(this.offset + this.limit < this.total);
    /**
     * Fetch a page of rows for `table_name` from `GET /api/db/tables/{table_name}`.
     * `limit` is clamped to `[1, TABLE_LIMIT_MAX]`.
     *
     * @mutates `this`
     */
    async fetch(table_name, offset = 0, limit = 100) {
        this.table_name = table_name;
        this.offset = offset;
        this.limit = Math.max(1, Math.min(TABLE_LIMIT_MAX, limit));
        await this.list.run(async () => {
            const response = await ui_fetch(`/api/db/tables/${table_name}?offset=${this.offset}&limit=${this.limit}`);
            if (!response.ok) {
                throw new Error(await parse_response_error(response, 'Failed to fetch table'));
            }
            const data = await response.json();
            this.columns = data.columns ?? [];
            this.rows = data.rows ?? [];
            this.total = data.total ?? 0;
            this.primary_key = data.primary_key ?? null;
        });
    }
    go_prev() {
        this.offset = Math.max(0, this.offset - this.limit);
    }
    go_next() {
        this.offset += this.limit;
    }
    /**
     * Delete a row by its primary key via `DELETE /api/db/tables/{table_name}/rows/{pk}`.
     * Optimistically drops it from `rows` and decrements `total` on success;
     * surfaces server errors on `delete_error`.
     *
     * @returns `true` when the row was removed; `false` on missing primary key or server error
     * @mutates `this`
     */
    async delete_row(row) {
        if (!this.primary_key)
            return false;
        const pk_value = row[this.primary_key];
        if (pk_value === null || pk_value === undefined)
            return false;
        const pk_str = format_value(pk_value);
        this.deleting = pk_str;
        this.delete_error = null;
        try {
            const response = await ui_fetch(`/api/db/tables/${this.table_name}/rows/${encodeURIComponent(pk_str)}`, { method: 'DELETE' });
            if (!response.ok) {
                try {
                    const data = await response.json();
                    const error_msg = data.error || 'unknown error';
                    this.delete_error = data.detail ? `${error_msg}: ${data.detail}` : error_msg;
                }
                catch {
                    this.delete_error = `Error: ${response.status}`;
                }
                return false;
            }
            // Remove from UI
            this.rows = this.rows.filter((r) => r[this.primary_key] !== pk_value);
            this.total -= 1;
            return true;
        }
        catch (e) {
            this.delete_error = e instanceof Error ? e.message : 'Delete failed';
            return false;
        }
        finally {
            this.deleting = null;
        }
    }
}
