import { useRef, useState } from 'react';
import {
  Code,
  ChevronDown,
  Edit2,
  MessageSquare,
  MoreHorizontal,
  Settings as SettingsIcon,
  Terminal,
  X,
} from 'lucide-react';
import { toast } from 'sonner';
import type { Chat, WorkspacePane } from '@/api/types';
import { BottomSheet } from '@/components/BottomSheet';
import { StatusDot } from '@/components/StatusDot';
import { ChatThroughput } from '@/components/ChatThroughput';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useLongPress } from '@/hooks/useLongPress';
import { cn } from '@/lib/utils';

interface Props {
  panes: WorkspacePane[];
  activePaneIdx: number;
  chats: Chat[];
  onSwitchPane: (idx: number) => void;
  onRemovePane: (idx: number) => void;
  onRenameChat: (chatId: string, name: string) => Promise<void>;
}

// v1.10.4: swipe-left-to-close on the pane pill. Threshold matches the spec
// (80px). Vertical bail-out at 30px because the pill sits inside a vertical
// scrollable header — diagonal-ish swipes shouldn't accidentally close panes.
const SWIPE_CLOSE_PX = 80;
const SWIPE_VERTICAL_BAIL_PX = 30;
// Visual cap: pill translates left up to this much. Past this, dragX stays
// pinned so the user has a clear "release to close" indicator.
const SWIPE_VISUAL_CAP = 120;

function paneIcon(kind: WorkspacePane['kind']) {
  if (kind === 'terminal') return <Terminal size={14} />;
  if (kind === 'coder') return <Code size={14} />;
  if (kind === 'settings') return <SettingsIcon size={14} />;
  return <MessageSquare size={14} />;
}

function paneActiveChatId(pane: WorkspacePane | undefined): string | null {
  if (!pane) return null;
  if (pane.chatId) return pane.chatId;
  const idx = pane.activeChatIdx;
  if (idx < 0 || idx >= pane.chatIds.length) return null;
  return pane.chatIds[idx] ?? null;
}

function paneLabel(pane: WorkspacePane, chats: Chat[]): string {
  const cid = paneActiveChatId(pane);
  if (cid) {
    const c = chats.find((x) => x.id === cid);
    if (c) return c.name ?? 'New chat';
  }
  if (pane.kind === 'chat') return 'Chat';
  if (pane.kind === 'terminal') return 'Terminal';
  if (pane.kind === 'coder') return 'Coder';
  if (pane.kind === 'settings') return 'Settings';
  return 'Empty';
}

export function MobileTabSwitcher({
  panes,
  activePaneIdx,
  chats,
  onSwitchPane,
  onRemovePane,
  onRenameChat,
}: Props) {
  const [open, setOpen] = useState(false);
  const [renamingChatId, setRenamingChatId] = useState<string | null>(null);
  const [renameValue, setRenameValue] = useState('');
  // v1.10.4: swipe-left state. dragX is the (clamped, negative) drag offset
  // in px. suppressClick latches when a swipe completes so the trailing click
  // doesn't pop open the BottomSheet on the just-closed pane.
  const [dragX, setDragX] = useState(0);
  const swipeStart = useRef<{ x: number; y: number } | null>(null);
  const swipeBailed = useRef(false);
  const suppressClick = useRef(false);

  const active = panes[activePaneIdx];
  const activeLabel = active ? paneLabel(active, chats) : 'Empty';
  const activeChatId = paneActiveChatId(active);

  function onPillTouchStart(e: React.TouchEvent<HTMLDivElement>): void {
    if (e.touches.length !== 1) return;
    const t = e.touches[0]!;
    swipeStart.current = { x: t.clientX, y: t.clientY };
    swipeBailed.current = false;
    setDragX(0);
  }
  function onPillTouchMove(e: React.TouchEvent<HTMLDivElement>): void {
    if (!swipeStart.current || swipeBailed.current) return;
    if (e.touches.length !== 1) return;
    const t = e.touches[0]!;
    const dx = t.clientX - swipeStart.current.x;
    const dy = t.clientY - swipeStart.current.y;
    // Bail to scroll if vertical motion dominates before horizontal.
    if (Math.abs(dy) > SWIPE_VERTICAL_BAIL_PX && Math.abs(dy) > Math.abs(dx)) {
      swipeBailed.current = true;
      setDragX(0);
      return;
    }
    // Only allow leftward drag (negative). Cap visual displacement.
    const clamped = Math.max(-SWIPE_VISUAL_CAP, Math.min(0, dx));
    setDragX(clamped);
  }
  function onPillTouchEnd(): void {
    const finalDx = dragX;
    swipeStart.current = null;
    if (swipeBailed.current) {
      setDragX(0);
      return;
    }
    if (finalDx <= -SWIPE_CLOSE_PX && panes.length > 1) {
      suppressClick.current = true;
      // Reset dragX after the close so subsequent re-renders look right.
      setDragX(0);
      onRemovePane(activePaneIdx);
      return;
    }
    setDragX(0);
  }
  function onPillClick(): void {
    if (suppressClick.current) {
      suppressClick.current = false;
      return;
    }
    setOpen(true);
  }
  const swipeProgress = Math.min(1, Math.abs(dragX) / SWIPE_CLOSE_PX);

  // Long-press mirrors ChatTabBar: synthesize a contextmenu event on the row
  // so the trailing kebab's Radix DropdownMenu opens at the touch point.
  const longPress = useLongPress(({ clientX, clientY, target }) => {
    if (!target || !(target instanceof Element)) return;
    const row = target.closest('[data-pane-id]') as HTMLElement | null;
    if (!row) return;
    const trigger = row.querySelector('[data-pane-kebab]') as HTMLElement | null;
    if (trigger) {
      trigger.click();
      return;
    }
    row.dispatchEvent(
      new MouseEvent('contextmenu', { bubbles: true, cancelable: true, clientX, clientY }),
    );
  });

  function startRename(chatId: string, currentName: string | null) {
    setRenamingChatId(chatId);
    setRenameValue(currentName ?? '');
  }
  async function finishRename() {
    if (renamingChatId && renameValue.trim()) {
      try {
        await onRenameChat(renamingChatId, renameValue.trim());
      } catch (err) {
        toast.error(err instanceof Error ? err.message : 'rename failed');
      }
    }
    setRenamingChatId(null);
  }

  function handleSwitchPane(idx: number) {
    onSwitchPane(idx);
    setOpen(false);
  }

  return (
    <>
      <div
        className="flex-1 relative min-w-0"
        onTouchStart={onPillTouchStart}
        onTouchMove={onPillTouchMove}
        onTouchEnd={onPillTouchEnd}
        onTouchCancel={onPillTouchEnd}
      >
        {/* v1.10.4: red "Close" hint behind the pill. Opacity tracks the
            swipe progress (0 at rest, 1 at the close threshold). aria-hidden
            because the actionable affordance is the swipe, not this label. */}
        <div
          aria-hidden="true"
          className="absolute inset-0 flex items-center justify-end pr-4 rounded-full bg-destructive/80 text-destructive-foreground text-xs font-medium"
          style={{ opacity: swipeProgress, pointerEvents: 'none' }}
        >
          Close
        </div>
        <button
          type="button"
          onClick={onPillClick}
          className="flex-1 w-full inline-flex items-center gap-1.5 min-h-[36px] px-3 text-sm rounded-full bg-muted/40 hover:bg-muted/70 text-foreground min-w-0 relative"
          aria-label="Switch pane"
          style={{
            transform: `translateX(${dragX}px)`,
            transition: dragX === 0 ? 'transform 180ms ease-out' : 'none',
          }}
        >
          <span className="shrink-0 text-muted-foreground">{paneIcon(active?.kind ?? 'chat')}</span>
          <StatusDot chatId={activeChatId} />
          <ChatThroughput chatId={activeChatId} />
          <span className="truncate flex-1 text-left">{activeLabel}</span>
          <ChevronDown size={14} className="opacity-60 shrink-0" />
        </button>
      </div>

      <BottomSheet open={open} onClose={() => setOpen(false)} title="Panes">
        <ul className="px-2 py-2 space-y-1">
          {panes.map((pane, idx) => {
            const isActive = idx === activePaneIdx;
            const cid = paneActiveChatId(pane);
            const chat = cid ? chats.find((c) => c.id === cid) ?? null : null;
            const label = paneLabel(pane, chats);
            return (
              <li
                key={pane.id}
                data-pane-id={pane.id}
                onTouchStart={longPress.onTouchStart}
                onTouchMove={longPress.onTouchMove}
                onTouchEnd={longPress.onTouchEnd}
                onTouchCancel={longPress.onTouchCancel}
                onClick={() => handleSwitchPane(idx)}
                style={{ WebkitTouchCallout: 'none' }}
                className={cn(
                  'flex items-center gap-2 px-3 py-2 rounded min-h-[48px] cursor-default select-none',
                  isActive
                    ? 'bg-accent/40 border-l-2 border-primary'
                    : 'hover:bg-muted/50',
                )}
              >
                <span className="shrink-0 text-muted-foreground">{paneIcon(pane.kind)}</span>
                <StatusDot chatId={cid ?? null} />
                <ChatThroughput chatId={cid ?? null} />
                {renamingChatId === cid && cid ? (
                  <input
                    autoFocus
                    value={renameValue}
                    onChange={(e) => setRenameValue(e.target.value)}
                    onBlur={() => void finishRename()}
                    onKeyDown={(e) => {
                      if (e.key === 'Enter') void finishRename();
                      if (e.key === 'Escape') setRenamingChatId(null);
                    }}
                    onClick={(e) => e.stopPropagation()}
                    className="bg-transparent border-b border-border text-sm outline-none flex-1 min-w-0"
                  />
                ) : (
                  <span className="truncate flex-1 text-sm">{label}</span>
                )}
                {isActive && (
                  <span aria-hidden="true" className="text-primary text-xs shrink-0">
                    ✓
                  </span>
                )}
                <DropdownMenu>
                  <DropdownMenuTrigger asChild>
                    <button
                      type="button"
                      data-pane-kebab
                      onClick={(e) => e.stopPropagation()}
                      className="inline-flex items-center justify-center p-1 rounded text-muted-foreground hover:bg-muted hover:text-foreground min-h-[44px] min-w-[44px]"
                      aria-label="Pane options"
                    >
                      <MoreHorizontal size={14} />
                    </button>
                  </DropdownMenuTrigger>
                  <DropdownMenuContent align="end" className="min-w-44">
                    {chat && (
                      <DropdownMenuItem onSelect={() => startRename(chat.id, chat.name)}>
                        <Edit2 size={14} /> Rename chat
                      </DropdownMenuItem>
                    )}
                    <DropdownMenuItem
                      disabled={panes.length <= 1}
                      onSelect={() => onRemovePane(idx)}
                    >
                      <X size={14} /> Close pane
                    </DropdownMenuItem>
                  </DropdownMenuContent>
                </DropdownMenu>
              </li>
            );
          })}
        </ul>
        {/* v1.8: New-pane button moved out of the sheet to the header row 2
            (see NewPaneMenu). Sheet is for switching only. */}
      </BottomSheet>
    </>
  );
}
