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

  type PlanForm = {
    id?: string;
    title: string;
    description: string;
    status: string;
    priority: string;
    owner?: string;
    due_date?: string;
    tags?: string;
  };

  const PLAN_PRIMARY_KEY = 'id';

  let selectedPlan = $state<Plan | null>(null);
  let gridTotal = $state<number | null>(null);
  let formOpened = $state(false);
  let formRequest = $state<'insert' | 'update' | 'delete'>('insert');
  let formValues = $state<PlanForm>({ title: '', description: '', status: 'draft', priority: 'medium' });
  let editorOpened = $state(false);
  let editorValues = $state<{ id?: string; description: string }>({ description: '' });
  let contextRow = $state<Record<string, unknown> | null>(null);
  let refreshKey = $state(0);
  const authToken = GlobalStateStore.getState().session.authToken ?? '';
  const planOnAPICall = $derived(FormerResolveSpecAPI({
    authToken,
    url: '/api/rs/public/plans'
  }));

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

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

  async function loadPlanFromRow(rowData: Record<string, unknown>): Promise<Plan> {
    const data = await planOnAPICall('read', 'update', undefined, String(rowData[PLAN_PRIMARY_KEY] ?? '')) as Record<string, unknown>;
    return normalizePlan(data);
  }

  function toPlanForm(plan: Plan): PlanForm {
    return {
      id: plan.id,
      title: plan.title,
      description: plan.description,
      status: plan.status,
      priority: plan.priority,
      owner: plan.owner,
      due_date: plan.due_date ? String(plan.due_date).slice(0, 10) : undefined,
      tags: plan.tags.join(', ')
    };
  }

  function normalizePlanRecordForFormer(data: Record<string, unknown>): PlanForm {
    return {
      id: data.id != null ? String(data.id) : undefined,
      title: typeof data.title === 'string' ? data.title : '',
      description: typeof data.description === 'string' ? data.description : '',
      status: typeof data.status === 'string' ? data.status : 'draft',
      priority: typeof data.priority === 'string' ? data.priority : 'medium',
      owner: typeof data.owner === 'string' && data.owner ? data.owner : undefined,
      due_date: typeof data.due_date === 'string' && data.due_date ? data.due_date.slice(0, 10) : undefined,
      tags: normalizeTags(data.tags).join(', ')
    };
  }

  async function onMenuItemSelect(item: GridlerContextMenuItem) {
    if (item.id === 'add') {
      formValues = { title: '', description: '', status: 'draft', priority: 'medium' };
      formRequest = 'insert';
      formOpened = true;
      return;
    }
    if (!contextRow) return;
    if (item.id === 'edit_content') {
      const plan = normalizePlan(contextRow);
      selectedPlan = plan;
      editorValues = { id: plan.id, description: plan.description };
      editorOpened = true;
      return;
    }
    const plan = normalizePlan(contextRow);
    formValues = toPlanForm(plan);
    formRequest = item.id === 'delete' ? 'delete' : 'update';
    formOpened = true;
  }

  function normalizePlanForm(data: PlanForm): Record<string, unknown> {
    return {
      title: data.title.trim(),
      description: data.description,
      status: data.status,
      priority: data.priority,
      owner: data.owner?.trim() || undefined,
      due_date: data.due_date || undefined,
      tags: data.tags?.split(',').map((t) => t.trim()).filter(Boolean) ?? []
    };
  }

  async function handlePlanSaved() {
    formOpened = false;
    if (contextRow?.id) {
      const data = await planOnAPICall('read', 'update', undefined, String(contextRow.id)) as Record<string, unknown>;
      selectedPlan = normalizePlan(data);
    }
    refreshKey++;
  }

  async function handlePlanEditorSaved() {
    editorOpened = false;
    if (editorValues.id) {
      const data = await planOnAPICall('read', 'update', undefined, String(editorValues.id)) as Record<string, unknown>;
      selectedPlan = normalizePlan(data);
      editorValues = { id: selectedPlan.id, description: selectedPlan.description };
    }
    refreshKey++;
  }

  const plansDataSourceOptions = {
    url: "/api/rs",
    authToken: GlobalStateStore.getState().session.authToken,
    schema: "public",
    entity: "plans",
    uniqueID: PLAN_PRIMARY_KEY,
    hotfields: [PLAN_PRIMARY_KEY],
    sort: [{ column: "updated_at", direction: "desc" }],
  } as unknown as {
    url: string;
    authToken?: string;
    schema: string;
    entity: string;
    uniqueID: string;
    hotfields: string[];
  };

  const columns: GridlerColumn[] = [
    { id: "title", title: "Title", dataKey: "title", width: 340 },
    { id: "status", title: "Status", dataKey: "status", width: 120 },
    { id: "priority", title: "Priority", dataKey: "priority", width: 110 },
    { id: "owner", title: "Owner", dataKey: "owner", width: 160 },
    { id: "due_date", title: "Due", dataKey: "due_date", width: 180, format: "datetime" },
    { id: "last_reviewed_at", title: "Reviewed", dataKey: "last_reviewed_at", width: 180, format: "datetime" },
    { id: "updated_at", title: "Updated", dataKey: "updated_at", width: 180, format: "datetime" },
  ];

  function normalizeTags(value: unknown): string[] {
    if (Array.isArray(value)) return value.map((t) => String(t).trim()).filter(Boolean);
    if (typeof value !== "string" || !value.trim()) return [];
    const trimmed = value.trim();
    if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
      return trimmed.slice(1, -1).split(",").map((t) => t.trim().replace(/^"(.*)"$/, "$1")).filter(Boolean);
    }
    return trimmed.split(",").map((t) => t.trim()).filter(Boolean);
  }

  function normalizePlan(rowData: Record<string, unknown>): Plan {
    return {
      id: String(rowData.id ?? ""),
      title: typeof rowData.title === "string" ? rowData.title : "",
      description: typeof rowData.description === "string" ? rowData.description : "",
      status: (typeof rowData.status === "string" ? rowData.status : "draft") as Plan["status"],
      priority: (typeof rowData.priority === "string" ? rowData.priority : "medium") as Plan["priority"],
      project_id: typeof rowData.project_id === "string" ? rowData.project_id : undefined,
      owner: typeof rowData.owner === "string" && rowData.owner ? rowData.owner : undefined,
      due_date: typeof rowData.due_date === "string" ? rowData.due_date : undefined,
      completed_at: typeof rowData.completed_at === "string" ? rowData.completed_at : undefined,
      reviewed_by: typeof rowData.reviewed_by === "string" ? rowData.reviewed_by : undefined,
      last_reviewed_at: typeof rowData.last_reviewed_at === "string" ? rowData.last_reviewed_at : undefined,
      supersedes_plan_id: typeof rowData.supersedes_plan_id === "string" ? rowData.supersedes_plan_id : undefined,
      tags: normalizeTags(rowData.tags),
      created_at: String(rowData.created_at ?? ""),
      updated_at: String(rowData.updated_at ?? ""),
    };
  }

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

  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 formatDate(value?: string): string {
    if (!value) return "—";
    return new Date(value).toLocaleString();
  }

  const statusClasses: Record<string, string> = {
    draft: "bg-slate-700/60 text-slate-300",
    active: "bg-cyan-900/60 text-cyan-200",
    blocked: "bg-amber-900/60 text-amber-200",
    completed: "bg-emerald-900/60 text-emerald-200",
    cancelled: "bg-slate-800/60 text-slate-500",
    superseded: "bg-purple-900/60 text-purple-300",
  };

  const priorityClasses: Record<string, string> = {
    low: "bg-slate-700/60 text-slate-400",
    medium: "bg-cyan-900/60 text-cyan-300",
    high: "bg-amber-900/60 text-amber-300",
    critical: "bg-rose-900/60 text-rose-300",
  };
</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">Plans</h2>
      <p class="mt-1 text-sm text-slate-400">
        {#if gridTotal === null}
          Server-backed grid
        {:else}
          {gridTotal} plan{gridTotal !== 1 ? "s" : ""}
        {/if}
      </p>
    </div>
    <div class="flex items-center gap-3">
      <button
        class="rounded-xl border border-white/10 bg-white/5 px-4 py-2 text-sm text-slate-200 transition hover:bg-white/10"
        onclick={() => { formValues = { title: '', description: '', status: 'draft', priority: 'medium' }; formRequest = 'insert'; formOpened = true; }}>New Plan</button
      >
    </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="PlansGridlerFull">
          <GridlerFull
            {columns}
            theme={adminGridTheme}
            rowMarkers="number"
            height={560}
            width="100%"
            pageSize={40}
            dataSource="resolvespec"
            dataSourceOptions={plansDataSourceOptions}
            serverSideSearch={true}
            searchColumns={["title", "description", "status", "priority", "owner"]}
            {menuItems}
            {onGridEvent}
            {onRowClick}
            {onRowDblClick}
            {onRowContextMenu}
            {onMenuItemSelect}
          />
        </ErrorBoundary>
      {/key}
    </div>

    <aside class="rounded-2xl border border-white/10 bg-slate-900/70 p-4">
      <div class="flex items-center justify-between">
        <h3 class="text-sm font-semibold text-white">Plan Inspector</h3>
        {#if selectedPlan}
          <button
            class="text-xs text-cyan-300 hover:text-cyan-200"
            onclick={() => {
              const plan = selectedPlan;
              if (!plan) return;
              editorValues = { id: plan.id, description: plan.description };
              editorOpened = true;
            }}>Edit Content</button>
        {/if}
      </div>

      {#if !selectedPlan}
        <p class="mt-3 text-sm text-slate-500">
          Select a plan row to inspect details and relationships.
        </p>
      {:else}
        <div class="mt-3 space-y-3 text-sm text-slate-300">
          <p class="text-base font-semibold text-slate-100">{selectedPlan.title}</p>

          <div class="flex flex-wrap gap-2">
            <span class={`inline-flex items-center rounded-lg px-2.5 py-0.5 text-xs font-medium ${statusClasses[selectedPlan.status] ?? "bg-slate-700/60 text-slate-300"}`}>
              {selectedPlan.status}
            </span>
            <span class={`inline-flex items-center rounded-lg px-2.5 py-0.5 text-xs font-medium ${priorityClasses[selectedPlan.priority] ?? "bg-slate-700/60 text-slate-300"}`}>
              {selectedPlan.priority}
            </span>
          </div>

          <div class="rounded-xl border border-white/10 bg-white/5 p-3 space-y-1">
            <p><strong class="text-slate-100">Owner:</strong> {selectedPlan.owner || "—"}</p>
            <p><strong class="text-slate-100">Due:</strong> {formatDate(selectedPlan.due_date)}</p>
            <p><strong class="text-slate-100">Completed:</strong> {formatDate(selectedPlan.completed_at)}</p>
            <p><strong class="text-slate-100">Last reviewed:</strong> {formatDate(selectedPlan.last_reviewed_at)}</p>
            <p><strong class="text-slate-100">Reviewed by:</strong> {selectedPlan.reviewed_by || "—"}</p>
            <p><strong class="text-slate-100">Created:</strong> {formatDate(selectedPlan.created_at)}</p>
            <p><strong class="text-slate-100">Updated:</strong> {formatDate(selectedPlan.updated_at)}</p>
          </div>

          {#if selectedPlan.description}
            <div class="rounded-xl border border-white/10 bg-white/5 p-3">
              <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Description</p>
              <p class="mt-2 whitespace-pre-wrap text-slate-300">{selectedPlan.description}</p>
            </div>
          {/if}

          {#if selectedPlan.tags.length > 0}
            <div class="rounded-xl border border-white/10 bg-white/5 p-3">
              <p class="text-xs uppercase tracking-[0.18em] text-slate-500">Tags</p>
              <div class="mt-2 flex flex-wrap gap-1.5">
                {#each selectedPlan.tags as tag}
                  <span class="rounded-md bg-white/10 px-2 py-0.5 text-xs text-slate-300">{tag}</span>
                {/each}
              </div>
            </div>
          {/if}

          {#if selectedPlan.project_id || selectedPlan.supersedes_plan_id}
            <div class="rounded-xl border border-white/10 bg-white/5 p-3 space-y-1">
              {#if selectedPlan.project_id}
                <p><strong class="text-slate-100">Project:</strong> <span class="font-mono text-xs text-slate-400">{selectedPlan.project_id}</span></p>
              {/if}
              {#if selectedPlan.supersedes_plan_id}
                <p><strong class="text-slate-100">Supersedes:</strong> <span class="font-mono text-xs text-slate-400">{selectedPlan.supersedes_plan_id}</span></p>
              {/if}
            </div>
          {/if}
        </div>
      {/if}
    </aside>
  </div>
</div>

<ErrorBoundary namespace="PlansEditorFormer">
  <FormerShell
    bind:opened={editorOpened}
    bind:values={editorValues}
    request="update"
    title="Edit Plan Description"
    uniqueKeyField={PLAN_PRIMARY_KEY}
    width="min(96vw, 90rem)"
    onAPICall={planOnAPICall}
    beforeSave={(data) => ({ description: data.description })}
    afterSave={handlePlanEditorSaved}
    onClose={() => { editorOpened = false; }}
  >
    {#snippet children(state)}
      <ContentEditorField
        filename="plan.md"
        value={state.values?.description ?? ''}
        onchange={(v) => state.setState('values', { ...state.values, description: v })}
      />
    {/snippet}
  </FormerShell>
</ErrorBoundary>

<ErrorBoundary namespace="PlansFormer">
  <FormerShell
    bind:opened={formOpened}
    bind:values={formValues}
    bind:request={formRequest}
    title={formRequest === 'insert' ? 'New Plan' : formRequest === 'update' ? 'Edit Plan' : 'Delete Plan'}
    uniqueKeyField={PLAN_PRIMARY_KEY}
    onAPICall={planOnAPICall}
    afterGet={async (data) => normalizePlanRecordForFormer(data as Record<string, unknown>)}
    beforeSave={normalizePlanForm}
    afterSave={handlePlanSaved}
    onClose={() => { formOpened = false; }}
  >
    {#snippet children(state)}
      <div class="space-y-4 p-4">
        <TextInputCtrl
          label="Title"
          name="title"
          required
          disabled={state.request === 'delete'}
          value={state.values?.title ?? ''}
          onchange={(v) => state.setState('values', { ...state.values, title: v })}
        />
        <ContentEditorField
          filename="plan.md"
          value={state.values?.description ?? ''}
          disabled={state.request === 'delete'}
          onchange={(v) => state.setState('values', { ...state.values, description: v })}
        />
        <NativeSelectCtrl
          label="Status"
          name="status"
          disabled={state.request === 'delete'}
          value={state.values?.status ?? 'draft'}
          options={['draft', 'active', 'blocked', 'completed', 'cancelled', 'superseded']}
          onchange={(v) => state.setState('values', { ...state.values, status: v })}
        />
        <NativeSelectCtrl
          label="Priority"
          name="priority"
          disabled={state.request === 'delete'}
          value={state.values?.priority ?? 'medium'}
          options={['low', 'medium', 'high', 'critical']}
          onchange={(v) => state.setState('values', { ...state.values, priority: v })}
        />
        <TextInputCtrl
          label="Owner"
          name="owner"
          disabled={state.request === 'delete'}
          value={state.values?.owner ?? ''}
          onchange={(v) => state.setState('values', { ...state.values, owner: v || undefined })}
        />
        <TextInputCtrl
          label="Due Date"
          name="due_date"
          type="date"
          disabled={state.request === 'delete'}
          value={state.values?.due_date ?? ''}
          onchange={(v) => state.setState('values', { ...state.values, due_date: v || undefined })}
        />
        <TextInputCtrl
          label="Tags"
          name="tags"
          placeholder="comma-separated"
          disabled={state.request === 'delete'}
          value={state.values?.tags ?? ''}
          onchange={(v) => state.setState('values', { ...state.values, tags: v })}
        />
      </div>
    {/snippet}
  </FormerShell>
</ErrorBoundary>
