"use client";

import { useEffect, useState, useCallback, useMemo, useRef } from "react";
import {
  DndContext,
  DragEndEvent,
  DragOverlay,
  DragStartEvent,
  PointerSensor,
  useSensor,
  useSensors,
  rectIntersection,
  pointerWithin,
} from "@dnd-kit/core";
import {
  SortableContext,
  verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import {
  useAppStore,
  Feature,
  FeatureImage,
  FeatureImagePath,
  AgentModel,
  ThinkingLevel,
  AIProfile,
  defaultBackgroundSettings,
} from "@/store/app-store";
import { getElectronAPI } from "@/lib/electron";
import { cn, modelSupportsThinking } from "@/lib/utils";
import {
  Card,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { HotkeyButton } from "@/components/ui/hotkey-button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { CategoryAutocomplete } from "@/components/ui/category-autocomplete";
import { FeatureImageUpload } from "@/components/ui/feature-image-upload";
import {
  DescriptionImageDropZone,
  FeatureImagePath as DescriptionImagePath,
  ImagePreviewMap,
} from "@/components/ui/description-image-dropzone";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import { KanbanColumn } from "./kanban-column";
import { KanbanCard } from "./kanban-card";
import { AgentOutputModal } from "./agent-output-modal";
import { FeatureSuggestionsDialog } from "./feature-suggestions-dialog";
import { BoardBackgroundModal } from "@/components/dialogs/board-background-modal";
import {
  Plus,
  RefreshCw,
  Play,
  StopCircle,
  Loader2,
  Users,
  Trash2,
  FastForward,
  FlaskConical,
  CheckCircle2,
  MessageSquare,
  GitCommit,
  Brain,
  Zap,
  Settings2,
  Scale,
  Cpu,
  Rocket,
  Sparkles,
  UserCircle,
  Lightbulb,
  Search,
  X,
  Minimize2,
  Square,
  Maximize2,
  Shuffle,
  ImageIcon,
} from "lucide-react";
import { toast } from "sonner";
import { Slider } from "@/components/ui/slider";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip";
import { Checkbox } from "@/components/ui/checkbox";
import { useAutoMode } from "@/hooks/use-auto-mode";
import {
  useKeyboardShortcuts,
  useKeyboardShortcutsConfig,
  KeyboardShortcut,
} from "@/hooks/use-keyboard-shortcuts";
import { useWindowState } from "@/hooks/use-window-state";

type ColumnId = Feature["status"];

const COLUMNS: { id: ColumnId; title: string; color: string }[] = [
  { id: "backlog", title: "Backlog", color: "bg-zinc-500" },
  { id: "in_progress", title: "In Progress", color: "bg-yellow-500" },
  { id: "waiting_approval", title: "Waiting Approval", color: "bg-orange-500" },
  { id: "verified", title: "Verified", color: "bg-green-500" },
];

type ModelOption = {
  id: AgentModel;
  label: string;
  description: string;
  badge?: string;
  provider: "claude" | "codex";
};

const CLAUDE_MODELS: ModelOption[] = [
  {
    id: "haiku",
    label: "Claude Haiku",
    description: "Fast and efficient for simple tasks.",
    badge: "Speed",
    provider: "claude",
  },
  {
    id: "sonnet",
    label: "Claude Sonnet",
    description: "Balanced performance with strong reasoning.",
    badge: "Balanced",
    provider: "claude",
  },
  {
    id: "opus",
    label: "Claude Opus",
    description: "Most capable model for complex work.",
    badge: "Premium",
    provider: "claude",
  },
];

const CODEX_MODELS: ModelOption[] = [
  {
    id: "gpt-5.1-codex-max",
    label: "GPT-5.1 Codex Max",
    description: "Flagship Codex model tuned for deep coding tasks.",
    badge: "Flagship",
    provider: "codex",
  },
  {
    id: "gpt-5.1-codex",
    label: "GPT-5.1 Codex",
    description: "Strong coding performance with lower cost.",
    badge: "Standard",
    provider: "codex",
  },
  {
    id: "gpt-5.1-codex-mini",
    label: "GPT-5.1 Codex Mini",
    description: "Fastest Codex option for lightweight edits.",
    badge: "Fast",
    provider: "codex",
  },
  {
    id: "gpt-5.1",
    label: "GPT-5.1",
    description: "General-purpose reasoning with solid coding ability.",
    badge: "General",
    provider: "codex",
  },
];

// Profile icon mapping
const PROFILE_ICONS: Record<
  string,
  React.ComponentType<{ className?: string }>
> = {
  Brain,
  Zap,
  Scale,
  Cpu,
  Rocket,
  Sparkles,
};

export function BoardView() {
  const {
    currentProject,
    features,
    setFeatures,
    addFeature,
    updateFeature,
    removeFeature,
    moveFeature,
    maxConcurrency,
    setMaxConcurrency,
    defaultSkipTests,
    useWorktrees,
    showProfilesOnly,
    aiProfiles,
    kanbanCardDetailLevel,
    setKanbanCardDetailLevel,
    boardBackgroundByProject,
  } = useAppStore();
  const shortcuts = useKeyboardShortcutsConfig();
  const [activeFeature, setActiveFeature] = useState<Feature | null>(null);
  const [editingFeature, setEditingFeature] = useState<Feature | null>(null);
  const [showAddDialog, setShowAddDialog] = useState(false);
  const [newFeature, setNewFeature] = useState({
    category: "",
    description: "",
    steps: [""],
    images: [] as FeatureImage[],
    imagePaths: [] as DescriptionImagePath[],
    skipTests: false,
    model: "opus" as AgentModel,
    thinkingLevel: "none" as ThinkingLevel,
  });
  const [isLoading, setIsLoading] = useState(true);
  const [isMounted, setIsMounted] = useState(false);
  const [showOutputModal, setShowOutputModal] = useState(false);
  const [outputFeature, setOutputFeature] = useState<Feature | null>(null);
  const [featuresWithContext, setFeaturesWithContext] = useState<Set<string>>(
    new Set()
  );
  const [showDeleteAllVerifiedDialog, setShowDeleteAllVerifiedDialog] =
    useState(false);
  const [showBoardBackgroundModal, setShowBoardBackgroundModal] =
    useState(false);
  const [persistedCategories, setPersistedCategories] = useState<string[]>([]);
  const [showFollowUpDialog, setShowFollowUpDialog] = useState(false);
  const [followUpFeature, setFollowUpFeature] = useState<Feature | null>(null);
  const [followUpPrompt, setFollowUpPrompt] = useState("");
  const [followUpImagePaths, setFollowUpImagePaths] = useState<
    DescriptionImagePath[]
  >([]);
  // Preview maps to persist image previews across tab switches
  const [newFeaturePreviewMap, setNewFeaturePreviewMap] =
    useState<ImagePreviewMap>(() => new Map());
  const [followUpPreviewMap, setFollowUpPreviewMap] = useState<ImagePreviewMap>(
    () => new Map()
  );
  const [editFeaturePreviewMap, setEditFeaturePreviewMap] =
    useState<ImagePreviewMap>(() => new Map());
  // Local state to temporarily show advanced options when profiles-only mode is enabled
  const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
  const [showEditAdvancedOptions, setShowEditAdvancedOptions] = useState(false);
  const [showSuggestionsDialog, setShowSuggestionsDialog] = useState(false);
  const [suggestionsCount, setSuggestionsCount] = useState(0);
  const [featureSuggestions, setFeatureSuggestions] = useState<
    import("@/lib/electron").FeatureSuggestion[]
  >([]);
  const [isGeneratingSuggestions, setIsGeneratingSuggestions] = useState(false);
  // Search filter for Kanban cards
  const [searchQuery, setSearchQuery] = useState("");
  // Validation state for add feature form
  const [descriptionError, setDescriptionError] = useState(false);

  // Make current project available globally for modal
  useEffect(() => {
    if (currentProject) {
      (window as any).__currentProject = currentProject;
    }
    return () => {
      (window as any).__currentProject = null;
    };
  }, [currentProject]);

  // Listen for suggestions events to update count (persists even when dialog is closed)
  useEffect(() => {
    const api = getElectronAPI();
    if (!api?.suggestions) return;

    const unsubscribe = api.suggestions.onEvent((event) => {
      if (event.type === "suggestions_complete" && event.suggestions) {
        setSuggestionsCount(event.suggestions.length);
        setFeatureSuggestions(event.suggestions);
        setIsGeneratingSuggestions(false);
      } else if (event.type === "suggestions_error") {
        setIsGeneratingSuggestions(false);
      }
    });

    return () => {
      unsubscribe();
    };
  }, []);

  // Track previous project to detect switches
  const prevProjectPathRef = useRef<string | null>(null);
  const isSwitchingProjectRef = useRef<boolean>(false);
  // Track if this is the initial load (to avoid showing loading spinner on subsequent reloads)
  const isInitialLoadRef = useRef<boolean>(true);

  // Auto mode hook
  const autoMode = useAutoMode();
  // Get runningTasks from the hook (scoped to current project)
  const runningAutoTasks = autoMode.runningTasks;

  // Window state hook for compact dialog mode
  const { isMaximized } = useWindowState();

  // Get in-progress features for keyboard shortcuts (memoized for shortcuts)
  const inProgressFeaturesForShortcuts = useMemo(() => {
    return features.filter((f) => {
      const isRunning = runningAutoTasks.includes(f.id);
      return isRunning || f.status === "in_progress";
    });
  }, [features, runningAutoTasks]);

  // Ref to hold the start next callback (to avoid dependency issues)
  const startNextFeaturesRef = useRef<() => void>(() => {});

  // Ref for search input to enable keyboard shortcut focus
  const searchInputRef = useRef<HTMLInputElement>(null);

  // Keyboard shortcuts for this view
  const boardShortcuts: KeyboardShortcut[] = useMemo(() => {
    const shortcutsList: KeyboardShortcut[] = [
      {
        key: shortcuts.addFeature,
        action: () => setShowAddDialog(true),
        description: "Add new feature",
      },
      {
        key: shortcuts.startNext,
        action: () => startNextFeaturesRef.current(),
        description: "Start next features from backlog",
      },
      {
        key: "/",
        action: () => searchInputRef.current?.focus(),
        description: "Focus search input",
      },
    ];

    // Add shortcuts for in-progress cards (1-9 and 0 for 10th)
    inProgressFeaturesForShortcuts.slice(0, 10).forEach((feature, index) => {
      // Keys 1-9 for first 9 cards, 0 for 10th card
      const key = index === 9 ? "0" : String(index + 1);
      shortcutsList.push({
        key,
        action: () => {
          setOutputFeature(feature);
          setShowOutputModal(true);
        },
        description: `View output for in-progress card ${index + 1}`,
      });
    });

    return shortcutsList;
  }, [inProgressFeaturesForShortcuts, shortcuts]);
  useKeyboardShortcuts(boardShortcuts);

  // Prevent hydration issues
  useEffect(() => {
    setIsMounted(true);
  }, []);

  const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: {
        distance: 8,
      },
    })
  );

  // Get unique categories from existing features AND persisted categories for autocomplete suggestions
  const categorySuggestions = useMemo(() => {
    const featureCategories = features.map((f) => f.category).filter(Boolean);
    // Merge feature categories with persisted categories
    const allCategories = [...featureCategories, ...persistedCategories];
    return [...new Set(allCategories)].sort();
  }, [features, persistedCategories]);

  // Custom collision detection that prioritizes columns over cards
  const collisionDetectionStrategy = useCallback((args: any) => {
    // First, check if pointer is within a column
    const pointerCollisions = pointerWithin(args);
    const columnCollisions = pointerCollisions.filter((collision: any) =>
      COLUMNS.some((col) => col.id === collision.id)
    );

    // If we found a column collision, use that
    if (columnCollisions.length > 0) {
      return columnCollisions;
    }

    // Otherwise, use rectangle intersection for cards
    return rectIntersection(args);
  }, []);

  // Load features using features API
  // IMPORTANT: Do NOT add 'features' to dependency array - it would cause infinite reload loop
  const loadFeatures = useCallback(async () => {
    if (!currentProject) return;

    const currentPath = currentProject.path;
    const previousPath = prevProjectPathRef.current;
    const isProjectSwitch =
      previousPath !== null && currentPath !== previousPath;

    // Get cached features from store (without adding to dependencies)
    const cachedFeatures = useAppStore.getState().features;

    // If project switched, mark it but don't clear features yet
    // We'll clear after successful API load to prevent data loss
    if (isProjectSwitch) {
      console.log(
        `[BoardView] Project switch detected: ${previousPath} -> ${currentPath}`
      );
      isSwitchingProjectRef.current = true;
      isInitialLoadRef.current = true;
    }

    // Update the ref to track current project
    prevProjectPathRef.current = currentPath;

    // Only show loading spinner on initial load to prevent board flash during reloads
    if (isInitialLoadRef.current) {
      setIsLoading(true);
    }

    try {
      const api = getElectronAPI();
      if (!api.features) {
        console.error("[BoardView] Features API not available");
        // Keep cached features if API is unavailable
        return;
      }

      const result = await api.features.getAll(currentProject.path);

      if (result.success && result.features) {
        const featuresWithIds = result.features.map(
          (f: any, index: number) => ({
            ...f,
            id: f.id || `feature-${index}-${Date.now()}`,
            status: f.status || "backlog",
            startedAt: f.startedAt, // Preserve startedAt timestamp
            // Ensure model and thinkingLevel are set for backward compatibility
            model: f.model || "opus",
            thinkingLevel: f.thinkingLevel || "none",
          })
        );
        // Successfully loaded features - now safe to set them
        setFeatures(featuresWithIds);

        // Only clear categories on project switch AFTER successful load
        if (isProjectSwitch) {
          setPersistedCategories([]);
        }
      } else if (!result.success && result.error) {
        console.error("[BoardView] API returned error:", result.error);
        // If it's a new project or the error indicates no features found,
        // that's expected - start with empty array
        if (isProjectSwitch) {
          setFeatures([]);
          setPersistedCategories([]);
        }
        // Otherwise keep cached features
      }
    } catch (error) {
      console.error("Failed to load features:", error);
      // On error, keep existing cached features for the current project
      // Only clear on project switch if we have no features from server
      if (isProjectSwitch && cachedFeatures.length === 0) {
        setFeatures([]);
        setPersistedCategories([]);
      }
    } finally {
      setIsLoading(false);
      isInitialLoadRef.current = false;
      isSwitchingProjectRef.current = false;
    }
  }, [currentProject, setFeatures]);

  // Load persisted categories from file
  const loadCategories = useCallback(async () => {
    if (!currentProject) return;

    try {
      const api = getElectronAPI();
      const result = await api.readFile(
        `${currentProject.path}/.automaker/categories.json`
      );

      if (result.success && result.content) {
        const parsed = JSON.parse(result.content);
        if (Array.isArray(parsed)) {
          setPersistedCategories(parsed);
        }
      } else {
        // File doesn't exist, ensure categories are cleared
        setPersistedCategories([]);
      }
    } catch (error) {
      console.error("Failed to load categories:", error);
      // If file doesn't exist, ensure categories are cleared
      setPersistedCategories([]);
    }
  }, [currentProject]);

  // Save a new category to the persisted categories file
  const saveCategory = useCallback(
    async (category: string) => {
      if (!currentProject || !category.trim()) return;

      try {
        const api = getElectronAPI();

        // Read existing categories
        let categories: string[] = [...persistedCategories];

        // Add new category if it doesn't exist
        if (!categories.includes(category)) {
          categories.push(category);
          categories.sort(); // Keep sorted

          // Write back to file
          await api.writeFile(
            `${currentProject.path}/.automaker/categories.json`,
            JSON.stringify(categories, null, 2)
          );

          // Update state
          setPersistedCategories(categories);
        }
      } catch (error) {
        console.error("Failed to save category:", error);
      }
    },
    [currentProject, persistedCategories]
  );

  // Sync skipTests default when dialog opens
  useEffect(() => {
    if (showAddDialog) {
      setNewFeature((prev) => ({
        ...prev,
        skipTests: defaultSkipTests,
      }));
    }
  }, [showAddDialog, defaultSkipTests]);

  // Listen for auto mode feature completion and errors to reload features
  useEffect(() => {
    const api = getElectronAPI();
    if (!api?.autoMode || !currentProject) return;

    const { removeRunningTask } = useAppStore.getState();
    const projectId = currentProject.id;

    const unsubscribe = api.autoMode.onEvent((event) => {
      // Use event's projectPath or projectId if available, otherwise use current project
      // Board view only reacts to events for the currently selected project
      const eventProjectId =
        ("projectId" in event && event.projectId) || projectId;

      if (event.type === "auto_mode_feature_complete") {
        // Reload features when a feature is completed
        console.log("[Board] Feature completed, reloading features...");
        loadFeatures();
        // Play ding sound when feature is done (unless muted)
        const { muteDoneSound } = useAppStore.getState();
        if (!muteDoneSound) {
          const audio = new Audio("/sounds/ding.mp3");
          audio
            .play()
            .catch((err) => console.warn("Could not play ding sound:", err));
        }
      } else if (event.type === "auto_mode_error") {
        // Reload features when an error occurs (feature moved to waiting_approval)
        console.log(
          "[Board] Feature error, reloading features...",
          event.error
        );

        // Remove from running tasks so it moves to the correct column
        if (event.featureId) {
          removeRunningTask(eventProjectId, event.featureId);
        }

        loadFeatures();

        // Check for authentication errors and show a more helpful message
        const isAuthError =
          event.errorType === "authentication" ||
          (event.error &&
            (event.error.includes("Authentication failed") ||
              event.error.includes("Invalid API key")));

        if (isAuthError) {
          toast.error("Authentication Failed", {
            description:
              "Your API key is invalid or expired. Please check Settings or run 'claude login' in terminal.",
            duration: 10000,
          });
        } else {
          toast.error("Agent encountered an error", {
            description: event.error || "Check the logs for details",
          });
        }
      }
    });

    return unsubscribe;
  }, [loadFeatures, currentProject]);

  useEffect(() => {
    loadFeatures();
  }, [loadFeatures]);

  // Load persisted categories on mount
  useEffect(() => {
    loadCategories();
  }, [loadCategories]);

  // Sync running tasks from electron backend on mount
  useEffect(() => {
    if (!currentProject) return;

    const syncRunningTasks = async () => {
      try {
        const api = getElectronAPI();
        if (!api?.autoMode?.status) return;

        const status = await api.autoMode.status(currentProject.path);
        if (status.success) {
          const projectId = currentProject.id;
          const { clearRunningTasks, addRunningTask, setAutoModeRunning } =
            useAppStore.getState();

          // Sync running features if available
          if (status.runningFeatures) {
            console.log(
              "[Board] Syncing running tasks from backend:",
              status.runningFeatures
            );

            // Clear existing running tasks for this project and add the actual running ones
            clearRunningTasks(projectId);

            // Add each running feature to the store
            status.runningFeatures.forEach((featureId: string) => {
              addRunningTask(projectId, featureId);
            });
          }

          // Sync auto mode running state (backend returns autoLoopRunning, mock returns isRunning)
          const isAutoModeRunning =
            status.autoLoopRunning ?? status.isRunning ?? false;
          console.log(
            "[Board] Syncing auto mode running state:",
            isAutoModeRunning
          );
          setAutoModeRunning(projectId, isAutoModeRunning);
        }
      } catch (error) {
        console.error("[Board] Failed to sync running tasks:", error);
      }
    };

    syncRunningTasks();
  }, [currentProject]);

  // Check which features have context files
  useEffect(() => {
    const checkAllContexts = async () => {
      // Check context for in_progress, waiting_approval, and verified features
      const featuresWithPotentialContext = features.filter(
        (f) =>
          f.status === "in_progress" ||
          f.status === "waiting_approval" ||
          f.status === "verified"
      );
      const contextChecks = await Promise.all(
        featuresWithPotentialContext.map(async (f) => ({
          id: f.id,
          hasContext: await checkContextExists(f.id),
        }))
      );

      const newSet = new Set<string>();
      contextChecks.forEach(({ id, hasContext }) => {
        if (hasContext) {
          newSet.add(id);
        }
      });

      setFeaturesWithContext(newSet);
    };

    if (features.length > 0 && !isLoading) {
      checkAllContexts();
    }
  }, [features, isLoading]);

  // Persist feature update to API (replaces saveFeatures)
  const persistFeatureUpdate = useCallback(
    async (featureId: string, updates: Partial<Feature>) => {
      if (!currentProject) return;

      try {
        const api = getElectronAPI();
        if (!api.features) {
          console.error("[BoardView] Features API not available");
          return;
        }

        const result = await api.features.update(
          currentProject.path,
          featureId,
          updates
        );
        if (result.success && result.feature) {
          updateFeature(result.feature.id, result.feature);
        }
      } catch (error) {
        console.error("Failed to persist feature update:", error);
      }
    },
    [currentProject, updateFeature]
  );

  // Persist feature creation to API
  const persistFeatureCreate = useCallback(
    async (feature: Feature) => {
      if (!currentProject) return;

      try {
        const api = getElectronAPI();
        if (!api.features) {
          console.error("[BoardView] Features API not available");
          return;
        }

        const result = await api.features.create(currentProject.path, feature);
        if (result.success && result.feature) {
          updateFeature(result.feature.id, result.feature);
        }
      } catch (error) {
        console.error("Failed to persist feature creation:", error);
      }
    },
    [currentProject, updateFeature]
  );

  // Persist feature deletion to API
  const persistFeatureDelete = useCallback(
    async (featureId: string) => {
      if (!currentProject) return;

      try {
        const api = getElectronAPI();
        if (!api.features) {
          console.error("[BoardView] Features API not available");
          return;
        }

        await api.features.delete(currentProject.path, featureId);
      } catch (error) {
        console.error("Failed to persist feature deletion:", error);
      }
    },
    [currentProject]
  );

  const handleDragStart = (event: DragStartEvent) => {
    const { active } = event;
    const feature = features.find((f) => f.id === active.id);
    if (feature) {
      setActiveFeature(feature);
    }
  };

  const handleDragEnd = async (event: DragEndEvent) => {
    const { active, over } = event;
    setActiveFeature(null);

    if (!over) return;

    const featureId = active.id as string;
    const overId = over.id as string;

    // Find the feature being dragged
    const draggedFeature = features.find((f) => f.id === featureId);
    if (!draggedFeature) return;

    // Check if this is a running task (non-skipTests, TDD)
    const isRunningTask = runningAutoTasks.includes(featureId);

    // Determine if dragging is allowed based on status and skipTests
    // - Backlog items can always be dragged
    // - waiting_approval items can always be dragged (to allow manual verification via drag)
    // - verified items can always be dragged (to allow moving back to waiting_approval)
    // - skipTests (non-TDD) items can be dragged between in_progress and verified
    // - Non-skipTests (TDD) items that are in progress cannot be dragged (they are running)
    if (
      draggedFeature.status !== "backlog" &&
      draggedFeature.status !== "waiting_approval" &&
      draggedFeature.status !== "verified"
    ) {
      // Only allow dragging in_progress if it's a skipTests feature and not currently running
      if (!draggedFeature.skipTests || isRunningTask) {
        console.log(
          "[Board] Cannot drag feature - TDD feature or currently running"
        );
        return;
      }
    }

    let targetStatus: ColumnId | null = null;

    // Check if we dropped on a column
    const column = COLUMNS.find((c) => c.id === overId);
    if (column) {
      targetStatus = column.id;
    } else {
      // Dropped on another feature - find its column
      const overFeature = features.find((f) => f.id === overId);
      if (overFeature) {
        targetStatus = overFeature.status;
      }
    }

    if (!targetStatus) return;

    // Same column, nothing to do
    if (targetStatus === draggedFeature.status) return;

    // Check concurrency limit before moving to in_progress (only for backlog -> in_progress and if running agent)
    if (
      targetStatus === "in_progress" &&
      draggedFeature.status === "backlog" &&
      !autoMode.canStartNewTask
    ) {
      console.log("[Board] Cannot start new task - at max concurrency limit");
      toast.error("Concurrency limit reached", {
        description: `You can only have ${autoMode.maxConcurrency} task${
          autoMode.maxConcurrency > 1 ? "s" : ""
        } running at a time. Wait for a task to complete or increase the limit.`,
      });
      return;
    }

    // Handle different drag scenarios
    if (draggedFeature.status === "backlog") {
      // From backlog
      if (targetStatus === "in_progress") {
        // Update with startedAt timestamp
        const updates = {
          status: targetStatus,
          startedAt: new Date().toISOString(),
        };
        updateFeature(featureId, updates);
        persistFeatureUpdate(featureId, updates);
        console.log("[Board] Feature moved to in_progress, starting agent...");
        await handleRunFeature(draggedFeature);
      } else {
        moveFeature(featureId, targetStatus);
        persistFeatureUpdate(featureId, { status: targetStatus });
      }
    } else if (draggedFeature.status === "waiting_approval") {
      // waiting_approval features can be dragged to verified for manual verification
      // NOTE: This check must come BEFORE skipTests check because waiting_approval
      // features often have skipTests=true, and we want status-based handling first
      if (targetStatus === "verified") {
        moveFeature(featureId, "verified");
        // Clear justFinishedAt timestamp when manually verifying via drag
        persistFeatureUpdate(featureId, {
          status: "verified",
          justFinishedAt: undefined,
        });
        toast.success("Feature verified", {
          description: `Manually verified: ${draggedFeature.description.slice(
            0,
            50
          )}${draggedFeature.description.length > 50 ? "..." : ""}`,
        });
      } else if (targetStatus === "backlog") {
        // Allow moving waiting_approval cards back to backlog
        moveFeature(featureId, "backlog");
        // Clear justFinishedAt timestamp when moving back to backlog
        persistFeatureUpdate(featureId, {
          status: "backlog",
          justFinishedAt: undefined,
        });
        toast.info("Feature moved to backlog", {
          description: `Moved to Backlog: ${draggedFeature.description.slice(
            0,
            50
          )}${draggedFeature.description.length > 50 ? "..." : ""}`,
        });
      }
    } else if (draggedFeature.skipTests) {
      // skipTests feature being moved between in_progress and verified
      if (
        targetStatus === "verified" &&
        draggedFeature.status === "in_progress"
      ) {
        // Manual verify via drag
        moveFeature(featureId, "verified");
        persistFeatureUpdate(featureId, { status: "verified" });
        toast.success("Feature verified", {
          description: `Marked as verified: ${draggedFeature.description.slice(
            0,
            50
          )}${draggedFeature.description.length > 50 ? "..." : ""}`,
        });
      } else if (
        targetStatus === "waiting_approval" &&
        draggedFeature.status === "verified"
      ) {
        // Move verified feature back to waiting_approval
        moveFeature(featureId, "waiting_approval");
        persistFeatureUpdate(featureId, { status: "waiting_approval" });
        toast.info("Feature moved back", {
          description: `Moved back to Waiting Approval: ${draggedFeature.description.slice(
            0,
            50
          )}${draggedFeature.description.length > 50 ? "..." : ""}`,
        });
      } else if (targetStatus === "backlog") {
        // Allow moving skipTests cards back to backlog
        moveFeature(featureId, "backlog");
        persistFeatureUpdate(featureId, { status: "backlog" });
        toast.info("Feature moved to backlog", {
          description: `Moved to Backlog: ${draggedFeature.description.slice(
            0,
            50
          )}${draggedFeature.description.length > 50 ? "..." : ""}`,
        });
      }
    } else if (draggedFeature.status === "verified") {
      // Handle verified TDD (non-skipTests) features being moved back
      if (targetStatus === "waiting_approval") {
        // Move verified feature back to waiting_approval
        moveFeature(featureId, "waiting_approval");
        persistFeatureUpdate(featureId, { status: "waiting_approval" });
        toast.info("Feature moved back", {
          description: `Moved back to Waiting Approval: ${draggedFeature.description.slice(
            0,
            50
          )}${draggedFeature.description.length > 50 ? "..." : ""}`,
        });
      } else if (targetStatus === "backlog") {
        // Allow moving verified cards back to backlog
        moveFeature(featureId, "backlog");
        persistFeatureUpdate(featureId, { status: "backlog" });
        toast.info("Feature moved to backlog", {
          description: `Moved to Backlog: ${draggedFeature.description.slice(
            0,
            50
          )}${draggedFeature.description.length > 50 ? "..." : ""}`,
        });
      }
    }
  };

  const handleAddFeature = () => {
    // Validate description is required
    if (!newFeature.description.trim()) {
      setDescriptionError(true);
      return;
    }
    const category = newFeature.category || "Uncategorized";
    const selectedModel = newFeature.model;
    const normalizedThinking = modelSupportsThinking(selectedModel)
      ? newFeature.thinkingLevel
      : "none";
    const newFeatureData = {
      category,
      description: newFeature.description,
      steps: newFeature.steps.filter((s) => s.trim()),
      status: "backlog" as const,
      images: newFeature.images,
      imagePaths: newFeature.imagePaths,
      skipTests: newFeature.skipTests,
      model: selectedModel,
      thinkingLevel: normalizedThinking,
    };
    const createdFeature = addFeature(newFeatureData);
    persistFeatureCreate(createdFeature);
    // Persist the category
    saveCategory(category);
    setNewFeature({
      category: "",
      description: "",
      steps: [""],
      images: [],
      imagePaths: [],
      skipTests: defaultSkipTests,
      model: "opus",
      thinkingLevel: "none",
    });
    // Clear the preview map when the feature is added
    setNewFeaturePreviewMap(new Map());
    setShowAddDialog(false);
  };

  const handleUpdateFeature = () => {
    if (!editingFeature) return;

    const selectedModel = (editingFeature.model ?? "opus") as AgentModel;
    const normalizedThinking = modelSupportsThinking(selectedModel)
      ? editingFeature.thinkingLevel
      : "none";

    const updates = {
      category: editingFeature.category,
      description: editingFeature.description,
      steps: editingFeature.steps,
      skipTests: editingFeature.skipTests,
      model: selectedModel,
      thinkingLevel: normalizedThinking,
      imagePaths: editingFeature.imagePaths,
    };
    updateFeature(editingFeature.id, updates);
    persistFeatureUpdate(editingFeature.id, updates);
    // Clear the preview map after saving
    setEditFeaturePreviewMap(new Map());
    // Persist the category if it's new
    if (editingFeature.category) {
      saveCategory(editingFeature.category);
    }
    setEditingFeature(null);
  };

  const handleDeleteFeature = async (featureId: string) => {
    const feature = features.find((f) => f.id === featureId);
    if (!feature) return;

    // Check if the feature is currently running
    const isRunning = runningAutoTasks.includes(featureId);

    // If the feature is running, stop the agent first
    if (isRunning) {
      try {
        await autoMode.stopFeature(featureId);
        toast.success("Agent stopped", {
          description: `Stopped and deleted: ${feature.description.slice(
            0,
            50
          )}${feature.description.length > 50 ? "..." : ""}`,
        });
      } catch (error) {
        console.error("[Board] Error stopping feature before delete:", error);
        toast.error("Failed to stop agent", {
          description: "The feature will still be deleted.",
        });
      }
    }

    // Note: Agent context file will be deleted automatically when feature folder is deleted
    // via persistFeatureDelete, so no manual deletion needed
    if (currentProject) {
      try {
        // Feature folder deletion handles agent-output.md automatically
        console.log(
          `[Board] Feature ${featureId} will be deleted (including agent context)`
        );
      } catch (error) {
        // Context file might not exist, which is fine
        console.log(
          `[Board] Context file not found or already deleted for feature ${featureId}`
        );
      }
    }

    // Delete attached images if they exist
    if (feature.imagePaths && feature.imagePaths.length > 0) {
      try {
        const api = getElectronAPI();
        for (const imagePathObj of feature.imagePaths) {
          try {
            await api.deleteFile(imagePathObj.path);
            console.log(`[Board] Deleted image: ${imagePathObj.path}`);
          } catch (error) {
            console.error(
              `[Board] Failed to delete image ${imagePathObj.path}:`,
              error
            );
          }
        }
      } catch (error) {
        console.error(
          `[Board] Error deleting images for feature ${featureId}:`,
          error
        );
      }
    }

    // Remove the feature immediately without confirmation
    removeFeature(featureId);
    persistFeatureDelete(featureId);
  };

  const handleRunFeature = async (feature: Feature) => {
    if (!currentProject) return;

    try {
      const api = getElectronAPI();
      if (!api?.autoMode) {
        console.error("Auto mode API not available");
        return;
      }

      // Call the API to run this specific feature by ID
      const result = await api.autoMode.runFeature(
        currentProject.path,
        feature.id,
        useWorktrees
      );

      if (result.success) {
        console.log("[Board] Feature run started successfully");
        // The feature status will be updated by the auto mode service
        // and the UI will reload features when the agent completes (via event listener)
      } else {
        console.error("[Board] Failed to run feature:", result.error);
        // Reload to revert the UI status change
        await loadFeatures();
      }
    } catch (error) {
      console.error("[Board] Error running feature:", error);
      // Reload to revert the UI status change
      await loadFeatures();
    }
  };

  const handleVerifyFeature = async (feature: Feature) => {
    if (!currentProject) return;

    console.log("[Board] Verifying feature:", {
      id: feature.id,
      description: feature.description,
    });

    try {
      const api = getElectronAPI();
      if (!api?.autoMode) {
        console.error("Auto mode API not available");
        return;
      }

      // Call the API to verify this specific feature by ID
      const result = await api.autoMode.verifyFeature(
        currentProject.path,
        feature.id
      );

      if (result.success) {
        console.log("[Board] Feature verification started successfully");
        // The feature status will be updated by the auto mode service
        // and the UI will reload features when verification completes
      } else {
        console.error("[Board] Failed to verify feature:", result.error);
        await loadFeatures();
      }
    } catch (error) {
      console.error("[Board] Error verifying feature:", error);
      await loadFeatures();
    }
  };

  const handleResumeFeature = async (feature: Feature) => {
    if (!currentProject) return;

    console.log("[Board] Resuming feature:", {
      id: feature.id,
      description: feature.description,
    });

    try {
      const api = getElectronAPI();
      if (!api?.autoMode) {
        console.error("Auto mode API not available");
        return;
      }

      // Call the API to resume this specific feature by ID with context
      const result = await api.autoMode.resumeFeature(
        currentProject.path,
        feature.id
      );

      if (result.success) {
        console.log("[Board] Feature resume started successfully");
        // The feature status will be updated by the auto mode service
        // and the UI will reload features when resume completes
      } else {
        console.error("[Board] Failed to resume feature:", result.error);
        await loadFeatures();
      }
    } catch (error) {
      console.error("[Board] Error resuming feature:", error);
      await loadFeatures();
    }
  };

  // Manual verification handler for skipTests features
  const handleManualVerify = (feature: Feature) => {
    console.log("[Board] Manually verifying feature:", {
      id: feature.id,
      description: feature.description,
    });
    moveFeature(feature.id, "verified");
    // Clear justFinishedAt timestamp when manually verifying
    persistFeatureUpdate(feature.id, {
      status: "verified",
      justFinishedAt: undefined,
    });
    toast.success("Feature verified", {
      description: `Marked as verified: ${feature.description.slice(0, 50)}${
        feature.description.length > 50 ? "..." : ""
      }`,
    });
  };

  // Move feature back to in_progress from verified (for skipTests features)
  const handleMoveBackToInProgress = (feature: Feature) => {
    console.log("[Board] Moving feature back to in_progress:", {
      id: feature.id,
      description: feature.description,
    });
    const updates = {
      status: "in_progress" as const,
      startedAt: new Date().toISOString(),
    };
    updateFeature(feature.id, updates);
    persistFeatureUpdate(feature.id, updates);
    toast.info("Feature moved back", {
      description: `Moved back to In Progress: ${feature.description.slice(
        0,
        50
      )}${feature.description.length > 50 ? "..." : ""}`,
    });
  };

  // Open follow-up dialog for waiting_approval features
  const handleOpenFollowUp = (feature: Feature) => {
    console.log("[Board] Opening follow-up dialog for feature:", {
      id: feature.id,
      description: feature.description,
    });
    setFollowUpFeature(feature);
    setFollowUpPrompt("");
    setFollowUpImagePaths([]);
    setShowFollowUpDialog(true);
  };

  // Handle sending follow-up prompt
  const handleSendFollowUp = async () => {
    if (!currentProject || !followUpFeature || !followUpPrompt.trim()) return;

    // Save values before clearing state
    const featureId = followUpFeature.id;
    const featureDescription = followUpFeature.description;
    const prompt = followUpPrompt;
    const imagePaths = followUpImagePaths.map((img) => img.path);

    console.log("[Board] Sending follow-up prompt for feature:", {
      id: featureId,
      prompt: prompt,
      imagePaths: imagePaths,
    });

    const api = getElectronAPI();
    if (!api?.autoMode?.followUpFeature) {
      console.error("Follow-up feature API not available");
      toast.error("Follow-up not available", {
        description: "This feature is not available in the current version.",
      });
      return;
    }

    // Move feature back to in_progress before sending follow-up
    // Clear justFinishedAt timestamp since user is now interacting with it
    const updates = {
      status: "in_progress" as const,
      startedAt: new Date().toISOString(),
      justFinishedAt: undefined,
    };
    updateFeature(featureId, updates);
    persistFeatureUpdate(featureId, updates);

    // Reset follow-up state immediately (close dialog, clear form)
    setShowFollowUpDialog(false);
    setFollowUpFeature(null);
    setFollowUpPrompt("");
    setFollowUpImagePaths([]);
    setFollowUpPreviewMap(new Map());

    // Show success toast immediately
    toast.success("Follow-up started", {
      description: `Continuing work on: ${featureDescription.slice(0, 50)}${
        featureDescription.length > 50 ? "..." : ""
      }`,
    });

    // Call the API in the background (don't await - let it run async)
    api.autoMode
      .followUpFeature(currentProject.path, featureId, prompt, imagePaths)
      .catch((error) => {
        console.error("[Board] Error sending follow-up:", error);
        toast.error("Failed to send follow-up", {
          description:
            error instanceof Error ? error.message : "An error occurred",
        });
        // Reload features to revert status if there was an error
        loadFeatures();
      });
  };

  // Handle commit-only for waiting_approval features (marks as verified and commits)
  const handleCommitFeature = async (feature: Feature) => {
    if (!currentProject) return;

    console.log("[Board] Committing feature:", {
      id: feature.id,
      description: feature.description,
    });

    try {
      const api = getElectronAPI();
      if (!api?.autoMode?.commitFeature) {
        console.error("Commit feature API not available");
        toast.error("Commit not available", {
          description: "This feature is not available in the current version.",
        });
        return;
      }

      // Call the API to commit this feature
      const result = await api.autoMode.commitFeature(
        currentProject.path,
        feature.id
      );

      if (result.success) {
        console.log("[Board] Feature committed successfully");
        // Move to verified status
        moveFeature(feature.id, "verified");
        persistFeatureUpdate(feature.id, { status: "verified" });
        toast.success("Feature committed", {
          description: `Committed and verified: ${feature.description.slice(
            0,
            50
          )}${feature.description.length > 50 ? "..." : ""}`,
        });
      } else {
        console.error("[Board] Failed to commit feature:", result.error);
        toast.error("Failed to commit feature", {
          description: result.error || "An error occurred",
        });
        await loadFeatures();
      }
    } catch (error) {
      console.error("[Board] Error committing feature:", error);
      toast.error("Failed to commit feature", {
        description:
          error instanceof Error ? error.message : "An error occurred",
      });
      await loadFeatures();
    }
  };

  // Move feature to waiting_approval (for skipTests features when agent completes)
  const handleMoveToWaitingApproval = (feature: Feature) => {
    console.log("[Board] Moving feature to waiting_approval:", {
      id: feature.id,
      description: feature.description,
    });
    const updates = { status: "waiting_approval" as const };
    updateFeature(feature.id, updates);
    persistFeatureUpdate(feature.id, updates);
    toast.info("Feature ready for review", {
      description: `Ready for approval: ${feature.description.slice(0, 50)}${
        feature.description.length > 50 ? "..." : ""
      }`,
    });
  };

  // Revert feature changes by removing the worktree
  const handleRevertFeature = async (feature: Feature) => {
    if (!currentProject) return;

    console.log("[Board] Reverting feature:", {
      id: feature.id,
      description: feature.description,
      branchName: feature.branchName,
    });

    try {
      const api = getElectronAPI();
      if (!api?.worktree?.revertFeature) {
        console.error("Worktree API not available");
        toast.error("Revert not available", {
          description: "This feature is not available in the current version.",
        });
        return;
      }

      const result = await api.worktree.revertFeature(
        currentProject.path,
        feature.id
      );

      if (result.success) {
        console.log("[Board] Feature reverted successfully");
        // Reload features to update the UI
        await loadFeatures();
        toast.success("Feature reverted", {
          description: `All changes discarded. Moved back to backlog: ${feature.description.slice(
            0,
            50
          )}${feature.description.length > 50 ? "..." : ""}`,
        });
      } else {
        console.error("[Board] Failed to revert feature:", result.error);
        toast.error("Failed to revert feature", {
          description: result.error || "An error occurred",
        });
      }
    } catch (error) {
      console.error("[Board] Error reverting feature:", error);
      toast.error("Failed to revert feature", {
        description:
          error instanceof Error ? error.message : "An error occurred",
      });
    }
  };

  // Merge feature worktree changes back to main branch
  const handleMergeFeature = async (feature: Feature) => {
    if (!currentProject) return;

    console.log("[Board] Merging feature:", {
      id: feature.id,
      description: feature.description,
      branchName: feature.branchName,
    });

    try {
      const api = getElectronAPI();
      if (!api?.worktree?.mergeFeature) {
        console.error("Worktree API not available");
        toast.error("Merge not available", {
          description: "This feature is not available in the current version.",
        });
        return;
      }

      const result = await api.worktree.mergeFeature(
        currentProject.path,
        feature.id
      );

      if (result.success) {
        console.log("[Board] Feature merged successfully");
        // Reload features to update the UI
        await loadFeatures();
        toast.success("Feature merged", {
          description: `Changes merged to main branch: ${feature.description.slice(
            0,
            50
          )}${feature.description.length > 50 ? "..." : ""}`,
        });
      } else {
        console.error("[Board] Failed to merge feature:", result.error);
        toast.error("Failed to merge feature", {
          description: result.error || "An error occurred",
        });
      }
    } catch (error) {
      console.error("[Board] Error merging feature:", error);
      toast.error("Failed to merge feature", {
        description:
          error instanceof Error ? error.message : "An error occurred",
      });
    }
  };

  const checkContextExists = async (featureId: string): Promise<boolean> => {
    if (!currentProject) return false;

    try {
      const api = getElectronAPI();
      if (!api?.autoMode?.contextExists) {
        return false;
      }

      const result = await api.autoMode.contextExists(
        currentProject.path,
        featureId
      );

      return result.success && result.exists === true;
    } catch (error) {
      console.error("[Board] Error checking context:", error);
      return false;
    }
  };

  // Memoize column features to prevent unnecessary re-renders
  const columnFeaturesMap = useMemo(() => {
    const map: Record<ColumnId, Feature[]> = {
      backlog: [],
      in_progress: [],
      waiting_approval: [],
      verified: [],
    };

    // Filter features by search query (case-insensitive)
    const normalizedQuery = searchQuery.toLowerCase().trim();
    const filteredFeatures = normalizedQuery
      ? features.filter(
          (f) =>
            f.description.toLowerCase().includes(normalizedQuery) ||
            f.category.toLowerCase().includes(normalizedQuery)
        )
      : features;

    filteredFeatures.forEach((f) => {
      // If feature has a running agent, always show it in "in_progress"
      const isRunning = runningAutoTasks.includes(f.id);
      if (isRunning) {
        map.in_progress.push(f);
      } else {
        // Otherwise, use the feature's status (fallback to backlog for unknown statuses)
        const status = f.status as ColumnId;
        if (map[status]) {
          map[status].push(f);
        } else {
          // Unknown status, default to backlog
          map.backlog.push(f);
        }
      }
    });

    return map;
  }, [features, runningAutoTasks, searchQuery]);

  const getColumnFeatures = useCallback(
    (columnId: ColumnId) => {
      return columnFeaturesMap[columnId];
    },
    [columnFeaturesMap]
  );

  const handleViewOutput = (feature: Feature) => {
    setOutputFeature(feature);
    setShowOutputModal(true);
  };

  // Handle number key press when output modal is open
  const handleOutputModalNumberKeyPress = useCallback(
    (key: string) => {
      // Convert key to index: 1-9 -> 0-8, 0 -> 9
      const index = key === "0" ? 9 : parseInt(key, 10) - 1;

      // Get the feature at that index from in-progress features
      const targetFeature = inProgressFeaturesForShortcuts[index];

      if (!targetFeature) {
        // No feature at this index, do nothing
        return;
      }

      // If pressing the same number key as the currently open feature, close the modal
      if (targetFeature.id === outputFeature?.id) {
        setShowOutputModal(false);
      }
      // If pressing a different number key, switch to that feature's output
      else {
        setOutputFeature(targetFeature);
        // Modal stays open, just showing different content
      }
    },
    [inProgressFeaturesForShortcuts, outputFeature?.id]
  );

  const handleForceStopFeature = async (feature: Feature) => {
    try {
      await autoMode.stopFeature(feature.id);

      // Determine where to move the feature after stopping:
      // - If it's a skipTests feature that was in waiting_approval (i.e., during commit operation),
      //   move it back to waiting_approval so user can try commit again or do follow-up
      // - Otherwise, move to backlog
      const targetStatus =
        feature.skipTests && feature.status === "waiting_approval"
          ? "waiting_approval"
          : "backlog";

      if (targetStatus !== feature.status) {
        moveFeature(feature.id, targetStatus);
        persistFeatureUpdate(feature.id, { status: targetStatus });
      }

      toast.success("Agent stopped", {
        description:
          targetStatus === "waiting_approval"
            ? `Stopped commit - returned to waiting approval: ${feature.description.slice(
                0,
                50
              )}${feature.description.length > 50 ? "..." : ""}`
            : `Stopped working on: ${feature.description.slice(0, 50)}${
                feature.description.length > 50 ? "..." : ""
              }`,
      });
    } catch (error) {
      console.error("[Board] Error stopping feature:", error);
      toast.error("Failed to stop agent", {
        description:
          error instanceof Error ? error.message : "An error occurred",
      });
    }
  };

  // Start next features from backlog up to the concurrency limit
  const handleStartNextFeatures = useCallback(async () => {
    const backlogFeatures = features.filter((f) => f.status === "backlog");
    const availableSlots = maxConcurrency - runningAutoTasks.length;

    if (availableSlots <= 0) {
      toast.error("Concurrency limit reached", {
        description: `You can only have ${maxConcurrency} task${
          maxConcurrency > 1 ? "s" : ""
        } running at a time. Wait for a task to complete or increase the limit.`,
      });
      return;
    }

    if (backlogFeatures.length === 0) {
      toast.info("No features in backlog", {
        description: "Add features to the backlog first.",
      });
      return;
    }

    const featuresToStart = backlogFeatures.slice(0, 1);

    for (const feature of featuresToStart) {
      // Update the feature status with startedAt timestamp
      const updates = {
        status: "in_progress" as const,
        startedAt: new Date().toISOString(),
      };
      updateFeature(feature.id, updates);
      persistFeatureUpdate(feature.id, updates);
      // Start the agent for this feature
      await handleRunFeature(feature);
    }

    toast.success(
      `Started ${featuresToStart.length} feature${
        featuresToStart.length > 1 ? "s" : ""
      }`,
      {
        description: featuresToStart
          .map(
            (f) =>
              f.description.slice(0, 30) +
              (f.description.length > 30 ? "..." : "")
          )
          .join(", "),
      }
    );
  }, [features, maxConcurrency, runningAutoTasks.length, updateFeature]);

  // Update ref when handleStartNextFeatures changes
  useEffect(() => {
    startNextFeaturesRef.current = handleStartNextFeatures;
  }, [handleStartNextFeatures]);

  const renderModelOptions = (
    options: ModelOption[],
    selectedModel: AgentModel,
    onSelect: (model: AgentModel) => void,
    testIdPrefix = "model-select"
  ) => (
    <div className="flex gap-2 flex-wrap">
      {options.map((option) => {
        const isSelected = selectedModel === option.id;
        const isCodex = option.provider === "codex";
        // Shorter display names for compact view
        const shortName = option.label
          .replace("Claude ", "")
          .replace("GPT-5.1 Codex ", "")
          .replace("GPT-5.1 ", "");
        return (
          <button
            key={option.id}
            type="button"
            onClick={() => onSelect(option.id)}
            title={option.description}
            className={cn(
              "flex-1 min-w-[80px] px-3 py-2 rounded-md border text-sm font-medium transition-colors",
              isSelected
                ? isCodex
                  ? "bg-emerald-600 text-white border-emerald-500"
                  : "bg-primary text-primary-foreground border-primary"
                : "bg-background hover:bg-accent border-input"
            )}
            data-testid={`${testIdPrefix}-${option.id}`}
          >
            {shortName}
          </button>
        );
      })}
    </div>
  );

  const newModelAllowsThinking = modelSupportsThinking(newFeature.model);
  const editModelAllowsThinking = modelSupportsThinking(editingFeature?.model);

  if (!currentProject) {
    return (
      <div
        className="flex-1 flex items-center justify-center"
        data-testid="board-view-no-project"
      >
        <p className="text-muted-foreground">No project selected</p>
      </div>
    );
  }

  if (isLoading) {
    return (
      <div
        className="flex-1 flex items-center justify-center"
        data-testid="board-view-loading"
      >
        <RefreshCw className="w-6 h-6 animate-spin text-muted-foreground" />
      </div>
    );
  }

  return (
    <div
      className="flex-1 flex flex-col overflow-hidden content-bg relative"
      data-testid="board-view"
    >
      {/* Header */}
      <div className="flex items-center justify-between p-4 border-b border-border bg-glass backdrop-blur-md">
        <div>
          <h1 className="text-xl font-bold">Kanban Board</h1>
          <p className="text-sm text-muted-foreground">{currentProject.name}</p>
        </div>
        <div className="flex gap-2 items-center">
          {/* Concurrency Slider - only show after mount to prevent hydration issues */}
          {isMounted && (
            <div
              className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-secondary border border-border"
              data-testid="concurrency-slider-container"
            >
              <Users className="w-4 h-4 text-muted-foreground" />
              <Slider
                value={[maxConcurrency]}
                onValueChange={(value) => setMaxConcurrency(value[0])}
                min={1}
                max={10}
                step={1}
                className="w-20"
                data-testid="concurrency-slider"
              />
              <span
                className="text-sm text-muted-foreground min-w-[2ch] text-center"
                data-testid="concurrency-value"
              >
                {maxConcurrency}
              </span>
            </div>
          )}

          {/* Auto Mode Toggle - only show after mount to prevent hydration issues */}
          {isMounted && (
            <>
              {autoMode.isRunning ? (
                <Button
                  variant="destructive"
                  size="sm"
                  onClick={() => autoMode.stop()}
                  data-testid="stop-auto-mode"
                >
                  <StopCircle className="w-4 h-4 mr-2" />
                  Stop Auto Mode
                </Button>
              ) : (
                <Button
                  variant="secondary"
                  size="sm"
                  onClick={() => autoMode.start()}
                  data-testid="start-auto-mode"
                >
                  <Play className="w-4 h-4 mr-2" />
                  Auto Mode
                </Button>
              )}
            </>
          )}

          <HotkeyButton
            size="sm"
            onClick={() => setShowAddDialog(true)}
            hotkey={shortcuts.addFeature}
            hotkeyActive={false}
            data-testid="add-feature-button"
          >
            <Plus className="w-4 h-4 mr-2" />
            Add Feature
          </HotkeyButton>
        </div>
      </div>

      {/* Main Content Area */}
      <div className="flex-1 flex flex-col overflow-hidden">
        {/* Search Bar Row */}
        <div className="px-4 pt-4 pb-2 flex items-center justify-between">
          <div className="relative max-w-md flex-1">
            <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground pointer-events-none" />
            <Input
              ref={searchInputRef}
              type="text"
              placeholder="Search features by keyword..."
              value={searchQuery}
              onChange={(e) => setSearchQuery(e.target.value)}
              className="pl-9 pr-12 border-border"
              data-testid="kanban-search-input"
            />
            {searchQuery ? (
              <button
                onClick={() => setSearchQuery("")}
                className="absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded-sm hover:bg-accent text-muted-foreground hover:text-foreground transition-colors"
                data-testid="kanban-search-clear"
                aria-label="Clear search"
              >
                <X className="w-4 h-4" />
              </button>
            ) : (
              <span
                className="absolute right-2 top-1/2 -translate-y-1/2 px-1.5 py-0.5 text-[10px] font-mono rounded bg-brand-500/10 border border-brand-500/30 text-brand-400/70"
                data-testid="kanban-search-hotkey"
              >
                /
              </span>
            )}
          </div>

          {/* Board Background & Detail Level Controls */}
          {isMounted && (
            <TooltipProvider>
              <div className="flex items-center gap-2 ml-4">
                {/* Board Background Button */}
                <Tooltip>
                  <TooltipTrigger asChild>
                    <Button
                      variant="outline"
                      size="sm"
                      onClick={() => setShowBoardBackgroundModal(true)}
                      className="h-8 px-2"
                      data-testid="board-background-button"
                    >
                      <ImageIcon className="w-4 h-4" />
                    </Button>
                  </TooltipTrigger>
                  <TooltipContent>
                    <p>Board Background Settings</p>
                  </TooltipContent>
                </Tooltip>

                {/* Kanban Card Detail Level Toggle */}
                <div
                  className="flex items-center rounded-lg bg-secondary border border-border"
                  data-testid="kanban-detail-toggle"
                >
                  <Tooltip>
                    <TooltipTrigger asChild>
                      <button
                        onClick={() => setKanbanCardDetailLevel("minimal")}
                        className={cn(
                          "p-2 rounded-l-lg transition-colors",
                          kanbanCardDetailLevel === "minimal"
                            ? "bg-brand-500/20 text-brand-500"
                            : "text-muted-foreground hover:text-foreground hover:bg-accent"
                        )}
                        data-testid="kanban-toggle-minimal"
                      >
                        <Minimize2 className="w-4 h-4" />
                      </button>
                    </TooltipTrigger>
                    <TooltipContent>
                      <p>Minimal - Title & category only</p>
                    </TooltipContent>
                  </Tooltip>
                  <Tooltip>
                    <TooltipTrigger asChild>
                      <button
                        onClick={() => setKanbanCardDetailLevel("standard")}
                        className={cn(
                          "p-2 transition-colors",
                          kanbanCardDetailLevel === "standard"
                            ? "bg-brand-500/20 text-brand-500"
                            : "text-muted-foreground hover:text-foreground hover:bg-accent"
                        )}
                        data-testid="kanban-toggle-standard"
                      >
                        <Square className="w-4 h-4" />
                      </button>
                    </TooltipTrigger>
                    <TooltipContent>
                      <p>Standard - Steps & progress</p>
                    </TooltipContent>
                  </Tooltip>
                  <Tooltip>
                    <TooltipTrigger asChild>
                      <button
                        onClick={() => setKanbanCardDetailLevel("detailed")}
                        className={cn(
                          "p-2 rounded-r-lg transition-colors",
                          kanbanCardDetailLevel === "detailed"
                            ? "bg-brand-500/20 text-brand-500"
                            : "text-muted-foreground hover:text-foreground hover:bg-accent"
                        )}
                        data-testid="kanban-toggle-detailed"
                      >
                        <Maximize2 className="w-4 h-4" />
                      </button>
                    </TooltipTrigger>
                    <TooltipContent>
                      <p>Detailed - Model, tools & tasks</p>
                    </TooltipContent>
                  </Tooltip>
                </div>
              </div>
            </TooltipProvider>
          )}
        </div>
        {/* Kanban Columns */}
        {(() => {
          // Get background settings for current project
          const backgroundSettings =
            (currentProject && boardBackgroundByProject[currentProject.path]) ||
            defaultBackgroundSettings;

          // Build background image style if image exists
          const backgroundImageStyle = backgroundSettings.imagePath
            ? {
                backgroundImage: `url(${
                  process.env.NEXT_PUBLIC_SERVER_URL || "http://localhost:3008"
                }/api/fs/image?path=${encodeURIComponent(
                  backgroundSettings.imagePath
                )}&projectPath=${encodeURIComponent(
                  currentProject?.path || ""
                )}${
                  backgroundSettings.imageVersion
                    ? `&v=${backgroundSettings.imageVersion}`
                    : ""
                })`,
                backgroundSize: "cover",
                backgroundPosition: "center",
                backgroundRepeat: "no-repeat",
              }
            : {};

          return (
            <div
              className="flex-1 overflow-x-auto px-4 pb-4 relative"
              style={backgroundImageStyle}
            >
              <DndContext
                sensors={sensors}
                collisionDetection={collisionDetectionStrategy}
                onDragStart={handleDragStart}
                onDragEnd={handleDragEnd}
              >
                <div className="flex gap-4 h-full min-w-max">
                  {COLUMNS.map((column) => {
                    const columnFeatures = getColumnFeatures(column.id);
                    return (
                      <KanbanColumn
                        key={column.id}
                        id={column.id}
                        title={column.title}
                        color={column.color}
                        count={columnFeatures.length}
                        opacity={backgroundSettings.columnOpacity}
                        showBorder={backgroundSettings.columnBorderEnabled}
                        hideScrollbar={backgroundSettings.hideScrollbar}
                        headerAction={
                          column.id === "verified" &&
                          columnFeatures.length > 0 ? (
                            <Button
                              variant="ghost"
                              size="sm"
                              className="h-6 px-2 text-xs text-destructive hover:text-destructive hover:bg-destructive/10"
                              onClick={() =>
                                setShowDeleteAllVerifiedDialog(true)
                              }
                              data-testid="delete-all-verified-button"
                            >
                              <Trash2 className="w-3 h-3 mr-1" />
                              Delete All
                            </Button>
                          ) : column.id === "backlog" ? (
                            <div className="flex items-center gap-1">
                              <Button
                                variant="ghost"
                                size="sm"
                                className="h-6 w-6 p-0 text-yellow-500 hover:text-yellow-400 hover:bg-yellow-500/10 relative"
                                onClick={() => setShowSuggestionsDialog(true)}
                                title="Feature Suggestions"
                                data-testid="feature-suggestions-button"
                              >
                                <Lightbulb className="w-3.5 h-3.5" />
                                {suggestionsCount > 0 && (
                                  <span
                                    className="absolute -top-1 -right-1 w-4 h-4 text-[9px] font-mono rounded-full bg-yellow-500 text-black flex items-center justify-center"
                                    data-testid="suggestions-count"
                                  >
                                    {suggestionsCount}
                                  </span>
                                )}
                              </Button>
                              {columnFeatures.length > 0 && (
                                <HotkeyButton
                                  variant="ghost"
                                  size="sm"
                                  className="h-6 px-2 text-xs text-primary hover:text-primary hover:bg-primary/10"
                                  onClick={handleStartNextFeatures}
                                  hotkey={shortcuts.startNext}
                                  hotkeyActive={false}
                                  data-testid="start-next-button"
                                >
                                  <FastForward className="w-3 h-3 mr-1" />
                                  Pull Top
                                </HotkeyButton>
                              )}
                            </div>
                          ) : undefined
                        }
                      >
                        <SortableContext
                          items={columnFeatures.map((f) => f.id)}
                          strategy={verticalListSortingStrategy}
                        >
                          {columnFeatures.map((feature, index) => {
                            // Calculate shortcut key for in-progress cards (first 10 get 1-9, 0)
                            let shortcutKey: string | undefined;
                            if (column.id === "in_progress" && index < 10) {
                              shortcutKey =
                                index === 9 ? "0" : String(index + 1);
                            }
                            return (
                              <KanbanCard
                                key={feature.id}
                                feature={feature}
                                onEdit={() => setEditingFeature(feature)}
                                onDelete={() => handleDeleteFeature(feature.id)}
                                onViewOutput={() => handleViewOutput(feature)}
                                onVerify={() => handleVerifyFeature(feature)}
                                onResume={() => handleResumeFeature(feature)}
                                onForceStop={() =>
                                  handleForceStopFeature(feature)
                                }
                                onManualVerify={() =>
                                  handleManualVerify(feature)
                                }
                                onMoveBackToInProgress={() =>
                                  handleMoveBackToInProgress(feature)
                                }
                                onFollowUp={() => handleOpenFollowUp(feature)}
                                onCommit={() => handleCommitFeature(feature)}
                                onRevert={() => handleRevertFeature(feature)}
                                onMerge={() => handleMergeFeature(feature)}
                                hasContext={featuresWithContext.has(feature.id)}
                                isCurrentAutoTask={runningAutoTasks.includes(
                                  feature.id
                                )}
                                shortcutKey={shortcutKey}
                                opacity={backgroundSettings.cardOpacity}
                                glassmorphism={
                                  backgroundSettings.cardGlassmorphism
                                }
                                cardBorderEnabled={
                                  backgroundSettings.cardBorderEnabled
                                }
                                cardBorderOpacity={
                                  backgroundSettings.cardBorderOpacity
                                }
                              />
                            );
                          })}
                        </SortableContext>
                      </KanbanColumn>
                    );
                  })}
                </div>

                <DragOverlay>
                  {activeFeature && (
                    <Card className="w-72 opacity-90 rotate-3 shadow-xl">
                      <CardHeader className="p-3">
                        <CardTitle className="text-sm">
                          {activeFeature.description}
                        </CardTitle>
                        <CardDescription className="text-xs">
                          {activeFeature.category}
                        </CardDescription>
                      </CardHeader>
                    </Card>
                  )}
                </DragOverlay>
              </DndContext>
            </div>
          );
        })()}
      </div>

      {/* Board Background Modal */}
      <BoardBackgroundModal
        open={showBoardBackgroundModal}
        onOpenChange={setShowBoardBackgroundModal}
      />

      {/* Add Feature Dialog */}
      <Dialog
        open={showAddDialog}
        onOpenChange={(open) => {
          setShowAddDialog(open);
          // Clear preview map, validation error, and reset advanced options when dialog closes
          if (!open) {
            setNewFeaturePreviewMap(new Map());
            setShowAdvancedOptions(false);
            setDescriptionError(false);
          }
        }}
      >
        <DialogContent
          compact={!isMaximized}
          data-testid="add-feature-dialog"
          onPointerDownOutside={(e) => {
            // Prevent dialog from closing when clicking on category autocomplete dropdown
            const target = e.target as HTMLElement;
            if (target.closest('[data-testid="category-autocomplete-list"]')) {
              e.preventDefault();
            }
          }}
          onInteractOutside={(e) => {
            // Prevent dialog from closing when clicking on category autocomplete dropdown
            const target = e.target as HTMLElement;
            if (target.closest('[data-testid="category-autocomplete-list"]')) {
              e.preventDefault();
            }
          }}
        >
          <DialogHeader>
            <DialogTitle>Add New Feature</DialogTitle>
            <DialogDescription>
              Create a new feature card for the Kanban board.
            </DialogDescription>
          </DialogHeader>
          <Tabs
            defaultValue="prompt"
            className="py-4 flex-1 min-h-0 flex flex-col"
          >
            <TabsList className="w-full grid grid-cols-3 mb-4">
              <TabsTrigger value="prompt" data-testid="tab-prompt">
                <MessageSquare className="w-4 h-4 mr-2" />
                Prompt
              </TabsTrigger>
              <TabsTrigger value="model" data-testid="tab-model">
                <Settings2 className="w-4 h-4 mr-2" />
                Model
              </TabsTrigger>
              <TabsTrigger value="testing" data-testid="tab-testing">
                <FlaskConical className="w-4 h-4 mr-2" />
                Testing
              </TabsTrigger>
            </TabsList>

            {/* Prompt Tab */}
            <TabsContent value="prompt" className="space-y-4 overflow-y-auto">
              <div className="space-y-2">
                <Label htmlFor="description">Description</Label>
                <DescriptionImageDropZone
                  value={newFeature.description}
                  onChange={(value) => {
                    setNewFeature({ ...newFeature, description: value });
                    if (value.trim()) {
                      setDescriptionError(false);
                    }
                  }}
                  images={newFeature.imagePaths}
                  onImagesChange={(images) =>
                    setNewFeature({ ...newFeature, imagePaths: images })
                  }
                  placeholder="Describe the feature..."
                  previewMap={newFeaturePreviewMap}
                  onPreviewMapChange={setNewFeaturePreviewMap}
                  autoFocus
                  error={descriptionError}
                />
              </div>
              <div className="space-y-2">
                <Label htmlFor="category">Category (optional)</Label>
                <CategoryAutocomplete
                  value={newFeature.category}
                  onChange={(value) =>
                    setNewFeature({ ...newFeature, category: value })
                  }
                  suggestions={categorySuggestions}
                  placeholder="e.g., Core, UI, API"
                  data-testid="feature-category-input"
                />
              </div>
            </TabsContent>

            {/* Model Tab */}
            <TabsContent value="model" className="space-y-4 overflow-y-auto">
              {/* Show Advanced Options Toggle - only when profiles-only mode is enabled */}
              {showProfilesOnly && (
                <div className="flex items-center justify-between p-3 bg-muted/30 rounded-lg border border-border">
                  <div className="space-y-1">
                    <p className="text-sm font-medium text-foreground">
                      Simple Mode Active
                    </p>
                    <p className="text-xs text-muted-foreground">
                      Only showing AI profiles. Advanced model tweaking is
                      hidden.
                    </p>
                  </div>
                  <Button
                    variant="outline"
                    size="sm"
                    onClick={() => setShowAdvancedOptions(!showAdvancedOptions)}
                    data-testid="show-advanced-options-toggle"
                  >
                    <Settings2 className="w-4 h-4 mr-2" />
                    {showAdvancedOptions ? "Hide" : "Show"} Advanced
                  </Button>
                </div>
              )}

              {/* Quick Select Profile Section */}
              {aiProfiles.length > 0 && (
                <div className="space-y-3">
                  <div className="flex items-center justify-between">
                    <Label className="flex items-center gap-2">
                      <UserCircle className="w-4 h-4 text-brand-500" />
                      Quick Select Profile
                    </Label>
                    <span className="text-[11px] px-2 py-0.5 rounded-full border border-brand-500/40 text-brand-500">
                      Presets
                    </span>
                  </div>
                  <div className="grid grid-cols-2 gap-2">
                    {aiProfiles.slice(0, 6).map((profile) => {
                      const IconComponent = profile.icon
                        ? PROFILE_ICONS[profile.icon]
                        : Brain;
                      const isCodex = profile.provider === "codex";
                      const isSelected =
                        newFeature.model === profile.model &&
                        newFeature.thinkingLevel === profile.thinkingLevel;
                      return (
                        <button
                          key={profile.id}
                          type="button"
                          onClick={() => {
                            setNewFeature({
                              ...newFeature,
                              model: profile.model,
                              thinkingLevel: profile.thinkingLevel,
                            });
                            if (profile.thinkingLevel === "ultrathink") {
                              toast.warning("Ultrathink Selected", {
                                description:
                                  "Ultrathink uses extensive reasoning (45-180s, ~$0.48/task).",
                                duration: 4000,
                              });
                            }
                          }}
                          className={cn(
                            "flex items-center gap-2 p-2 rounded-lg border text-left transition-all",
                            isSelected
                              ? "bg-brand-500/10 border-brand-500 text-foreground"
                              : "bg-background hover:bg-accent border-input"
                          )}
                          data-testid={`profile-quick-select-${profile.id}`}
                        >
                          <div
                            className={cn(
                              "w-7 h-7 rounded flex items-center justify-center flex-shrink-0",
                              isCodex ? "bg-emerald-500/10" : "bg-primary/10"
                            )}
                          >
                            {IconComponent && (
                              <IconComponent
                                className={cn(
                                  "w-4 h-4",
                                  isCodex ? "text-emerald-500" : "text-primary"
                                )}
                              />
                            )}
                          </div>
                          <div className="min-w-0 flex-1">
                            <p className="text-sm font-medium truncate">
                              {profile.name}
                            </p>
                            <p className="text-[10px] text-muted-foreground truncate">
                              {profile.model}
                              {profile.thinkingLevel !== "none" &&
                                ` + ${profile.thinkingLevel}`}
                            </p>
                          </div>
                        </button>
                      );
                    })}
                  </div>
                  <p className="text-xs text-muted-foreground">
                    Or customize below. Manage profiles in{" "}
                    <button
                      type="button"
                      onClick={() => {
                        setShowAddDialog(false);
                        useAppStore.getState().setCurrentView("profiles");
                      }}
                      className="text-brand-500 hover:underline"
                    >
                      AI Profiles
                    </button>
                  </p>
                </div>
              )}

              {/* Separator */}
              {aiProfiles.length > 0 &&
                (!showProfilesOnly || showAdvancedOptions) && (
                  <div className="border-t border-border" />
                )}

              {/* Claude Models Section - Hidden when showProfilesOnly is true and showAdvancedOptions is false */}
              {(!showProfilesOnly || showAdvancedOptions) && (
                <div className="space-y-3">
                  <div className="flex items-center justify-between">
                    <Label className="flex items-center gap-2">
                      <Brain className="w-4 h-4 text-primary" />
                      Claude (SDK)
                    </Label>
                    <span className="text-[11px] px-2 py-0.5 rounded-full border border-primary/40 text-primary">
                      Native
                    </span>
                  </div>
                  {renderModelOptions(
                    CLAUDE_MODELS,
                    newFeature.model,
                    (model) =>
                      setNewFeature({
                        ...newFeature,
                        model,
                        thinkingLevel: modelSupportsThinking(model)
                          ? newFeature.thinkingLevel
                          : "none",
                      })
                  )}

                  {/* Thinking Level - Only shown when Claude model is selected */}
                  {newModelAllowsThinking && (
                    <div className="space-y-2 pt-2 border-t border-border">
                      <Label className="flex items-center gap-2 text-sm">
                        <Brain className="w-3.5 h-3.5 text-muted-foreground" />
                        Thinking Level
                      </Label>
                      <div className="flex gap-2 flex-wrap">
                        {(
                          [
                            "none",
                            "low",
                            "medium",
                            "high",
                            "ultrathink",
                          ] as ThinkingLevel[]
                        ).map((level) => (
                          <button
                            key={level}
                            type="button"
                            onClick={() => {
                              setNewFeature({
                                ...newFeature,
                                thinkingLevel: level,
                              });
                              if (level === "ultrathink") {
                                toast.warning("Ultrathink Selected", {
                                  description:
                                    "Ultrathink uses extensive reasoning (45-180s, ~$0.48/task). Best for complex architecture, migrations, or debugging.",
                                  duration: 5000,
                                });
                              }
                            }}
                            className={cn(
                              "flex-1 px-3 py-2 rounded-md border text-sm font-medium transition-colors min-w-[60px]",
                              newFeature.thinkingLevel === level
                                ? "bg-primary text-primary-foreground border-primary"
                                : "bg-background hover:bg-accent border-input"
                            )}
                            data-testid={`thinking-level-${level}`}
                          >
                            {level === "none" && "None"}
                            {level === "low" && "Low"}
                            {level === "medium" && "Med"}
                            {level === "high" && "High"}
                            {level === "ultrathink" && "Ultra"}
                          </button>
                        ))}
                      </div>
                      <p className="text-xs text-muted-foreground">
                        Higher levels give more time to reason through complex
                        problems.
                      </p>
                    </div>
                  )}
                </div>
              )}

              {/* Separator */}
              {(!showProfilesOnly || showAdvancedOptions) && (
                <div className="border-t border-border" />
              )}

              {/* Codex Models Section - Hidden when showProfilesOnly is true and showAdvancedOptions is false */}
              {(!showProfilesOnly || showAdvancedOptions) && (
                <div className="space-y-3">
                  <div className="flex items-center justify-between">
                    <Label className="flex items-center gap-2">
                      <Zap className="w-4 h-4 text-emerald-500" />
                      OpenAI via Codex CLI
                    </Label>
                    <span className="text-[11px] px-2 py-0.5 rounded-full border border-emerald-500/50 text-emerald-600 dark:text-emerald-300">
                      CLI
                    </span>
                  </div>
                  {renderModelOptions(CODEX_MODELS, newFeature.model, (model) =>
                    setNewFeature({
                      ...newFeature,
                      model,
                      thinkingLevel: "none",
                    })
                  )}
                  <p className="text-xs text-muted-foreground">
                    Codex models do not support thinking levels.
                  </p>
                </div>
              )}
            </TabsContent>

            {/* Testing Tab */}
            <TabsContent value="testing" className="space-y-4 overflow-y-auto">
              <div className="flex items-center space-x-2">
                <Checkbox
                  id="skip-tests"
                  checked={!newFeature.skipTests}
                  onCheckedChange={(checked) =>
                    setNewFeature({
                      ...newFeature,
                      skipTests: checked !== true,
                    })
                  }
                  data-testid="skip-tests-checkbox"
                />
                <div className="flex items-center gap-2">
                  <Label
                    htmlFor="skip-tests"
                    className="text-sm cursor-pointer"
                  >
                    Enable automated testing
                  </Label>
                  <FlaskConical className="w-3.5 h-3.5 text-muted-foreground" />
                </div>
              </div>
              <p className="text-xs text-muted-foreground">
                When enabled, this feature will use automated TDD. When
                disabled, it will require manual verification.
              </p>

              {/* Verification Steps - Only shown when skipTests is enabled */}
              {newFeature.skipTests && (
                <div className="space-y-2 pt-2 border-t border-border">
                  <Label>Verification Steps</Label>
                  <p className="text-xs text-muted-foreground mb-2">
                    Add manual steps to verify this feature works correctly.
                  </p>
                  {newFeature.steps.map((step, index) => (
                    <Input
                      key={index}
                      placeholder={`Verification step ${index + 1}`}
                      value={step}
                      onChange={(e) => {
                        const steps = [...newFeature.steps];
                        steps[index] = e.target.value;
                        setNewFeature({ ...newFeature, steps });
                      }}
                      data-testid={`feature-step-${index}-input`}
                    />
                  ))}
                  <Button
                    variant="outline"
                    size="sm"
                    onClick={() =>
                      setNewFeature({
                        ...newFeature,
                        steps: [...newFeature.steps, ""],
                      })
                    }
                    data-testid="add-step-button"
                  >
                    <Plus className="w-4 h-4 mr-2" />
                    Add Verification Step
                  </Button>
                </div>
              )}
            </TabsContent>
          </Tabs>
          <DialogFooter>
            <Button variant="ghost" onClick={() => setShowAddDialog(false)}>
              Cancel
            </Button>
            <HotkeyButton
              onClick={handleAddFeature}
              hotkey={{ key: "Enter", cmdCtrl: true }}
              hotkeyActive={showAddDialog}
              data-testid="confirm-add-feature"
            >
              Add Feature
            </HotkeyButton>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Edit Feature Dialog */}
      <Dialog
        open={!!editingFeature}
        onOpenChange={(open) => {
          if (!open) {
            setEditingFeature(null);
            setShowEditAdvancedOptions(false);
            setEditFeaturePreviewMap(new Map());
          }
        }}
      >
        <DialogContent
          compact={!isMaximized}
          data-testid="edit-feature-dialog"
          onPointerDownOutside={(e) => {
            // Prevent dialog from closing when clicking on category autocomplete dropdown
            const target = e.target as HTMLElement;
            if (target.closest('[data-testid="category-autocomplete-list"]')) {
              e.preventDefault();
            }
          }}
          onInteractOutside={(e) => {
            // Prevent dialog from closing when clicking on category autocomplete dropdown
            const target = e.target as HTMLElement;
            if (target.closest('[data-testid="category-autocomplete-list"]')) {
              e.preventDefault();
            }
          }}
        >
          <DialogHeader>
            <DialogTitle>Edit Feature</DialogTitle>
            <DialogDescription>Modify the feature details.</DialogDescription>
          </DialogHeader>
          {editingFeature && (
            <Tabs
              defaultValue="prompt"
              className="py-4 flex-1 min-h-0 flex flex-col"
            >
              <TabsList className="w-full grid grid-cols-3 mb-4">
                <TabsTrigger value="prompt" data-testid="edit-tab-prompt">
                  <MessageSquare className="w-4 h-4 mr-2" />
                  Prompt
                </TabsTrigger>
                <TabsTrigger value="model" data-testid="edit-tab-model">
                  <Settings2 className="w-4 h-4 mr-2" />
                  Model
                </TabsTrigger>
                <TabsTrigger value="testing" data-testid="edit-tab-testing">
                  <FlaskConical className="w-4 h-4 mr-2" />
                  Testing
                </TabsTrigger>
              </TabsList>

              {/* Prompt Tab */}
              <TabsContent value="prompt" className="space-y-4 overflow-y-auto">
                <div className="space-y-2">
                  <Label htmlFor="edit-description">Description</Label>
                  <DescriptionImageDropZone
                    value={editingFeature.description}
                    onChange={(value) =>
                      setEditingFeature({
                        ...editingFeature,
                        description: value,
                      })
                    }
                    images={editingFeature.imagePaths ?? []}
                    onImagesChange={(images) =>
                      setEditingFeature({
                        ...editingFeature,
                        imagePaths: images,
                      })
                    }
                    placeholder="Describe the feature..."
                    previewMap={editFeaturePreviewMap}
                    onPreviewMapChange={setEditFeaturePreviewMap}
                    data-testid="edit-feature-description"
                  />
                </div>
                <div className="space-y-2">
                  <Label htmlFor="edit-category">Category (optional)</Label>
                  <CategoryAutocomplete
                    value={editingFeature.category}
                    onChange={(value) =>
                      setEditingFeature({
                        ...editingFeature,
                        category: value,
                      })
                    }
                    suggestions={categorySuggestions}
                    placeholder="e.g., Core, UI, API"
                    data-testid="edit-feature-category"
                  />
                </div>
              </TabsContent>

              {/* Model Tab */}
              <TabsContent value="model" className="space-y-4 overflow-y-auto">
                {/* Show Advanced Options Toggle - only when profiles-only mode is enabled */}
                {showProfilesOnly && (
                  <div className="flex items-center justify-between p-3 bg-muted/30 rounded-lg border border-border">
                    <div className="space-y-1">
                      <p className="text-sm font-medium text-foreground">
                        Simple Mode Active
                      </p>
                      <p className="text-xs text-muted-foreground">
                        Only showing AI profiles. Advanced model tweaking is
                        hidden.
                      </p>
                    </div>
                    <Button
                      variant="outline"
                      size="sm"
                      onClick={() =>
                        setShowEditAdvancedOptions(!showEditAdvancedOptions)
                      }
                      data-testid="edit-show-advanced-options-toggle"
                    >
                      <Settings2 className="w-4 h-4 mr-2" />
                      {showEditAdvancedOptions ? "Hide" : "Show"} Advanced
                    </Button>
                  </div>
                )}

                {/* Quick Select Profile Section */}
                {aiProfiles.length > 0 && (
                  <div className="space-y-3">
                    <div className="flex items-center justify-between">
                      <Label className="flex items-center gap-2">
                        <UserCircle className="w-4 h-4 text-brand-500" />
                        Quick Select Profile
                      </Label>
                      <span className="text-[11px] px-2 py-0.5 rounded-full border border-brand-500/40 text-brand-500">
                        Presets
                      </span>
                    </div>
                    <div className="grid grid-cols-2 gap-2">
                      {aiProfiles.slice(0, 6).map((profile) => {
                        const IconComponent = profile.icon
                          ? PROFILE_ICONS[profile.icon]
                          : Brain;
                        const isCodex = profile.provider === "codex";
                        const isSelected =
                          editingFeature.model === profile.model &&
                          editingFeature.thinkingLevel ===
                            profile.thinkingLevel;
                        return (
                          <button
                            key={profile.id}
                            type="button"
                            onClick={() => {
                              setEditingFeature({
                                ...editingFeature,
                                model: profile.model,
                                thinkingLevel: profile.thinkingLevel,
                              });
                              if (profile.thinkingLevel === "ultrathink") {
                                toast.warning("Ultrathink Selected", {
                                  description:
                                    "Ultrathink uses extensive reasoning (45-180s, ~$0.48/task).",
                                  duration: 4000,
                                });
                              }
                            }}
                            className={cn(
                              "flex items-center gap-2 p-2 rounded-lg border text-left transition-all",
                              isSelected
                                ? "bg-brand-500/10 border-brand-500 text-foreground"
                                : "bg-background hover:bg-accent border-input"
                            )}
                            data-testid={`edit-profile-quick-select-${profile.id}`}
                          >
                            <div
                              className={cn(
                                "w-7 h-7 rounded flex items-center justify-center flex-shrink-0",
                                isCodex ? "bg-emerald-500/10" : "bg-primary/10"
                              )}
                            >
                              {IconComponent && (
                                <IconComponent
                                  className={cn(
                                    "w-4 h-4",
                                    isCodex
                                      ? "text-emerald-500"
                                      : "text-primary"
                                  )}
                                />
                              )}
                            </div>
                            <div className="min-w-0 flex-1">
                              <p className="text-sm font-medium truncate">
                                {profile.name}
                              </p>
                              <p className="text-[10px] text-muted-foreground truncate">
                                {profile.model}
                                {profile.thinkingLevel !== "none" &&
                                  ` + ${profile.thinkingLevel}`}
                              </p>
                            </div>
                          </button>
                        );
                      })}
                    </div>
                    <p className="text-xs text-muted-foreground">
                      Or customize below.
                    </p>
                  </div>
                )}

                {/* Separator */}
                {aiProfiles.length > 0 &&
                  (!showProfilesOnly || showEditAdvancedOptions) && (
                    <div className="border-t border-border" />
                  )}

                {/* Claude Models Section - Hidden when showProfilesOnly is true and showEditAdvancedOptions is false */}
                {(!showProfilesOnly || showEditAdvancedOptions) && (
                  <div className="space-y-3">
                    <div className="flex items-center justify-between">
                      <Label className="flex items-center gap-2">
                        <Brain className="w-4 h-4 text-primary" />
                        Claude (SDK)
                      </Label>
                      <span className="text-[11px] px-2 py-0.5 rounded-full border border-primary/40 text-primary">
                        Native
                      </span>
                    </div>
                    {renderModelOptions(
                      CLAUDE_MODELS,
                      (editingFeature.model ?? "opus") as AgentModel,
                      (model) =>
                        setEditingFeature({
                          ...editingFeature,
                          model,
                          thinkingLevel: modelSupportsThinking(model)
                            ? editingFeature.thinkingLevel
                            : "none",
                        }),
                      "edit-model-select"
                    )}

                    {/* Thinking Level - Only shown when Claude model is selected */}
                    {editModelAllowsThinking && (
                      <div className="space-y-2 pt-2 border-t border-border">
                        <Label className="flex items-center gap-2 text-sm">
                          <Brain className="w-3.5 h-3.5 text-muted-foreground" />
                          Thinking Level
                        </Label>
                        <div className="flex gap-2 flex-wrap">
                          {(
                            [
                              "none",
                              "low",
                              "medium",
                              "high",
                              "ultrathink",
                            ] as ThinkingLevel[]
                          ).map((level) => (
                            <button
                              key={level}
                              type="button"
                              onClick={() => {
                                setEditingFeature({
                                  ...editingFeature,
                                  thinkingLevel: level,
                                });
                                if (level === "ultrathink") {
                                  toast.warning("Ultrathink Selected", {
                                    description:
                                      "Ultrathink uses extensive reasoning (45-180s, ~$0.48/task). Best for complex architecture, migrations, or debugging.",
                                    duration: 5000,
                                  });
                                }
                              }}
                              className={cn(
                                "flex-1 px-3 py-2 rounded-md border text-sm font-medium transition-colors min-w-[60px]",
                                (editingFeature.thinkingLevel ?? "none") ===
                                  level
                                  ? "bg-primary text-primary-foreground border-primary"
                                  : "bg-background hover:bg-accent border-input"
                              )}
                              data-testid={`edit-thinking-level-${level}`}
                            >
                              {level === "none" && "None"}
                              {level === "low" && "Low"}
                              {level === "medium" && "Med"}
                              {level === "high" && "High"}
                              {level === "ultrathink" && "Ultra"}
                            </button>
                          ))}
                        </div>
                        <p className="text-xs text-muted-foreground">
                          Higher levels give more time to reason through complex
                          problems.
                        </p>
                      </div>
                    )}
                  </div>
                )}

                {/* Separator */}
                {(!showProfilesOnly || showEditAdvancedOptions) && (
                  <div className="border-t border-border" />
                )}

                {/* Codex Models Section - Hidden when showProfilesOnly is true and showEditAdvancedOptions is false */}
                {(!showProfilesOnly || showEditAdvancedOptions) && (
                  <div className="space-y-3">
                    <div className="flex items-center justify-between">
                      <Label className="flex items-center gap-2">
                        <Zap className="w-4 h-4 text-emerald-500" />
                        OpenAI via Codex CLI
                      </Label>
                      <span className="text-[11px] px-2 py-0.5 rounded-full border border-emerald-500/50 text-emerald-600 dark:text-emerald-300">
                        CLI
                      </span>
                    </div>
                    {renderModelOptions(
                      CODEX_MODELS,
                      (editingFeature.model ?? "opus") as AgentModel,
                      (model) =>
                        setEditingFeature({
                          ...editingFeature,
                          model,
                          thinkingLevel: "none",
                        }),
                      "edit-model-select"
                    )}
                    <p className="text-xs text-muted-foreground">
                      Codex models do not support thinking levels.
                    </p>
                  </div>
                )}
              </TabsContent>

              {/* Testing Tab */}
              <TabsContent
                value="testing"
                className="space-y-4 overflow-y-auto"
              >
                <div className="flex items-center space-x-2">
                  <Checkbox
                    id="edit-skip-tests"
                    checked={!(editingFeature.skipTests ?? false)}
                    onCheckedChange={(checked) =>
                      setEditingFeature({
                        ...editingFeature,
                        skipTests: checked !== true,
                      })
                    }
                    data-testid="edit-skip-tests-checkbox"
                  />
                  <div className="flex items-center gap-2">
                    <Label
                      htmlFor="edit-skip-tests"
                      className="text-sm cursor-pointer"
                    >
                      Enable automated testing
                    </Label>
                    <FlaskConical className="w-3.5 h-3.5 text-muted-foreground" />
                  </div>
                </div>
                <p className="text-xs text-muted-foreground">
                  When enabled, this feature will use automated TDD. When
                  disabled, it will require manual verification.
                </p>

                {/* Verification Steps - Only shown when skipTests is enabled */}
                {editingFeature.skipTests && (
                  <div className="space-y-2 pt-2 border-t border-border">
                    <Label>Verification Steps</Label>
                    <p className="text-xs text-muted-foreground mb-2">
                      Add manual steps to verify this feature works correctly.
                    </p>
                    {editingFeature.steps.map((step, index) => (
                      <Input
                        key={index}
                        value={step}
                        placeholder={`Verification step ${index + 1}`}
                        onChange={(e) => {
                          const steps = [...editingFeature.steps];
                          steps[index] = e.target.value;
                          setEditingFeature({ ...editingFeature, steps });
                        }}
                        data-testid={`edit-feature-step-${index}`}
                      />
                    ))}
                    <Button
                      variant="outline"
                      size="sm"
                      onClick={() =>
                        setEditingFeature({
                          ...editingFeature,
                          steps: [...editingFeature.steps, ""],
                        })
                      }
                    >
                      <Plus className="w-4 h-4 mr-2" />
                      Add Verification Step
                    </Button>
                  </div>
                )}
              </TabsContent>
            </Tabs>
          )}
          <DialogFooter>
            <Button variant="ghost" onClick={() => setEditingFeature(null)}>
              Cancel
            </Button>
            <HotkeyButton
              onClick={handleUpdateFeature}
              hotkey={{ key: "Enter", cmdCtrl: true }}
              hotkeyActive={!!editingFeature}
              data-testid="confirm-edit-feature"
            >
              Save Changes
            </HotkeyButton>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Agent Output Modal */}
      <AgentOutputModal
        open={showOutputModal}
        onClose={() => setShowOutputModal(false)}
        featureDescription={outputFeature?.description || ""}
        featureId={outputFeature?.id || ""}
        featureStatus={outputFeature?.status}
        onNumberKeyPress={handleOutputModalNumberKeyPress}
      />

      {/* Delete All Verified Dialog */}
      <Dialog
        open={showDeleteAllVerifiedDialog}
        onOpenChange={setShowDeleteAllVerifiedDialog}
      >
        <DialogContent data-testid="delete-all-verified-dialog">
          <DialogHeader>
            <DialogTitle>Delete All Verified Features</DialogTitle>
            <DialogDescription>
              Are you sure you want to delete all verified features? This action
              cannot be undone.
              {getColumnFeatures("verified").length > 0 && (
                <span className="block mt-2 text-yellow-500">
                  {getColumnFeatures("verified").length} feature(s) will be
                  deleted.
                </span>
              )}
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button
              variant="ghost"
              onClick={() => setShowDeleteAllVerifiedDialog(false)}
            >
              Cancel
            </Button>
            <Button
              variant="destructive"
              onClick={async () => {
                const verifiedFeatures = getColumnFeatures("verified");
                const api = getElectronAPI();

                for (const feature of verifiedFeatures) {
                  // Check if the feature is currently running
                  const isRunning = runningAutoTasks.includes(feature.id);

                  // If the feature is running, stop the agent first
                  if (isRunning) {
                    try {
                      await autoMode.stopFeature(feature.id);
                    } catch (error) {
                      console.error(
                        "[Board] Error stopping feature before delete:",
                        error
                      );
                    }
                  }

                  // Note: Agent context file will be deleted automatically when feature folder is deleted
                  // via persistFeatureDelete, so no manual deletion needed
                  try {
                    // Feature folder deletion handles agent-output.md automatically
                    console.log(
                      `[Board] Feature ${feature.id} will be deleted (including agent context)`
                    );
                  } catch (error) {
                    // Context file might not exist, which is fine
                    console.debug(
                      "[Board] No context file to delete for feature:",
                      feature.id
                    );
                  }

                  // Remove the feature
                  removeFeature(feature.id);
                  persistFeatureDelete(feature.id);
                }

                setShowDeleteAllVerifiedDialog(false);
                toast.success("All verified features deleted", {
                  description: `Deleted ${verifiedFeatures.length} feature(s).`,
                });
              }}
              data-testid="confirm-delete-all-verified"
            >
              <Trash2 className="w-4 h-4 mr-2" />
              Delete All
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Follow-Up Prompt Dialog */}
      <Dialog
        open={showFollowUpDialog}
        onOpenChange={(open) => {
          if (!open) {
            setShowFollowUpDialog(false);
            setFollowUpFeature(null);
            setFollowUpPrompt("");
            setFollowUpImagePaths([]);
            setFollowUpPreviewMap(new Map());
          }
        }}
      >
        <DialogContent
          compact={!isMaximized}
          data-testid="follow-up-dialog"
          onKeyDown={(e) => {
            if (
              (e.metaKey || e.ctrlKey) &&
              e.key === "Enter" &&
              followUpPrompt.trim()
            ) {
              e.preventDefault();
              handleSendFollowUp();
            }
          }}
        >
          <DialogHeader>
            <DialogTitle>Follow-Up Prompt</DialogTitle>
            <DialogDescription>
              Send additional instructions to continue working on this feature.
              {followUpFeature && (
                <span className="block mt-2 text-primary">
                  Feature: {followUpFeature.description.slice(0, 100)}
                  {followUpFeature.description.length > 100 ? "..." : ""}
                </span>
              )}
            </DialogDescription>
          </DialogHeader>
          <div className="space-y-4 py-4 overflow-y-auto flex-1 min-h-0">
            <div className="space-y-2">
              <Label htmlFor="follow-up-prompt">Instructions</Label>
              <DescriptionImageDropZone
                value={followUpPrompt}
                onChange={setFollowUpPrompt}
                images={followUpImagePaths}
                onImagesChange={setFollowUpImagePaths}
                placeholder="Describe what needs to be fixed or changed..."
                previewMap={followUpPreviewMap}
                onPreviewMapChange={setFollowUpPreviewMap}
              />
            </div>
            <p className="text-xs text-muted-foreground">
              The agent will continue from where it left off, using the existing
              context. You can attach screenshots to help explain the issue.
            </p>
          </div>
          <DialogFooter>
            <Button
              variant="ghost"
              onClick={() => {
                setShowFollowUpDialog(false);
                setFollowUpFeature(null);
                setFollowUpPrompt("");
                setFollowUpImagePaths([]);
                setFollowUpPreviewMap(new Map());
              }}
            >
              Cancel
            </Button>
            <HotkeyButton
              onClick={handleSendFollowUp}
              disabled={!followUpPrompt.trim()}
              hotkey={{ key: "Enter", cmdCtrl: true }}
              hotkeyActive={showFollowUpDialog}
              data-testid="confirm-follow-up"
            >
              <MessageSquare className="w-4 h-4 mr-2" />
              Send Follow-Up
            </HotkeyButton>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Feature Suggestions Dialog */}
      <FeatureSuggestionsDialog
        open={showSuggestionsDialog}
        onClose={() => {
          setShowSuggestionsDialog(false);
        }}
        projectPath={currentProject.path}
        suggestions={featureSuggestions}
        setSuggestions={(suggestions) => {
          setFeatureSuggestions(suggestions);
          setSuggestionsCount(suggestions.length);
        }}
        isGenerating={isGeneratingSuggestions}
        setIsGenerating={setIsGeneratingSuggestions}
      />
    </div>
  );
}
