import { useCoreServices } from '@learnitive/context';
import type { ProjectSemanticSnapshot, SemanticNode } from '@learnitive/project-semantic-index';
import type { ProjectFilePath } from '@learnitive/project-storage';
import { Button } from '@learnitive/ui-components';
import { WsPath } from '@learnitive/ws-path';
import {
  AlertTriangle,
  AtSign,
  BookOpen,
  FileText,
  Hash,
  Link2,
  RefreshCw,
} from 'lucide-react';
import React from 'react';

type NavigatorTab = 'outline' | 'labels' | 'references' | 'citations' | 'bibliography';

function warningFor(snapshot: ProjectSemanticSnapshot, node: SemanticNode): boolean {
  if (!node.target) return false;
  return snapshot.diagnostics.some((diagnostic) => {
    if (diagnostic.file && diagnostic.file !== node.file) return false;
    if (diagnostic.target && diagnostic.target !== node.target) return false;
    if (node.kind === 'reference') return diagnostic.kind === 'missing-reference';
    if (node.kind === 'citation') return diagnostic.kind === 'missing-citation';
    if (node.kind === 'label') return diagnostic.kind === 'duplicate-label';
    return false;
  });
}

export function ProjectOutlinePanel({
  wsName,
  onClose,
  onNavigate,
}: {
  wsName: string;
  onClose: () => void;
  onNavigate?: () => void;
}) {
  const { projectSemantic, latexSource, navigation } = useCoreServices();
  const [snapshot, setSnapshot] = React.useState(() => projectSemantic.getSnapshot(wsName));
  const [loading, setLoading] = React.useState(!snapshot);
  const [tab, setTab] = React.useState<NavigatorTab>('outline');

  const refresh = React.useCallback(() => {
    setLoading(true);
    void projectSemantic.refresh(wsName).finally(() => setLoading(false));
  }, [projectSemantic, wsName]);

  React.useEffect(() => {
    const unsubscribe = projectSemantic.subscribe(wsName, () => {
      setSnapshot(projectSemantic.getSnapshot(wsName));
      setLoading(false);
    });
    refresh();
    return unsubscribe;
  }, [projectSemantic, refresh, wsName]);

  const navigate = (file: ProjectFilePath, line = 1) => {
    const wsPath = WsPath.fromParts(wsName, file).wsPath;
    onNavigate?.();
    latexSource.requestSelect(wsPath, line);
    navigation.goWsPath(wsPath);
  };

  const nodes = snapshot
    ? tab === 'outline'
      ? snapshot.outline
      : tab === 'labels'
        ? snapshot.labels
        : tab === 'references'
          ? snapshot.references
          : tab === 'citations'
            ? snapshot.citations
            : []
    : [];

  return (
    <aside className="flex w-72 shrink-0 flex-col border-r bg-muted/10" data-testid="interactive-project-outline">
      <div className="flex items-center gap-2 border-b px-3 py-2">
        <strong className="flex-1 text-sm">{t.app.projectOutline.navigator}</strong>
        <Button size="icon" variant="ghost" aria-label={t.app.projectOutline.refresh} onClick={refresh}>
          <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />
        </Button>
        <Button size="sm" variant="ghost" onClick={onClose}>{t.app.projectOutline.close}</Button>
      </div>
      <div className="grid grid-cols-5 border-b bg-background" role="tablist" aria-label={t.app.projectOutline.navigator}>
        {([
          ['outline', FileText, t.app.projectOutline.outline],
          ['labels', Hash, t.app.projectOutline.labels],
          ['references', Link2, t.app.projectOutline.references],
          ['citations', AtSign, t.app.projectOutline.citations],
          ['bibliography', BookOpen, t.app.projectOutline.bibliography],
        ] as const).map(([id, Icon, label]) => (
          <button
            key={id}
            type="button"
            role="tab"
            aria-label={label}
            aria-selected={tab === id}
            className={tab === id ? 'flex h-9 items-center justify-center border-b-2 border-primary text-foreground' : 'flex h-9 items-center justify-center text-muted-foreground hover:text-foreground'}
            title={label}
            onClick={() => setTab(id)}
          >
            <Icon className="size-3.5" />
          </button>
        ))}
      </div>
      {snapshot && snapshot.diagnostics.length > 0 && (
        <div className="flex items-center gap-2 border-b px-3 py-2 text-amber-700 text-xs dark:text-amber-300">
          <AlertTriangle className="size-4" />
          {t.app.projectOutline.issues({ count: snapshot.diagnostics.length })}
        </div>
      )}
      <div className="min-h-0 flex-1 overflow-auto p-2">
        {tab === 'bibliography' ? (
          !snapshot?.bibliographyEntries.length ? (
            <p className="px-2 py-3 text-muted-foreground text-xs">{loading ? t.app.projectOutline.loading : t.app.projectOutline.emptyBibliography}</p>
          ) : (
            <div className="space-y-0.5">
              {snapshot.bibliographyEntries.map((entry) => {
                const title = entry.fields.find((field) => field.name === 'title')?.value;
                const unused = snapshot.diagnostics.some((diagnostic) => diagnostic.kind === 'unused-bibliography-entry' && diagnostic.target === entry.key);
                return (
                  <button key={`${entry.file}:${entry.key}:${entry.range.from}`} type="button" className="flex w-full items-start gap-2 rounded px-2 py-1.5 text-left text-sm hover:bg-muted" onClick={() => navigate(entry.file, entry.line)}>
                    <BookOpen className="mt-0.5 size-3.5 shrink-0 text-muted-foreground" />
                    <span className="min-w-0 flex-1">
                      <span className="block truncate">{title || entry.key}</span>
                      <span className="block truncate text-[11px] text-muted-foreground">{entry.key} · {entry.file}:{entry.line}</span>
                    </span>
                    {unused && <AlertTriangle className="mt-0.5 size-3.5 shrink-0 text-amber-600" aria-label={t.app.projectOutline.warning} />}
                  </button>
                );
              })}
            </div>
          )
        ) : !nodes.length ? (
          <p className="px-2 py-3 text-muted-foreground text-xs">{loading ? t.app.projectOutline.loading : tab === 'outline' ? t.app.projectOutline.empty : t.app.projectOutline.emptySection}</p>
        ) : (
          <div className="space-y-0.5">
            {nodes.map((node) => {
              const warning = snapshot ? warningFor(snapshot, node) : false;
              return (
                <button
                  key={node.id}
                  type="button"
                  className="flex w-full items-start gap-2 rounded px-2 py-1.5 text-left text-sm hover:bg-muted"
                  style={tab === 'outline' ? { paddingLeft: `${Math.max(8, (node.level ?? 1) * 10)}px` } : undefined}
                  onClick={() => navigate(node.file, node.line)}
                >
                  <FileText className="mt-0.5 size-3.5 shrink-0 text-muted-foreground" />
                  <span className="min-w-0 flex-1">
                    <span className="block truncate">{node.title || node.target || node.kind}</span>
                    {tab !== 'outline' && <span className="block truncate text-[11px] text-muted-foreground">{node.file}:{node.line}</span>}
                  </span>
                  {warning && <AlertTriangle className="mt-0.5 size-3.5 shrink-0 text-amber-600" aria-label={t.app.projectOutline.warning} />}
                </button>
              );
            })}
          </div>
        )}
      </div>
    </aside>
  );
}
