<script lang="ts">
  import {
    ErrorBoundary,
    FormerResolveSpecAPI,
    GridlerFull,
    TextAreaCtrl,
    TextInputCtrl,
    type GridlerColumn,
    type GridlerContextMenuItem,
  } from "@warkypublic/svelix";
  import { adminGridTheme } from "../../gridTheme";
  import { GlobalStateStore } from "../../shellState";
  import type { StoredFile } from "../../types";
  import FormerShell from "../shared/FormerShell.svelte";

  type FileForm = {
    id?: string;
    name: string;
    media_type: string;
    kind: string;
    project_id?: string;
    thought_id?: string;
    encoding?: string;
    content?: string;
  };

  const FILE_PRIMARY_KEY = 'id';

  let selectedFile = $state<StoredFile | null>(null);
  let gridTotal = $state<number | null>(null);
  let formOpened = $state(false);
  let formRequest = $state<'insert' | 'update' | 'delete'>('update');
  let formValues = $state<FileForm>({ name: '', media_type: '', kind: 'file', encoding: 'base64', content: '' });
  let contextRow = $state<Record<string, unknown> | null>(null);
  let refreshKey = $state(0);
  const authToken = GlobalStateStore.getState().session.authToken ?? '';
  const filesOnAPICall = $derived(FormerResolveSpecAPI({
    authToken,
    url: '/api/rs/public/stored_files'
  }));

  const columns: GridlerColumn[] = [
    { id: 'name', title: 'Name', dataKey: 'name', width: 280 },
    { id: 'media_type', title: 'Type', dataKey: 'media_type', width: 220 },
    { id: 'kind', title: 'Kind', dataKey: 'kind', width: 120 },
    { id: 'size_bytes', title: 'Size', dataKey: 'size_bytes', width: 120, format: 'number' },
    { id: 'thought_id', title: 'Thought', dataKey: 'thought_id', width: 220 },
    { id: 'project_id', title: 'Project', dataKey: 'project_id', width: 220 },
    { id: 'created_at', title: 'Uploaded', dataKey: 'created_at', width: 180, format: 'datetime' }
  ];

  const menuItems: GridlerContextMenuItem[] = [
    { id: 'edit', label: 'Edit' },
    { id: 'delete', label: 'Delete' }
  ];

  const filesDataSourceOptions = {
    url: '/api/rs',
    authToken: GlobalStateStore.getState().session.authToken,
    schema: 'public',
    entity: 'stored_files',
    uniqueID: FILE_PRIMARY_KEY,
    hotfields: [FILE_PRIMARY_KEY, 'guid', 'project_id', 'thought_id'],
    columns: ['id', 'guid', 'name', 'media_type', 'kind', 'size_bytes', 'created_at', 'updated_at', 'project_id', 'thought_id'],
    sort: [{ column: 'created_at', direction: 'desc' }]
  } as unknown as {
    url: string;
    authToken?: string;
    schema: string;
    entity: string;
    uniqueID: string;
    hotfields: string[];
    columns: string[];
  };

  function normalizeFile(rowData: Record<string, unknown>): StoredFile {
    return {
      id: String(rowData.id ?? ''),
      name: String(rowData.name ?? ''),
      media_type: String(rowData.media_type ?? ''),
      kind: String(rowData.kind ?? ''),
      size_bytes: Number(rowData.size_bytes ?? 0),
      thought_id: typeof rowData.thought_id === 'string' ? rowData.thought_id : undefined,
      project_id: typeof rowData.project_id === 'string' ? rowData.project_id : undefined,
      sha256: String(rowData.sha256 ?? ''),
      created_at: String(rowData.created_at ?? ''),
      updated_at: String(rowData.updated_at ?? '')
    };
  }

  function normalizeFileRecordForFormer(data: Record<string, unknown>): FileForm {
    return {
      id: typeof data.id === 'string' || typeof data.id === 'number' ? String(data.id) : undefined,
      name: typeof data.name === 'string' ? data.name : '',
      media_type: typeof data.media_type === 'string' ? data.media_type : '',
      kind: typeof data.kind === 'string' ? data.kind : 'file',
      project_id: typeof data.project_id === 'string' && data.project_id ? data.project_id : undefined,
      thought_id: typeof data.thought_id === 'string' && data.thought_id ? data.thought_id : undefined,
      encoding: typeof data.encoding === 'string' ? data.encoding : 'base64',
      content: typeof data.content === 'string' ? data.content : ''
    };
  }

  async function loadFileFromRow(rowData: Record<string, unknown>): Promise<FileForm> {
    const id = String(rowData[FILE_PRIMARY_KEY] ?? '');
    const data = await filesOnAPICall('read', 'update', undefined, id) as Record<string, unknown>;
    return normalizeFileRecordForFormer(data);
  }

  function onRowClick(_row: number, rowData: Record<string, unknown> | undefined) {
    selectedFile = rowData ? normalizeFile(rowData) : null;
  }

  function onRowContextMenu(_row: number, rowData: Record<string, unknown> | undefined) {
    contextRow = rowData ?? null;
  }

  async function onMenuItemSelect(item: GridlerContextMenuItem) {
    if (!contextRow) return;
    formValues = normalizeFileRecordForFormer(contextRow);
    formRequest = item.id === 'delete' ? 'delete' : 'update';
    formOpened = true;
  }

  function onRowDblClick(_row: number, rowData: Record<string, unknown> | undefined) {
    if (!rowData) return;
    contextRow = rowData;
    void onMenuItemSelect({ id: 'edit', label: 'Edit' });
  }

  function onGridEvent(
    type: string,
    _item?: unknown,
    _column?: unknown,
    _coords?: unknown,
    detail?: Record<string, unknown>
  ) {
    if (type !== 'page_loaded' && type !== 'load') return;
    const total = detail?.total;
    if (typeof total === 'number') gridTotal = total;
  }

  function normalizeFileForm(data: FileForm): Record<string, unknown> {
    return {
      name: data.name.trim(),
      media_type: data.media_type.trim(),
      kind: data.kind.trim(),
      project_id: data.project_id?.trim() || undefined,
      thought_id: data.thought_id?.trim() || undefined
    };
  }

  async function handleFileSaved() {
    formOpened = false;
    if (contextRow) {
      selectedFile = normalizeFile(contextRow);
    }
    refreshKey += 1;
  }

  function formatBytes(n: number): string {
    if (n < 1024) return `${n} B`;
    if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
    return `${(n / 1024 / 1024).toFixed(1)} MB`;
  }

  function formatDate(value?: string): string {
    if (!value) return '—';
    return new Date(value).toLocaleString();
  }
</script>

<div class="space-y-4 w-full">
  <div class="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
    <div>
      <h2 class="text-2xl font-semibold text-white">Files</h2>
      <p class="mt-1 text-sm text-slate-400">
        {#if gridTotal === null}
          Server-backed grid
        {:else}
          {gridTotal} file{gridTotal !== 1 ? 's' : ''}
        {/if}
      </p>
    </div>
  </div>

  <div class="flex flex-col gap-4">
    <div class="rounded-2xl border border-white/10 bg-slate-950/30 p-3">
      {#key refreshKey}
        <ErrorBoundary namespace="FilesGridlerFull">
          <GridlerFull
            {columns}
            theme={adminGridTheme}
            rowMarkers="number"
            height={560}
            width="100%"
            pageSize={40}
            dataSource="resolvespec"
            dataSourceOptions={filesDataSourceOptions}
            serverSideSearch={true}
            searchColumns={['name', 'media_type', 'kind', 'project_id', 'thought_id']}
            {menuItems}
            {onGridEvent}
            {onRowClick}
            {onRowDblClick}
            {onRowContextMenu}
            {onMenuItemSelect}
          />
        </ErrorBoundary>
      {/key}
    </div>

    <aside class="rounded-2xl border border-white/10 bg-slate-900/70 p-4">
      <h3 class="text-sm font-semibold text-white">File Inspector</h3>
      {#if !selectedFile}
        <p class="mt-3 text-sm text-slate-500">Select a file row to inspect metadata.</p>
      {:else}
        <div class="mt-3 space-y-3 text-sm text-slate-300">
          <p class="text-base font-semibold text-slate-100">{selectedFile.name}</p>
          <div class="rounded-xl border border-white/10 bg-white/5 p-3 space-y-1">
            <p><strong class="text-slate-100">Type:</strong> {selectedFile.media_type}</p>
            <p><strong class="text-slate-100">Kind:</strong> {selectedFile.kind || '—'}</p>
            <p><strong class="text-slate-100">Size:</strong> {formatBytes(selectedFile.size_bytes)}</p>
            <p><strong class="text-slate-100">Thought:</strong> {selectedFile.thought_id || '—'}</p>
            <p><strong class="text-slate-100">Project:</strong> {selectedFile.project_id || '—'}</p>
            <p><strong class="text-slate-100">Uploaded:</strong> {formatDate(selectedFile.created_at)}</p>
            <p><strong class="text-slate-100">Updated:</strong> {formatDate(selectedFile.updated_at)}</p>
          </div>
          <div class="flex items-center gap-3">
            <a
              href={`/files/${selectedFile.id}`}
              target="_blank"
              rel="noreferrer"
              class="text-xs text-cyan-300 hover:text-cyan-200"
            >Download</a>
          </div>
        </div>
      {/if}
    </aside>
  </div>
</div>

<ErrorBoundary namespace="FilesFormer">
  <FormerShell
    bind:opened={formOpened}
    bind:values={formValues}
    bind:request={formRequest}
    title={formRequest === 'update' ? 'Edit File' : 'Delete File'}
    uniqueKeyField={FILE_PRIMARY_KEY}
    onAPICall={filesOnAPICall}
    afterGet={async (data) => normalizeFileRecordForFormer(data as Record<string, unknown>)}
    beforeSave={normalizeFileForm}
    afterSave={handleFileSaved}
    onClose={() => { formOpened = false; }}
  >
    {#snippet children(state)}
      <div class="space-y-4 p-4">
        <TextInputCtrl
          label="Name"
          name="name"
          required
          disabled={state.request === 'delete'}
          value={state.values?.name ?? ''}
          onchange={(v) => state.setState('values', { ...state.values, name: v })}
        />
        <TextInputCtrl
          label="Media Type"
          name="media_type"
          required
          disabled={state.request === 'delete'}
          value={state.values?.media_type ?? ''}
          onchange={(v) => state.setState('values', { ...state.values, media_type: v })}
        />
        <TextInputCtrl
          label="Kind"
          name="kind"
          disabled={state.request === 'delete'}
          value={state.values?.kind ?? ''}
          onchange={(v) => state.setState('values', { ...state.values, kind: v })}
        />
        <TextInputCtrl
          label="Project ID"
          name="project_id"
          disabled={state.request === 'delete'}
          value={state.values?.project_id ?? ''}
          onchange={(v) => state.setState('values', { ...state.values, project_id: v || undefined })}
        />
        <TextInputCtrl
          label="Thought ID"
          name="thought_id"
          disabled={state.request === 'delete'}
          value={state.values?.thought_id ?? ''}
          onchange={(v) => state.setState('values', { ...state.values, thought_id: v || undefined })}
        />
        {#if state.request !== 'delete'}
          <TextAreaCtrl
            label="Content"
            name="content"
            rows={4}
            disabled={true}
            value={state.values?.content ?? ''}
            onchange={() => {}}
          />
        {/if}
      </div>
    {/snippet}
  </FormerShell>
</ErrorBoundary>
