/**
 * Right-side artifact panel — the WorkBuddy-style split-pane sidebar
 * shown next to the Chat conversation.  Hosts three top-level tabs:
 *
 *   - Changes: side-by-side diff for the focused file only (no
 *     in-panel file list — open a file from the run’s file cards below
 *     the graph, or “View file changes” picks the latest change).
 *   - Preview: rendered preview of whichever file is currently
 *     focused (Markdown → rendered, code → syntax-highlighted).  Shares
 *     `focusedFile` with the changes tab so switching tabs keeps
 *     context.
 *   - Workspace (browser): read-only workspace tree + file preview,
 *     scoped to the current agent's `agent.workspace`.
 *
 * Open/close + tab + focused-file state lives in the
 * `useArtifactPanel` zustand store so any part of the page (file cards,
 * toolbar buttons, "View file changes →" links) can drive it.
 */
import { useLayoutEffect, useMemo, useRef } from 'react';
import { Eye, FileEdit, FolderOpen, FolderTree, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { toast } from 'sonner';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { supportsRichDocumentPreview, type GeneratedFile } from '@/lib/generated-files';
import { invokeIpc } from '@/lib/api-client';
import type { AgentSummary } from '@/types/agent';
import { useArtifactPanel } from '@/stores/artifact-panel';
import type { FilePreviewTarget } from './types';
import { FilePreviewBody } from './FilePreviewBody';
import { WorkspaceBrowserBody } from './WorkspaceBrowserBody';
import { WORKSPACE_BROWSER_ENABLED } from './workspace-browser-config';

export interface ArtifactPanelProps {
  /** All files generated by the AI across the conversation. */
  files: GeneratedFile[];
  /** Currently selected agent (drives the workspace tab). */
  agent: AgentSummary | null;
  /** Used to mark "Added this run" badges on the workspace tree. */
  runStartedAt?: number | null;
  /** Bumping this number triggers a workspace tree reload. */
  refreshSignal?: number;
}

export function ArtifactPanel({ files, agent, runStartedAt, refreshSignal }: ArtifactPanelProps) {
  const { t } = useTranslation('chat');
  const tab = useArtifactPanel((s) => s.tab);
  const setTab = useArtifactPanel((s) => s.setTab);
  const focusedFile = useArtifactPanel((s) => s.focusedFile);
  const setFocusedFile = useArtifactPanel((s) => s.setFocusedFile);
  const close = useArtifactPanel((s) => s.close);
  const richFocusedFile = !!focusedFile && supportsRichDocumentPreview(focusedFile.ext);
  const requestedTab = !WORKSPACE_BROWSER_ENABLED && tab === 'browser' ? 'changes' : tab;
  const visibleTab = richFocusedFile && requestedTab === 'changes' ? 'preview' : requestedTab;

  const handleRevealFocusedFile = () => {
    if (!focusedFile) return;
    invokeIpc('shell:showItemInFolder', focusedFile.filePath).catch(() => {
      toast.error(t('filePreview.errors.openInFinderFailed', 'Could not reveal in file manager'));
    });
  };

  return (
    <div data-testid="artifact-panel" className="flex h-full min-h-0 flex-col bg-background">
      <div className="relative z-30 flex shrink-0 items-center justify-between gap-2 border-b border-black/5 bg-background px-3 py-2 dark:border-white/10">
        <div className="flex min-w-0 items-center gap-1">
          {richFocusedFile ? (
            <PanelTabButton
              testId="artifact-panel-action-open-folder"
              icon={<FolderOpen className="h-3.5 w-3.5" />}
              label={t('generatedFiles.openFolder', 'Open folder')}
              active={false}
              onClick={handleRevealFocusedFile}
            />
          ) : (
            <PanelTabButton
              testId="artifact-panel-tab-changes"
              icon={<FileEdit className="h-3.5 w-3.5" />}
              label={t('artifactPanel.tabs.changes', 'Changes')}
              active={visibleTab === 'changes'}
              onClick={() => setTab('changes')}
            />
          )}
          <PanelTabButton
            testId="artifact-panel-tab-preview"
            icon={<Eye className="h-3.5 w-3.5" />}
            label={t('artifactPanel.tabs.preview', 'Preview')}
            active={visibleTab === 'preview'}
            onClick={() => setTab('preview')}
          />
          {WORKSPACE_BROWSER_ENABLED && (
            <PanelTabButton
              testId="artifact-panel-tab-browser"
              icon={<FolderTree className="h-3.5 w-3.5" />}
              label={t('artifactPanel.tabs.browser', 'Workspace')}
              active={visibleTab === 'browser'}
              onClick={() => setTab('browser')}
            />
          )}
        </div>
        <Button
          type="button"
          variant="ghost"
          size="icon"
          className="h-7 w-7 shrink-0"
          onClick={close}
          aria-label={t('filePreview.actions.close', 'Close')}
        >
          <X className="h-4 w-4 pointer-events-none" />
        </Button>
      </div>

      <div className="relative z-0 min-h-0 flex-1 overflow-hidden">
        <div className={cn('h-full min-h-0', visibleTab !== 'changes' && 'hidden')}>
          <ChangesTab
            files={files}
            focusedFile={focusedFile}
            onFocus={(f) => setFocusedFile(f)}
            active={visibleTab === 'changes'}
          />
        </div>
        <div className={cn('h-full min-h-0', visibleTab !== 'preview' && 'hidden')}>
          <PreviewTab focusedFile={focusedFile} />
        </div>
        {WORKSPACE_BROWSER_ENABLED && (
          <div className={cn('h-full min-h-0', visibleTab !== 'browser' && 'hidden')}>
            <WorkspaceBrowserBody
              agent={agent}
              runStartedAt={runStartedAt}
              refreshSignal={refreshSignal}
              compact
            />
          </div>
        )}
      </div>
    </div>
  );
}

interface PanelTabButtonProps {
  testId?: string;
  icon: React.ReactNode;
  label: string;
  active: boolean;
  onClick: () => void;
}

function PanelTabButton({ testId, icon, label, active, onClick }: PanelTabButtonProps) {
  const pointerActivated = useRef(false);

  return (
    <button
      data-testid={testId}
      type="button"
      onPointerDown={(event) => {
        if (event.button !== 0) return;
        pointerActivated.current = true;
        event.preventDefault();
        onClick();
      }}
      onClick={() => {
        if (pointerActivated.current) {
          pointerActivated.current = false;
          return;
        }
        onClick();
      }}
      className={cn(
        'relative z-40 flex items-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium transition-colors',
        active
          ? 'bg-foreground/10 text-foreground'
          : 'text-muted-foreground hover:bg-black/5 hover:text-foreground dark:hover:bg-white/10',
      )}
    >
      {icon}
      <span>{label}</span>
    </button>
  );
}

function generatedFileToTarget(file: GeneratedFile): FilePreviewTarget {
  return {
    filePath: file.filePath,
    fileName: file.fileName,
    ext: file.ext,
    mimeType: file.mimeType,
    contentType: file.contentType,
    size: file.size,
    action: file.action,
    fullContent: file.fullContent,
    baseline: file.baseline,
    edits: file.edits,
  };
}

interface ChangesTabProps {
  files: GeneratedFile[];
  focusedFile: FilePreviewTarget | null;
  onFocus: (file: FilePreviewTarget) => void;
  active: boolean;
}

/**
 * Full-width diff for the focused file.  Which file is focused comes from
 * the run’s file cards or “View file changes →” (auto-first); switching workspace
 * tabs does not bring back a sidebar list.
 */
function ChangesTab({ files, focusedFile, onFocus, active }: ChangesTabProps) {
  const { t } = useTranslation('chat');

  // De-dup files by path, keep the latest entry (highest lastSeenIndex).
  const uniqueFiles = useMemo(() => {
    const map = new Map<string, GeneratedFile>();
    for (const f of files) {
      const existing = map.get(f.filePath);
      if (!existing || f.lastSeenIndex >= existing.lastSeenIndex) map.set(f.filePath, f);
    }
    return Array.from(map.values()).sort((a, b) => b.lastSeenIndex - a.lastSeenIndex);
  }, [files]);

  // Auto-select the first generated file only when entering this tab without
  // any focus. Files opened from chat cards (for example `SKILL.md`) may not
  // be present in `files`; keep that focus instead of jumping to an unrelated
  // generated file.
  useLayoutEffect(() => {
    if (!active) return;
    if (focusedFile) return;
    if (uniqueFiles.length === 0) return;
    onFocus(generatedFileToTarget(uniqueFiles[0]));
  }, [active, focusedFile, uniqueFiles, onFocus]);

  if (!focusedFile && uniqueFiles.length === 0) {
    return (
      <div className="flex h-full items-center justify-center px-6 text-center text-sm text-muted-foreground">
        {t('artifactPanel.changes.empty', 'No file changes in this session yet')}
      </div>
    );
  }

  return focusedFile ? (
    <div className="flex h-full min-h-0 flex-col">
      <FilePreviewBody key={focusedFile.filePath} file={focusedFile} compact mode="diff" />
    </div>
  ) : (
    <div className="flex h-full items-center justify-center px-6 text-center text-sm text-muted-foreground">
      {t('artifactPanel.changes.selectFileHint', 'Click a file card in the conversation, or choose “View file changes”')}
    </div>
  );
}

interface PreviewTabProps {
  focusedFile: FilePreviewTarget | null;
}

function PreviewTab({ focusedFile }: PreviewTabProps) {
  const { t } = useTranslation('chat');
  if (!focusedFile) {
    return (
      <div className="flex h-full flex-col items-center justify-center gap-2 px-6 text-center">
        <p className="text-sm font-medium text-foreground">
          {t('artifactPanel.preview.emptyTitle', 'No file selected')}
        </p>
        <p className="max-w-md text-xs text-muted-foreground">
          {t(
            'artifactPanel.preview.emptyHint',
            'Click a file card in the conversation to open the sidebar and select a file first.',
          )}
        </p>
      </div>
    );
  }
  return <FilePreviewBody key={focusedFile.filePath} file={focusedFile} compact mode="preview" />;
}

export default ArtifactPanel;
