import { useMemo, useState } from 'react';
import {
  AlertCircle,
  Braces,
  Code2,
  Copy,
  Eye,
  FileText,
  Folder,
  FolderOpen,
  Image as ImageIcon,
  Pencil,
  RefreshCw,
  Settings,
  Trash,
} from 'lucide-react';
import { defineStory, useBoolean, useSelect } from '@djangocfg/playground';
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuTrigger,
} from '@djangocfg/ui-core/components';

import { TreeRoot } from './TreeRoot';
import { TreeProvider } from './context/TreeContext';
import { TreeContent } from './components/TreeContent';
import { useTreeActions } from './context/hooks';
import { createDemoTree, type DemoNode } from './data/createDemoTree';
import type { TreeItemId, TreeNode } from './types';

export default defineStory({
  title: 'Tools/Tree',
  component: TreeRoot,
  description: 'Decomposed shadcn-style tree (own engine, no external libs).',
});

// ---------------------------------------------------------------------------
// Sample data
// ---------------------------------------------------------------------------

interface FsNode {
  name: string;
  ext?: string;
  status?: 'modified' | 'error' | 'disabled';
}

const fs: TreeNode<FsNode>[] = [
  {
    id: 'src',
    data: { name: 'src' },
    children: [
      {
        id: 'components',
        data: { name: 'components' },
        children: [
          { id: 'Button.tsx', data: { name: 'Button.tsx', ext: 'tsx' } },
          {
            id: 'Card.tsx',
            data: { name: 'Card.tsx', ext: 'tsx', status: 'modified' },
          },
          { id: 'Tree.tsx', data: { name: 'Tree.tsx', ext: 'tsx' } },
        ],
      },
      {
        id: 'hooks',
        data: { name: 'hooks' },
        children: [
          { id: 'useDebounce.ts', data: { name: 'useDebounce.ts', ext: 'ts' } },
          {
            id: 'useTheme.ts',
            data: { name: 'useTheme.ts', ext: 'ts', status: 'error' },
          },
        ],
      },
      { id: 'index.ts', data: { name: 'index.ts', ext: 'ts' } },
      {
        id: '_old.ts',
        data: { name: '_old.ts', ext: 'ts', status: 'disabled' },
        disabled: true,
      },
    ],
  },
  {
    id: 'public',
    data: { name: 'public' },
    children: [
      { id: 'favicon.ico', data: { name: 'favicon.ico', ext: 'ico' } },
      { id: 'logo.svg', data: { name: 'logo.svg', ext: 'svg' } },
    ],
  },
  { id: 'package.json', data: { name: 'package.json', ext: 'json' } },
  { id: 'README.md', data: { name: 'README.md', ext: 'md' } },
];

const getName = (n: TreeNode<FsNode | DemoNode>) => n.data.name;

// ---------------------------------------------------------------------------
// 1) Default — sensible cozy defaults
// ---------------------------------------------------------------------------

export const Default = () => (
  <div className="h-96 w-80 rounded-md border border-border bg-card">
    <TreeRoot<FsNode>
      data={fs}
      getItemName={getName}
      initialExpandedIds={['src']}
    />
  </div>
);

// ---------------------------------------------------------------------------
// 1.5) WithActivationModes — VSCode-style click semantics
// ---------------------------------------------------------------------------

export const WithActivationModes = () => {
  const [log, setLog] = useState<string[]>([]);
  const append = (s: string) =>
    setLog((prev) => [s, ...prev].slice(0, 8));

  return (
    <div className="flex flex-col gap-3">
      <div className="grid grid-cols-3 gap-3">
        {(['single-click', 'double-click', 'single-click-preview'] as const).map(
          (mode) => (
            <div key={mode} className="flex flex-col gap-2">
              <span className="text-xs font-medium text-muted-foreground">
                {mode}
              </span>
              <div className="h-72 w-64 rounded-md border border-border bg-card">
                <TreeRoot<FsNode>
                  data={fs}
                  getItemName={getName}
                  initialExpandedIds={['src']}
                  activationMode={mode}
                  onActivate={(node, { preview }) =>
                    append(
                      `[${mode}] ${node.data.name}${preview ? ' (preview)' : ''}`,
                    )
                  }
                />
              </div>
            </div>
          ),
        )}
      </div>
      <div className="rounded-md border border-border bg-muted/30 p-3 font-mono text-xs">
        {log.length === 0 ? (
          <span className="text-muted-foreground">
            click / double-click a file to log activations
          </span>
        ) : (
          log.map((line, i) => <div key={i}>{line}</div>)
        )}
      </div>
    </div>
  );
};

// ---------------------------------------------------------------------------
// 2) Densities — three presets side-by-side for comparison
// ---------------------------------------------------------------------------

export const Densities = () => (
  <div className="grid grid-cols-3 gap-3">
    {(['compact', 'cozy', 'comfortable'] as const).map((density) => (
      <div key={density} className="flex flex-col gap-2">
        <span className="text-xs font-medium text-muted-foreground capitalize">
          {density}
        </span>
        <div className="h-80 w-64 rounded-md border border-border bg-card">
          <TreeRoot<FsNode>
            data={fs}
            getItemName={getName}
            initialExpandedIds={['src', 'components']}
            appearance={{ density }}
          />
        </div>
      </div>
    ))}
  </div>
);

// ---------------------------------------------------------------------------
// 3) WithIcons — file-type icons via renderIcon slot
// ---------------------------------------------------------------------------

const FileIcon = ({ ext }: { ext?: string }) => {
  const props = {
    'aria-hidden': true,
    style: { width: 'var(--tree-icon-size)', height: 'var(--tree-icon-size)' },
    strokeWidth: 1.5 as const,
    className: 'shrink-0',
  };
  if (ext === 'tsx' || ext === 'ts')
    return <Code2 {...props} className={`${props.className} text-blue-400`} />;
  if (ext === 'json')
    return <Braces {...props} className={`${props.className} text-amber-400`} />;
  if (ext === 'svg' || ext === 'ico')
    return <ImageIcon {...props} className={`${props.className} text-pink-400`} />;
  if (ext === 'md')
    return <FileText {...props} className={`${props.className} text-sky-400`} />;
  return <FileText {...props} className={`${props.className} text-muted-foreground/80`} />;
};

const FolderTinted = ({ isExpanded }: { isExpanded: boolean }) => {
  const Icon = isExpanded ? FolderOpen : Folder;
  return (
    <Icon
      aria-hidden
      strokeWidth={1.5}
      style={{
        width: 'var(--tree-icon-size)',
        height: 'var(--tree-icon-size)',
      }}
      className="shrink-0 text-amber-300/90"
    />
  );
};

export const WithIcons = () => (
  <div className="h-96 w-80 rounded-md border border-border bg-card">
    <TreeRoot<FsNode>
      data={fs}
      getItemName={getName}
      initialExpandedIds={['src', 'components', 'hooks', 'public']}
      renderIcon={({ node, isFolder, isExpanded }) =>
        isFolder ? (
          <FolderTinted isExpanded={isExpanded} />
        ) : (
          <FileIcon ext={node.data.ext} />
        )
      }
    />
  </div>
);

// ---------------------------------------------------------------------------
// 4) WithStatus — modified / error / disabled rows via renderLabel
// ---------------------------------------------------------------------------

export const WithStatus = () => (
  <div className="h-96 w-80 rounded-md border border-border bg-card">
    <TreeRoot<FsNode>
      data={fs}
      getItemName={getName}
      initialExpandedIds={['src', 'components', 'hooks']}
      renderLabel={({ node }) => {
        const { name, status } = node.data;
        if (status === 'modified')
          return (
            <span className="flex min-w-0 items-center gap-1.5">
              <span className="truncate text-amber-400" style={{ fontSize: 'var(--tree-font-size)' }}>
                {name}
              </span>
              <span className="text-[10px] font-medium text-amber-400/80">M</span>
            </span>
          );
        if (status === 'error')
          return (
            <span className="flex min-w-0 items-center gap-1.5">
              <AlertCircle aria-hidden strokeWidth={2} className="size-3 shrink-0 text-destructive" />
              <span className="truncate text-destructive" style={{ fontSize: 'var(--tree-font-size)' }}>
                {name}
              </span>
            </span>
          );
        return (
          <span
            className="truncate"
            style={{ fontSize: 'var(--tree-font-size)' }}
          >
            {name}
          </span>
        );
      }}
    />
  </div>
);

// ---------------------------------------------------------------------------
// 5) WithActions — rename / delete buttons appear on hover
// ---------------------------------------------------------------------------

export const WithActions = () => {
  const [last, setLast] = useState<string>('');
  return (
    <div className="flex h-96 w-80 flex-col gap-2">
      <div className="text-xs text-muted-foreground">Last: {last || '—'}</div>
      <div className="flex-1 rounded-md border border-border bg-card">
        <TreeRoot<FsNode>
          data={fs}
          getItemName={getName}
          initialExpandedIds={['src']}
          renderActions={({ node }) => (
            <>
              <button
                type="button"
                aria-label={`Rename ${node.data.name}`}
                onClick={() => setLast(`Rename ${node.id}`)}
                className="rounded p-0.5 hover:bg-foreground/10"
              >
                <Pencil aria-hidden className="size-3 text-muted-foreground" />
              </button>
              <button
                type="button"
                aria-label={`Delete ${node.data.name}`}
                onClick={() => setLast(`Delete ${node.id}`)}
                className="rounded p-0.5 hover:bg-destructive/15 hover:text-destructive"
              >
                <Trash aria-hidden className="size-3 text-muted-foreground" />
              </button>
            </>
          )}
        />
      </div>
    </div>
  );
};

// ---------------------------------------------------------------------------
// 6) WithSearch — built-in search bar + match highlight
// ---------------------------------------------------------------------------

export const WithSearch = () => (
  <div className="h-96 w-80 rounded-md border border-border bg-card">
    <TreeRoot<FsNode>
      data={fs}
      getItemName={getName}
      initialExpandedIds={['src', 'components', 'hooks', 'public']}
      enableSearch
    />
  </div>
);

// ---------------------------------------------------------------------------
// 7) WithIndentGuides
// ---------------------------------------------------------------------------

export const WithIndentGuides = () => (
  <div className="h-96 w-80 rounded-md border border-border bg-card">
    <TreeRoot<FsNode>
      data={fs}
      getItemName={getName}
      initialExpandedIds={['src', 'components']}
      showIndentGuides
    />
  </div>
);

// ---------------------------------------------------------------------------
// 8) WithContextMenu — right-click via renderContextMenu slot
// ---------------------------------------------------------------------------

export const WithContextMenu = () => {
  const [last, setLast] = useState<string>('');
  return (
    <div className="flex h-96 w-80 flex-col gap-2">
      <div className="text-xs text-muted-foreground">Last: {last || '—'}</div>
      <div className="flex-1 rounded-md border border-border bg-card">
        <TreeRoot<FsNode>
          data={fs}
          getItemName={getName}
          initialExpandedIds={['src']}
          renderContextMenu={({ node }, trigger) => (
            <ContextMenu>
              <ContextMenuTrigger asChild>{trigger}</ContextMenuTrigger>
              <ContextMenuContent className="w-52">
                <ContextMenuItem onClick={() => setLast(`Reveal ${node.id}`)}>
                  <Eye className="mr-2 size-3.5" /> Reveal
                </ContextMenuItem>
                <ContextMenuItem onClick={() => setLast(`Copy ${node.id}`)}>
                  <Copy className="mr-2 size-3.5" /> Copy path
                  <ContextMenuShortcut>⌘C</ContextMenuShortcut>
                </ContextMenuItem>
                <ContextMenuSeparator />
                <ContextMenuItem onClick={() => setLast(`Refresh ${node.id}`)}>
                  <RefreshCw className="mr-2 size-3.5" /> Refresh
                </ContextMenuItem>
                <ContextMenuItem
                  variant="destructive"
                  onClick={() => setLast(`Delete ${node.id}`)}
                >
                  <Trash className="mr-2 size-3.5" /> Delete
                </ContextMenuItem>
              </ContextMenuContent>
            </ContextMenu>
          )}
        />
      </div>
    </div>
  );
};

// ---------------------------------------------------------------------------
// 9) AsyncLazyChildren — load children on expand
// ---------------------------------------------------------------------------

interface RemoteNode {
  name: string;
}
const remote: Record<string, { name: string; children?: string[] }> = {
  'a-root': { name: 'remote', children: ['a/1', 'a/2', 'a/3'] },
  'a/1': { name: 'docs', children: ['a/1/x'] },
  'a/2': { name: 'images', children: ['a/2/x', 'a/2/y'] },
  'a/3': { name: 'manifest.yml' },
  'a/1/x': { name: 'intro.md' },
  'a/2/x': { name: 'logo.png' },
  'a/2/y': { name: 'cover.jpg' },
};
const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));

export const AsyncLazyChildren = () => {
  const data = useMemo<TreeNode<RemoteNode>[]>(
    () => [{ id: 'a-root', data: { name: 'remote' }, isFolder: true }],
    [],
  );

  const loadChildren = async (node: TreeNode<RemoteNode>) => {
    await sleep(350);
    const meta = remote[node.id];
    if (!meta?.children) return [];
    return meta.children.map<TreeNode<RemoteNode>>((id) => ({
      id,
      data: { name: remote[id].name },
      isFolder: !!remote[id].children,
    }));
  };

  return (
    <div className="h-96 w-80 rounded-md border border-border bg-card">
      <TreeRoot<RemoteNode>
        data={data}
        getItemName={(n) => n.data.name}
        loadChildren={loadChildren}
        initialExpandedIds={['a-root']}
      />
    </div>
  );
};

// ---------------------------------------------------------------------------
// 10) ExpandCollapseAll — composition mode + toolbar
// ---------------------------------------------------------------------------

export const ExpandCollapseAll = () => {
  const data = useMemo(() => createDemoTree({ depth: 4, breadth: 3 }), []);
  return (
    <div className="flex h-96 w-80 flex-col gap-2 rounded-md border border-border bg-card p-2">
      <TreeProvider<DemoNode> data={data} getItemName={getName}>
        <Toolbar />
        <div className="min-h-0 flex-1 overflow-auto">
          <TreeContent<DemoNode> />
        </div>
      </TreeProvider>
    </div>
  );
};

function Toolbar() {
  const { expandAll, collapseAll } = useTreeActions();
  return (
    <div className="flex gap-2 text-xs">
      <button
        type="button"
        onClick={() => expandAll()}
        className="rounded border border-border px-2 py-1 hover:bg-accent"
      >
        Expand all
      </button>
      <button
        type="button"
        onClick={() => collapseAll()}
        className="rounded border border-border px-2 py-1 hover:bg-accent"
      >
        Collapse all
      </button>
    </div>
  );
}

// ---------------------------------------------------------------------------
// 11) Persisted — localStorage
// ---------------------------------------------------------------------------

export const Persisted = () => (
  <div className="flex h-96 w-80 flex-col gap-2">
    <p className="text-xs text-muted-foreground">
      Toggle folders, refresh — state restores from localStorage.
    </p>
    <div className="flex-1 rounded-md border border-border bg-card">
      <TreeRoot<FsNode>
        data={fs}
        getItemName={getName}
        persistKey="story.tree.fs"
        persistSelection
      />
    </div>
  </div>
);

// ---------------------------------------------------------------------------
// 12) LargeTree — scalability sanity check (~500 nodes)
// ---------------------------------------------------------------------------

export const LargeTree = () => {
  const data = useMemo(() => createDemoTree({ depth: 4, breadth: 5 }), []);
  return (
    <div className="h-[28rem] w-96 rounded-md border border-border bg-card">
      <TreeRoot<DemoNode>
        data={data}
        getItemName={getName}
        appearance={{ density: 'compact' }}
        showIndentGuides
        enableSearch
      />
    </div>
  );
};

// ---------------------------------------------------------------------------
// 13) Playground — every knob at once
// ---------------------------------------------------------------------------

export const Playground = () => {
  const [selectionMode] = useSelect('selectionMode', {
    options: ['none', 'single', 'multiple'] as const,
    defaultValue: 'single',
    label: 'Selection mode',
  });
  const [density] = useSelect('density', {
    options: ['compact', 'cozy', 'comfortable'] as const,
    defaultValue: 'cozy',
    label: 'Density',
  });
  const [accent] = useSelect('accent', {
    options: ['subtle', 'default', 'strong'] as const,
    defaultValue: 'default',
    label: 'Accent',
  });
  const [radius] = useSelect('radius', {
    options: ['none', 'sm', 'md'] as const,
    defaultValue: 'sm',
    label: 'Radius',
  });
  const [showSearch] = useBoolean('search', { defaultValue: true, label: 'Search' });
  const [typeAhead] = useBoolean('typeAhead', {
    defaultValue: true,
    label: 'Type-ahead',
  });
  const [indentGuides] = useBoolean('indentGuides', {
    defaultValue: true,
    label: 'Indent guides',
  });
  const [activeIndicator] = useBoolean('activeIndicator', {
    defaultValue: true,
    label: 'Active row indicator',
  });
  const [selected, setSelected] = useState<TreeItemId[]>([]);

  return (
    <div className="flex h-[28rem] w-[28rem] flex-col gap-2">
      <div className="flex items-center gap-2 text-xs text-muted-foreground">
        <Settings aria-hidden className="size-3" />
        Selected: {selected.length === 0 ? '(none)' : selected.join(', ')}
      </div>
      <div className="flex-1 rounded-md border border-border bg-card">
        <TreeRoot<FsNode>
          data={fs}
          getItemName={getName}
          initialExpandedIds={['src', 'components', 'hooks', 'public']}
          selectionMode={selectionMode}
          appearance={{ density, accent, radius, showActiveIndicator: activeIndicator }}
          enableSearch={showSearch}
          enableTypeAhead={typeAhead}
          showIndentGuides={indentGuides}
          onSelectionChange={setSelected}
        />
      </div>
    </div>
  );
};
