import { useState } from 'react';
import { Link, useNavigate, useSearchParams } from 'react-router';
import { FolderOpen, LayoutGrid, Plus, ArrowRight, Type, Rocket, FileText, Layout } from 'lucide-react';
import { useQueryClient } from '@tanstack/react-query';
import { useGlobal } from 'hooks/useGlobal';
import { useClient } from 'hooks/useClient';
import { useUserState } from 'state/useUserState';
import { useDashboardState, persistDashboardState } from 'state/useDashboardState';
import { WorkspaceLayout, WorkspaceView, createDefaultLayout, uid } from '../../components/Workspace';
import type { LayoutNode, DashboardDefinition } from '../../components/Workspace';
import { Button } from '@/components/ui/button';
import { generateSlug, slugify } from 'helpers/slug';
import { FileBrowserApp } from '../FileBrowser';
import { useAppRegistry } from '../../AppRegistry';
import {
  CREATING_DASHBOARD_KEY,
  EDITING_DASHBOARD_KEY,
  NEW_DASH_NAME_KEY,
  NEW_DASH_DESC_KEY,
  NEW_DASH_TEMPLATE_KEY,
} from './constants';

// --- Layout Templates ---

type LayoutTemplate = {
  name: string;
  layout: () => LayoutNode;
};

const templates: LayoutTemplate[] = [
  {
    name: 'Single',
    layout: () => ({ type: 'panel', id: uid(), appType: null }),
  },
  {
    name: '2 Columns',
    layout: () => ({
      type: 'group',
      id: uid(),
      direction: 'horizontal',
      children: [
        { node: { type: 'panel', id: uid(), appType: null }, size: 50 },
        { node: { type: 'panel', id: uid(), appType: null }, size: 50 },
      ],
    }),
  },
  {
    name: 'Main + Side',
    layout: () => ({
      type: 'group',
      id: uid(),
      direction: 'horizontal',
      children: [
        { node: { type: 'panel', id: uid(), appType: null }, size: 50 },
        {
          node: {
            type: 'group',
            id: uid(),
            direction: 'vertical',
            children: [
              { node: { type: 'panel', id: uid(), appType: null }, size: 50 },
              { node: { type: 'panel', id: uid(), appType: null }, size: 50 },
            ],
          },
          size: 50,
        },
      ],
    }),
  },
  {
    name: 'Sidebar',
    layout: () => ({
      type: 'group',
      id: uid(),
      direction: 'horizontal',
      children: [
        { node: { type: 'panel', id: uid(), appType: null }, size: 25 },
        { node: { type: 'panel', id: uid(), appType: null }, size: 75 },
      ],
    }),
  },
  {
    name: '2x2 Grid',
    layout: () => ({
      type: 'group',
      id: uid(),
      direction: 'vertical',
      children: [
        {
          node: {
            type: 'group',
            id: uid(),
            direction: 'horizontal',
            children: [
              { node: { type: 'panel', id: uid(), appType: null }, size: 50 },
              { node: { type: 'panel', id: uid(), appType: null }, size: 50 },
            ],
          },
          size: 50,
        },
        {
          node: {
            type: 'group',
            id: uid(),
            direction: 'horizontal',
            children: [
              { node: { type: 'panel', id: uid(), appType: null }, size: 50 },
              { node: { type: 'panel', id: uid(), appType: null }, size: 50 },
            ],
          },
          size: 50,
        },
      ],
    }),
  },
  {
    name: 'Cols + Bottom',
    layout: () => ({
      type: 'group',
      id: uid(),
      direction: 'vertical',
      children: [
        {
          node: {
            type: 'group',
            id: uid(),
            direction: 'horizontal',
            children: [
              { node: { type: 'panel', id: uid(), appType: null }, size: 50 },
              { node: { type: 'panel', id: uid(), appType: null }, size: 50 },
            ],
          },
          size: 70,
        },
        { node: { type: 'panel', id: uid(), appType: null }, size: 30 },
      ],
    }),
  },
];

// --- Template Thumbnails ---

const ThumbnailNode = ({ node }: { node: LayoutNode }) => {
  if (node.type === 'panel') {
    return <div className="h-full w-full bg-duck-teal/15 border border-duck-teal/20 rounded-sm" />;
  }
  const isH = node.direction === 'horizontal';
  return (
    <div className={`flex h-full w-full gap-1.5 ${isH ? 'flex-row' : 'flex-col'}`}>
      {node.children.map((child) => (
        <div key={child.node.id} style={{ flex: child.size }} className="min-h-0 min-w-0">
          <ThumbnailNode node={child.node} />
        </div>
      ))}
    </div>
  );
};

const TemplateCell = ({ index }: { index: number }) => {
  const tpl = templates[index]!;
  const [selected, setSelected] = useGlobal<number>(NEW_DASH_TEMPLATE_KEY, 0);
  const node = tpl.layout();
  const isSelected = selected === index;

  return (
    <div className="h-full w-full">
      <button
        type="button"
        onClick={() => setSelected(index)}
        className={`flex h-full w-full flex-col items-center justify-center gap-2 p-3 cursor-pointer transition-colors ${
          isSelected ? 'bg-duck-teal/10' : 'hover:bg-duck-dark/5'
        }`}
      >
        <div className="w-full flex-1 min-h-0 rounded border border-duck-dark/20 overflow-hidden">
          <ThumbnailNode node={node} />
        </div>
        <span className={`text-xs font-medium ${isSelected ? 'text-duck-teal' : 'text-duck-dark/60'}`}>{tpl.name}</span>
      </button>
    </div>
  );
};

// --- Panel: Template Picker (3x2 layout) ---

const tplPanelLayout: LayoutNode = {
  type: 'group',
  id: 'tpl-root',
  direction: 'vertical',
  children: [
    {
      node: {
        type: 'group',
        id: 'tpl-row-0',
        direction: 'horizontal',
        children: [
          { node: { type: 'panel', id: 'tpl-0', appType: 'tpl-0' }, size: 33.33 },
          { node: { type: 'panel', id: 'tpl-1', appType: 'tpl-1' }, size: 33.33 },
          { node: { type: 'panel', id: 'tpl-2', appType: 'tpl-2' }, size: 33.34 },
        ],
      },
      size: 50,
    },
    {
      node: {
        type: 'group',
        id: 'tpl-row-1',
        direction: 'horizontal',
        children: [
          { node: { type: 'panel', id: 'tpl-3', appType: 'tpl-3' }, size: 33.33 },
          { node: { type: 'panel', id: 'tpl-4', appType: 'tpl-4' }, size: 33.33 },
          { node: { type: 'panel', id: 'tpl-5', appType: 'tpl-5' }, size: 33.34 },
        ],
      },
      size: 50,
    },
  ],
};

const tplRegistry = Object.fromEntries(
  templates.map((tpl, i) => [
    `tpl-${i}`,
    { name: tpl.name, icon: Layout, component: () => <TemplateCell index={i} /> },
  ]),
);

const TemplatePanel = () => (
  <WorkspaceLayout layout={tplPanelLayout} onLayoutChange={() => {}} registry={tplRegistry} noHeader />
);

// --- Panel: Name ---

const NamePanel = () => {
  const [name, setName] = useGlobal<string>(NEW_DASH_NAME_KEY, '');
  const [description, setDescription] = useGlobal<string>(NEW_DASH_DESC_KEY, '');

  return (
    <div className="h-full w-full">
      <div className="flex h-full flex-col gap-4 p-4">
        <div className="flex flex-col gap-1.5">
          <label className="text-xs font-medium text-duck-dark/50 flex items-center gap-1.5">
            <Type className="h-3 w-3" />
            Name
          </label>
          <input
            type="text"
            value={name}
            onChange={(ev) => setName(ev.target.value)}
            placeholder="My Dashboard"
            autoFocus
            className="rounded-lg border border-duck-dark/20 bg-background px-3 py-2 text-sm text-duck-dark placeholder:text-duck-dark/30 focus:outline-none focus:ring-2 focus:ring-duck-teal/30 focus:border-duck-teal/50"
          />
        </div>
        <div className="flex flex-1 min-h-0 flex-col gap-1.5">
          <label className="text-xs font-medium text-duck-dark/50 flex items-center gap-1.5">
            <FileText className="h-3 w-3" />
            Description
          </label>
          <textarea
            value={description}
            onChange={(ev) => setDescription(ev.target.value)}
            placeholder="What is this dashboard for?"
            className="flex-1 min-h-0 resize-none rounded-lg border border-duck-dark/20 bg-background px-3 py-2 text-sm text-duck-dark placeholder:text-duck-dark/30 focus:outline-none focus:ring-2 focus:ring-duck-teal/30 focus:border-duck-teal/50"
          />
        </div>
      </div>
    </div>
  );
};

// --- Panel: Create ---

const CreatePanel = () => {
  const navigate = useNavigate();
  const queryClient = useQueryClient();
  const client = useClient();
  const [name, setName] = useGlobal<string>(NEW_DASH_NAME_KEY, '');
  const [filePath] = useUserState<string>('files/currentPath', '/');
  const { value: dashboards, setValue: setDashboards } = useDashboardState<DashboardDefinition[]>('workspaces', []);

  const cwd = filePath === '/' ? '~' : `~/${filePath.replace(/^\//, '')}`;

  const [description, setDescription] = useGlobal<string>(NEW_DASH_DESC_KEY, '');
  const [templateIdx, setTemplateIdx] = useGlobal<number>(NEW_DASH_TEMPLATE_KEY, 0);
  const [editingId, setEditingId] = useGlobal<string | null>(EDITING_DASHBOARD_KEY, null);
  const [, setCreating] = useGlobal<boolean>(CREATING_DASHBOARD_KEY, false);
  const [, setSearchParams] = useSearchParams();

  const isEditing = !!editingId;

  const handleSubmit = () => {
    const trimmed = name.trim();
    if (!trimmed) return;
    const desc = description.trim();

    if (isEditing) {
      const existingIds = new Set(dashboards.filter((w) => w.id !== editingId).map((w) => w.id));
      let newId = slugify(trimmed) || generateSlug();
      while (existingIds.has(newId)) newId = `${newId}-${generateSlug(1)}`;

      const idChanged = newId !== editingId;

      setDashboards((prev) =>
        prev.map((ws) =>
          ws.id === editingId ? { ...ws, id: newId, name: trimmed, description: desc || undefined, templateIdx } : ws,
        ),
      );

      const currentState = queryClient.getQueryData<Record<string, unknown>>(['DASHBOARD_STATE']) ?? {};
      const newLayoutKey = `ws-layout-${newId}`;

      // Rebuild from the template only when the user actually picked a different one. This ran
      // unconditionally, so renaming a dashboard — or fixing a typo in its description — threw away
      // however its panels had been arranged and whichever apps were in them. A template is a seed
      // chosen once at creation; it is not a description of the dashboard as it now stands.
      const kept = currentState[`ws-layout-${editingId}`];
      const templateChanged = dashboards.find((w) => w.id === editingId)?.templateIdx !== templateIdx;
      const keepable = !!kept && typeof kept === 'object' && !Array.isArray(kept);
      const wsLayout =
        !templateChanged && keepable ? kept : (templates[templateIdx]?.layout() ?? createDefaultLayout());

      if (idChanged) {
        // A rename is the same dashboard under a new key, so everything keyed on the old id moves with it.
        // Dropping the terminal maps without carrying them over abandoned every shell the dashboard held:
        // the panels came back empty and the processes stayed alive with nothing pointing at them.
        const carried: Record<string, unknown> = { [newLayoutKey]: wsLayout };
        for (const prefix of ['ws-terminals', 'ws-host-terminals']) {
          const value = currentState[`${prefix}-${editingId}`];
          if (value !== undefined) carried[`${prefix}-${newId}`] = value;
        }
        const oldKeys = [`ws-layout-${editingId}`, `ws-terminals-${editingId}`, `ws-host-terminals-${editingId}`];
        const cleaned = { ...currentState };
        for (const k of oldKeys) delete cleaned[k];
        queryClient.setQueryData(['DASHBOARD_STATE'], { ...cleaned, ...carried });

        // Key order is load-bearing: the server walks this object in insertion order, `ws-layout-<new>`
        // upserts the row and `ws-terminals-<new>` only *updates* one, so the new key must be written
        // before the old `ws-layout-<old>: null` deletes the row it was copied from.
        const patch: Record<string, unknown> = { ...carried };
        for (const k of oldKeys) patch[k] = null;
        persistDashboardState(client, queryClient, patch);
      } else if (wsLayout !== kept) {
        queryClient.setQueryData(['DASHBOARD_STATE'], { ...currentState, [newLayoutKey]: wsLayout });
        persistDashboardState(client, queryClient, { [newLayoutKey]: wsLayout });
      }

      setEditingId(null);
      setSearchParams(
        (p) => {
          p.set('selected', newId);
          return p;
        },
        { replace: true },
      );
    } else {
      const existingIds = new Set(dashboards.map((w) => w.id));
      let id = slugify(trimmed) || generateSlug();
      while (existingIds.has(id)) id = `${id}-${generateSlug(1)}`;

      const ws: DashboardDefinition = {
        id,
        name: trimmed,
        cwd,
        root: undefined,
        description: desc || undefined,
        templateIdx,
      };
      const wsLayout = templates[templateIdx]?.layout() ?? createDefaultLayout();
      const layoutKey = `ws-layout-${ws.id}`;

      setDashboards((prev) => [...prev, ws]);
      const currentState = queryClient.getQueryData<Record<string, unknown>>(['DASHBOARD_STATE']) ?? {};
      queryClient.setQueryData(['DASHBOARD_STATE'], { ...currentState, [layoutKey]: wsLayout });
      persistDashboardState(client, queryClient, { [layoutKey]: wsLayout });

      setName('');
      setDescription('');
      setTemplateIdx(0);
      navigate(`/dashboards/${ws.id}`);
    }
  };

  return (
    <div className="h-full w-full">
      <div className="flex h-full flex-col items-center justify-center gap-4 p-4">
        <Rocket className="h-8 w-8 text-duck-teal/40" />
        <div className="text-center">
          <p className="text-sm font-medium text-duck-dark/70">{name.trim() || 'Untitled'}</p>
          <p className="text-xs font-mono text-duck-dark/40 mt-1">{cwd}</p>
        </div>
        <Button
          onClick={handleSubmit}
          disabled={!name.trim()}
          className="bg-duck-teal hover:bg-duck-teal/90 cursor-pointer disabled:opacity-40"
        >
          <Plus className="h-4 w-4 mr-1" />
          {isEditing ? 'Update Dashboard' : 'Create Dashboard'}
        </Button>
        {/* Pure navigation, so it is an anchor. The create button above stays a button on purpose: it
            writes a dashboard and *then* goes there, and a link cannot express the "and then". */}
        {isEditing && (
          <Button asChild variant="outline" className="cursor-pointer">
            <Link to={`/dashboards/${editingId}`}>
              <ArrowRight className="h-4 w-4 mr-1" />
              Open Dashboard
            </Link>
          </Button>
        )}
      </div>
    </div>
  );
};

// --- CwdFileBrowser ---

const CwdFileBrowser = () => {
  const [currentPath] = useUserState<string>('files/currentPath', '/');
  const [basePath] = useState(currentPath);
  return <FileBrowserApp basePath={basePath} />;
};

// --- New Dashboard Layout ---

const newDashLayout: LayoutNode = {
  type: 'group',
  id: 'new-ws-root',
  direction: 'vertical',
  children: [
    {
      node: {
        type: 'group',
        id: 'new-ws-top',
        direction: 'horizontal',
        children: [
          { node: { type: 'panel', id: 'new-ws-name', appType: 'new-ws-name' }, size: 30 },
          { node: { type: 'panel', id: 'new-ws-dir', appType: 'file-browser-cwd' }, size: 70 },
        ],
      },
      size: 50,
    },
    {
      node: {
        type: 'group',
        id: 'new-ws-bottom',
        direction: 'horizontal',
        children: [
          { node: { type: 'panel', id: 'new-ws-template', appType: 'new-ws-template' }, size: 70 },
          { node: { type: 'panel', id: 'new-ws-create', appType: 'new-ws-create' }, size: 30 },
        ],
      },
      size: 50,
    },
  ],
};

const NewDashboardForm = () => {
  const { registry } = useAppRegistry();
  const newDashRegistry = {
    ...registry,
    'file-browser-cwd': { name: 'File Browser', icon: FolderOpen, component: CwdFileBrowser },
    'new-ws-name': { name: 'Name', icon: Type, component: NamePanel },
    'new-ws-template': { name: 'Template', icon: Layout, component: TemplatePanel },
    'new-ws-create': { name: 'Create', icon: Rocket, component: CreatePanel },
  };

  return <WorkspaceLayout layout={newDashLayout} onLayoutChange={() => {}} registry={newDashRegistry} noHeader />;
};

// --- Empty State ---

// Only reached when nothing is selected AND nothing is being created or edited — DashboardPreview checks
// those first now, so this component no longer needs its own creating/editing branch.
const DashboardPreviewEmpty = () => {
  const [, setCreating] = useGlobal<boolean>(CREATING_DASHBOARD_KEY, false);
  const [, setName] = useGlobal<string>(NEW_DASH_NAME_KEY, '');
  const [, setDescription] = useGlobal<string>(NEW_DASH_DESC_KEY, '');
  const [, setTemplateIdx] = useGlobal<number>(NEW_DASH_TEMPLATE_KEY, 0);
  const [, setFilePath] = useUserState<string>('files/currentPath', '/');

  const handleCreate = () => {
    setName(generateSlug());
    setDescription('');
    setTemplateIdx(0);
    setFilePath('/');
    setCreating(true);
  };

  return (
    <div className="h-full w-full">
      <div className="flex h-full w-full flex-col items-center justify-center gap-4">
        <LayoutGrid className="h-10 w-10 text-white/80" />
        <p className="text-sm text-white/80">Select a dashboard or create a new one</p>
        <Button onClick={handleCreate} className="bg-duck-teal hover:bg-duck-teal/90 cursor-pointer">
          <Plus className="h-4 w-4 mr-1" />
          New Dashboard
        </Button>
      </div>
    </div>
  );
};

// --- Preview Inner ---

const DashboardPreviewInner = ({ dashboard }: { dashboard: DashboardDefinition }) => {
  const ws = useDashboardState<LayoutNode>(`ws-layout-${dashboard.id}`, createDefaultLayout());

  return <WorkspaceView workspace={ws} cwd={dashboard.cwd} root={dashboard.root} />;
};

// --- Main Export ---

export const DashboardPreview = () => {
  const [searchParams] = useSearchParams();
  const selectedId = searchParams.get('selected');
  const { value: dashboards } = useDashboardState<DashboardDefinition[]>('workspaces', []);
  const [creating] = useGlobal<boolean>(CREATING_DASHBOARD_KEY, false);
  const [editingId] = useGlobal<string | null>(EDITING_DASHBOARD_KEY, null);

  // The form wins over the preview, but only for the dashboard actually selected. It used to be reachable
  // only through the empty state, so editing had to clear ?selected= to get to it — which meant the URL
  // forgot which dashboard you were editing, and on mobile it closed the very panel the form renders in.
  // The `editingId === selectedId` guard is what stops an abandoned edit from showing over a different
  // dashboard reached by back/forward or a pasted link (a click is already handled by the list).
  if (creating || (editingId && editingId === selectedId)) return <NewDashboardForm />;

  const dashboard = selectedId ? dashboards.find((ws) => ws.id === selectedId) : null;

  if (!dashboard) return <DashboardPreviewEmpty />;

  return (
    <div className="relative h-full w-full">
      <DashboardPreviewInner key={dashboard.id} dashboard={dashboard} />
      <Link
        to={`/dashboards/${dashboard.id}`}
        className="absolute inset-0 z-10 flex items-center justify-center bg-transparent hover:bg-duck-dark/10 transition-colors group"
      >
        <ArrowRight className="h-16 w-16 text-duck-teal/0 group-hover:text-duck-teal/60 transition-colors" />
      </Link>
    </div>
  );
};
