import React, { useState, useCallback, useRef } from 'react';
import { Plus } from 'lucide-react';
import { Toaster, toast } from 'sonner';
import { Header } from './components/Header';
import { ProjectsPanel } from './components/ProjectsPanel';
import { ChangesPanel } from './components/ChangesPanel';
import { WorktreesPanel } from './components/WorktreesPanel';
import { LogsPanel } from './components/LogsPanel';
import { FileViewPanel } from './components/FileViewPanel';
import { DeleteDialog } from './components/DeleteDialog';
import { DeleteWorktreeDialog } from './components/DeleteWorktreeDialog';
import { AddProjectDialog } from './components/AddProjectDialog';
import { CreateWorktreeDialog } from './components/CreateWorktreeDialog';
import { ProposalChat } from './components/ProposalChat';
import { ProposalSessionTabs } from './components/ProposalSessionTabs';
import { CloseSessionDialog } from './components/CloseSessionDialog';
import { OverviewDashboard } from './components/OverviewDashboard';
import { useAppStore } from './store/useAppStore';
import { useWebSocket } from './hooks/useWebSocket';
import {
  controlRun,
  controlStop,
  gitSync,
  deleteProject as deleteProjectAPI,
  addProject as addProjectAPI,
  createWorktree as createWorktreeAPI,
  deleteWorktree as deleteWorktreeAPI,
  mergeWorktree as mergeWorktreeAPI,
  refreshWorktrees as refreshWorktreesAPI,
  createProposalSession as createProposalSessionAPI,
  listProposalSessions as listProposalSessionsAPI,
  deleteProposalSession as deleteProposalSessionAPI,
  mergeProposalSession as mergeProposalSessionAPI,
  setUiState,
  deleteUiState,
  APIError,
} from './api/restClient';
import {
  DesktopCenterTab,
  DesktopRightTab,
  TabName,
  UI_STATE_KEYS,
  parsePersistedTabState,
  resolvePersistedBrowseSelection,
  serializeFileBrowseContext,
} from './lib/uiStatePersistence';

function App() {
  const store = useAppStore();
  const hasHydratedFromUiStateRef = useRef(false);

  const persistUiState = useCallback((key: string, value: string | null) => {
    if (value === null) {
      void deleteUiState(key).catch((err) => {
        console.warn(`Failed to delete ui-state key '${key}':`, err);
      });
      return;
    }

    void setUiState(key, value).catch((err) => {
      console.warn(`Failed to persist ui-state key '${key}':`, err);
    });
  }, []);

  const clearPersistedBrowseState = useCallback((keys?: string[]) => {
    const browseKeys = keys ?? [
      UI_STATE_KEYS.fileBrowseContext,
      UI_STATE_KEYS.desktopCenterTab,
      UI_STATE_KEYS.desktopRightTab,
      UI_STATE_KEYS.mobileActiveTab,
    ];

    browseKeys.forEach((key) => {
      persistUiState(key, null);
    });
  }, [persistUiState]);

  const [activeTab, setActiveTab] = useState<TabName>('projects');
  const [desktopCenterTab, setDesktopCenterTab] = useState<DesktopCenterTab>('changes');
  const [desktopRightTab, setDesktopRightTab] = useState<DesktopRightTab>('logs');
  const [isLoading, setIsLoading] = useState(false);
  const [deleteTarget, setDeleteTarget] = useState<{ id: string; name: string } | null>(null);
  const [isAddProjectOpen, setIsAddProjectOpen] = useState(false);
  const [isCreateWorktreeOpen, setIsCreateWorktreeOpen] = useState(false);
  const [deleteWorktreeTarget, setDeleteWorktreeTarget] = useState<string | null>(null);
  const [closeSessionTarget, setCloseSessionTarget] = useState<string | null>(null);

  const selectDesktopCenterTab = useCallback((tab: DesktopCenterTab) => {
    setDesktopCenterTab(tab);
    persistUiState(UI_STATE_KEYS.desktopCenterTab, tab);
  }, [persistUiState]);

  const selectDesktopRightTab = useCallback((tab: DesktopRightTab) => {
    setDesktopRightTab(tab);
    persistUiState(UI_STATE_KEYS.desktopRightTab, tab);
  }, [persistUiState]);

  const selectMobileTab = useCallback((tab: TabName) => {
    setActiveTab(tab);
    persistUiState(UI_STATE_KEYS.mobileActiveTab, tab);
  }, [persistUiState]);

  useWebSocket({
    onStateUpdate: (state) => {
      store.setFullState(state);

      if (hasHydratedFromUiStateRef.current) {
        return;
      }

      const restoredTabs = parsePersistedTabState(state.ui_state);
      if (restoredTabs.desktopCenterTab) {
        setDesktopCenterTab(restoredTabs.desktopCenterTab);
      }
      if (restoredTabs.desktopRightTab) {
        setDesktopRightTab(restoredTabs.desktopRightTab);
      }
      if (restoredTabs.mobileActiveTab) {
        setActiveTab(restoredTabs.mobileActiveTab);
      }

      const persistedProjectId = state.ui_state?.[UI_STATE_KEYS.selectedProjectId];
      const selectedProjectId =
        persistedProjectId && state.projects.some((project) => project.id === persistedProjectId)
          ? persistedProjectId
          : null;

      if (!selectedProjectId) {
        if (persistedProjectId) {
          persistUiState(UI_STATE_KEYS.selectedProjectId, null);
        }
        store.selectProject(null);
        store.setFileBrowseContext(null);
        clearPersistedBrowseState();
        hasHydratedFromUiStateRef.current = true;
        return;
      }

      if (store.state.selectedProjectId !== selectedProjectId) {
        store.selectProject(selectedProjectId);
      }

      const browseRestore = resolvePersistedBrowseSelection({
        uiState: state.ui_state,
        selectedProjectId,
        projects: state.projects,
        worktreesByProjectId: state.worktrees,
      });

      if (browseRestore.status === 'restored') {
        store.setFileBrowseContext(browseRestore.context);
        setDesktopCenterTab(browseRestore.tabs.desktopCenterTab);
        setDesktopRightTab(browseRestore.tabs.desktopRightTab);
        setActiveTab(browseRestore.tabs.mobileActiveTab);
      } else if (browseRestore.status === 'stale') {
        clearPersistedBrowseState(browseRestore.keysToClear);
        store.setFileBrowseContext(null);
      }

      if (browseRestore.status === 'defer') {
        return;
      }

      listProposalSessionsAPI(selectedProjectId)
        .then((sessions) => {
          store.setProposalSessions(selectedProjectId, sessions);
          const persistedSessionId = state.ui_state?.[UI_STATE_KEYS.activeProposalSessionId];
          if (persistedSessionId) {
            const exists = sessions.some((session) => session.id === persistedSessionId);
            if (exists) {
              store.setActiveProposalSession(persistedSessionId);
            } else {
              persistUiState(UI_STATE_KEYS.activeProposalSessionId, null);
            }
          }
          hasHydratedFromUiStateRef.current = true;
        })
        .catch((err) => {
          console.error('Failed to restore proposal sessions:', err);
          hasHydratedFromUiStateRef.current = true;
        });
    },
    onLogEntry: (entry) => store.appendLog(entry),
    onConnectionChange: (status) => store.setConnectionStatus(status),
    onError: (error) => {
      console.error('WebSocket error:', error);
      toast.error(`Connection error: ${error.message}`);
    },
  });

  // Global Run handler (starts orchestration across all projects)
  const handleRun = useCallback(async () => {
    setIsLoading(true);
    try {
      await controlRun();
      toast.success('Orchestration started');
    } catch (err) {
      toast.error(`Failed to start: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, []);

  // Global Stop handler (stops orchestration across all projects)
  const handleStop = useCallback(async () => {
    setIsLoading(true);
    try {
      await controlStop();
      toast.success('Orchestration stopped');
    } catch (err) {
      toast.error(`Failed to stop: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, []);

  const handleGitSync = useCallback(async (projectId: string) => {
    setIsLoading(true);
    try {
      await gitSync(projectId);
      toast.success('Git sync completed');
    } catch (err) {
      toast.error(`Failed to sync: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, []);

  const handleDeleteClick = useCallback((projectId: string) => {
    const project = store.state.projects.find((p) => p.id === projectId);
    const name = project ? `${project.repo}/${project.branch}` : projectId;
    setDeleteTarget({ id: projectId, name });
  }, [store.state.projects]);

  const handleDeleteConfirm = useCallback(async () => {
    if (!deleteTarget) return;
    setIsLoading(true);
    try {
      await deleteProjectAPI(deleteTarget.id);
      toast.success('Project deleted');
      setDeleteTarget(null);
    } catch (err) {
      toast.error(`Failed to delete: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, [deleteTarget]);

  const handleAddProject = useCallback(async (remoteUrl: string, branch: string) => {
    setIsLoading(true);
    try {
      await addProjectAPI(remoteUrl, branch);
      toast.success('Project added');
      setIsAddProjectOpen(false);
    } catch (err) {
      toast.error(`Failed to add project: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, []);

  // Worktree handlers
  const handleCreateWorktree = useCallback(async (changeId: string) => {
    const projectId = store.state.selectedProjectId;
    if (!projectId) return;
    setIsLoading(true);
    try {
      await createWorktreeAPI(projectId, changeId);
      toast.success('Worktree created');
      setIsCreateWorktreeOpen(false);
      // Refresh worktree list
      const updated = await refreshWorktreesAPI(projectId);
      store.setWorktrees(projectId, updated);
    } catch (err) {
      toast.error(`Failed to create worktree: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, [store]);

  const handleDeleteWorktreeClick = useCallback((branchName: string) => {
    setDeleteWorktreeTarget(branchName);
  }, []);

  const handleDeleteWorktreeConfirm = useCallback(async () => {
    const projectId = store.state.selectedProjectId;
    if (!projectId || !deleteWorktreeTarget) return;
    setIsLoading(true);
    try {
      await deleteWorktreeAPI(projectId, deleteWorktreeTarget);
      toast.success('Worktree deleted');
      if (store.state.fileBrowseContext?.type === 'worktree' && store.state.fileBrowseContext.worktreeBranch === deleteWorktreeTarget) {
        store.setFileBrowseContext(null);
        clearPersistedBrowseState();
      }
      setDeleteWorktreeTarget(null);
      // Refresh worktree list
      const updated = await refreshWorktreesAPI(projectId);
      store.setWorktrees(projectId, updated);
    } catch (err) {
      toast.error(`Failed to delete worktree: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, [store, deleteWorktreeTarget]);

  const handleMergeWorktree = useCallback(async (branchName: string) => {
    const projectId = store.state.selectedProjectId;
    if (!projectId) return;
    setIsLoading(true);
    try {
      await mergeWorktreeAPI(projectId, branchName);
      toast.success('Branch merged successfully');
      // Refresh worktree list
      const updated = await refreshWorktreesAPI(projectId);
      store.setWorktrees(projectId, updated);
    } catch (err) {
      toast.error(`Failed to merge: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, [store]);

  const handleClickChange = useCallback((changeId: string) => {
    const context = { type: 'change', changeId } as const;
    store.setFileBrowseContext(context);
    persistUiState(UI_STATE_KEYS.fileBrowseContext, serializeFileBrowseContext(context));
    selectDesktopCenterTab('changes');
    selectDesktopRightTab('files');
    selectMobileTab('files');
  }, [persistUiState, selectDesktopCenterTab, selectDesktopRightTab, selectMobileTab, store]);

  const handleClickWorktree = useCallback((branch: string) => {
    const context = { type: 'worktree', worktreeBranch: branch } as const;
    store.setFileBrowseContext(context);
    persistUiState(UI_STATE_KEYS.fileBrowseContext, serializeFileBrowseContext(context));
    selectDesktopCenterTab('worktrees');
    selectDesktopRightTab('files');
    selectMobileTab('files');
  }, [persistUiState, selectDesktopCenterTab, selectDesktopRightTab, selectMobileTab, store]);

  // ─── Proposal Session Handlers ────────────────────────────────────────────

  const handleCreateProposalSession = useCallback(async () => {
    const projectId = store.state.selectedProjectId;
    if (!projectId) return;
    setIsLoading(true);
    try {
      const session = await createProposalSessionAPI(projectId);
      store.addProposalSession(projectId, session);
      store.setActiveProposalSession(session.id);
      persistUiState(UI_STATE_KEYS.activeProposalSessionId, session.id);
      toast.success('Proposal session created');
    } catch (err) {
      toast.error(`Failed to create session: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, [persistUiState, store]);

  const handleMergeProposalSession = useCallback(async () => {
    const projectId = store.state.selectedProjectId;
    const sessionId = store.state.activeProposalSessionId;
    if (!projectId || !sessionId) return;
    setIsLoading(true);
    try {
      await mergeProposalSessionAPI(projectId, sessionId);
      store.removeProposalSession(projectId, sessionId);
      persistUiState(UI_STATE_KEYS.activeProposalSessionId, null);
      toast.success('Session merged successfully');
    } catch (err) {
      toast.error(`Failed to merge: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, [persistUiState, store]);

  const handleCloseProposalSession = useCallback(() => {
    const sessionId = store.state.activeProposalSessionId;
    if (!sessionId) return;

    // Find the session to check if dirty
    const projectId = store.state.selectedProjectId;
    if (!projectId) return;
    const sessions = store.state.proposalSessionsByProjectId[projectId] || [];
    const session = sessions.find((s) => s.id === sessionId);

    if (session?.is_dirty) {
      setCloseSessionTarget(sessionId);
    } else {
      handleForceCloseSession(sessionId);
    }
  }, [store]);

  const handleForceCloseSession = useCallback(async (sessionId?: string) => {
    const projectId = store.state.selectedProjectId;
    const targetId = sessionId || closeSessionTarget;
    if (!projectId || !targetId) return;
    setIsLoading(true);
    try {
      await deleteProposalSessionAPI(projectId, targetId, true);
      store.removeProposalSession(projectId, targetId);
      if (store.state.activeProposalSessionId === targetId) {
        persistUiState(UI_STATE_KEYS.activeProposalSessionId, null);
      }
      setCloseSessionTarget(null);
      toast.success('Session closed');
    } catch (err) {
      toast.error(`Failed to close session: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, [store, closeSessionTarget]);

  const handleBackFromProposal = useCallback(() => {
    store.setActiveProposalSession(null);
    persistUiState(UI_STATE_KEYS.activeProposalSessionId, null);
  }, [persistUiState, store]);

  // Load proposal sessions when project is selected
  const handleSelectProjectWithSessions = useCallback((projectId: string | null) => {
    store.selectProject(projectId);
    store.setActiveProposalSession(null);
    store.setFileBrowseContext(null);
    persistUiState(UI_STATE_KEYS.selectedProjectId, projectId);
    persistUiState(UI_STATE_KEYS.activeProposalSessionId, null);
    clearPersistedBrowseState();

    if (projectId) {
      listProposalSessionsAPI(projectId)
        .then((sessions) => {
          store.setProposalSessions(projectId, sessions);
          const persistedSessionId = store.state.uiState[UI_STATE_KEYS.activeProposalSessionId];
          if (persistedSessionId) {
            const exists = sessions.some((session) => session.id === persistedSessionId);
            if (exists) {
              store.setActiveProposalSession(persistedSessionId);
            } else {
              persistUiState(UI_STATE_KEYS.activeProposalSessionId, null);
            }
          }
        })
        .catch((err) => console.error('Failed to load proposal sessions:', err));
    }
  }, [clearPersistedBrowseState, persistUiState, store]);

  const handleRefreshWorktrees = useCallback(async () => {
    const projectId = store.state.selectedProjectId;
    if (!projectId) return;
    setIsLoading(true);
    try {
      const updated = await refreshWorktreesAPI(projectId);
      store.setWorktrees(projectId, updated);
    } catch (err) {
      toast.error(`Failed to refresh worktrees: ${err instanceof APIError ? err.message : String(err)}`);
    } finally {
      setIsLoading(false);
    }
  }, [store]);

  const selectedProject = store.state.projects.find(
    (p) => p.id === store.state.selectedProjectId,
  );
  const allLogs = Object.values(store.state.logsByProjectId)
    .flat()
    .sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
  const selectedProjectLogs = store.state.selectedProjectId
    ? store.state.logsByProjectId[store.state.selectedProjectId] || []
    : allLogs;
  const selectedProjectWorktrees = store.state.selectedProjectId
    ? store.state.worktreesByProjectId[store.state.selectedProjectId] || []
    : [];

  // Derived state for proposal sessions
  const currentProjectSessions = store.state.selectedProjectId
    ? store.state.proposalSessionsByProjectId[store.state.selectedProjectId] || []
    : [];
  const activeProposalSession = currentProjectSessions.find(
    (s) => s.id === store.state.activeProposalSessionId,
  );

  // Get close target session for dialog
  const closeTargetSession = closeSessionTarget
    ? currentProjectSessions.find((s) => s.id === closeSessionTarget)
    : null;

  const panelProps = {
    projects: store.state.projects,
    selectedProjectId: store.state.selectedProjectId,
    onSelectProject: handleSelectProjectWithSessions,
    onGitSync: handleGitSync,
    onDelete: handleDeleteClick,
    isLoading,
    syncAvailable: store.state.syncAvailable,
    activeCommands: store.state.activeCommands,
  };

  // Active commands for the currently selected project
  const selectedProjectActiveCommands = store.state.activeCommands.filter(
    (cmd) => cmd.project_id === store.state.selectedProjectId
  );

  return (
    <div className="flex h-screen flex-col bg-[#09090b] text-[#fafafa]">
      <Header
        connectionStatus={store.state.connectionStatus}
        orchestrationStatus={store.state.orchestrationStatus}
        onRun={handleRun}
        onStop={handleStop}
        isLoading={isLoading}
      />

      <div className="flex flex-1 overflow-hidden">
        {/* Desktop layout */}
        <aside className="hidden w-72 shrink-0 flex-col border-r border-[#27272a] md:flex">
          <div className="flex items-center justify-between border-b border-[#27272a] px-3 py-2">
            <span className="text-xs font-medium text-[#52525b] uppercase tracking-wider">Projects</span>
            <button
              onClick={() => setIsAddProjectOpen(true)}
              className="rounded p-0.5 text-[#52525b] transition-colors hover:text-[#6366f1]"
              aria-label="Add project"
            >
              <Plus className="size-4" />
            </button>
          </div>
          <div className="flex-1 overflow-y-auto">
            <ProjectsPanel {...panelProps} />
          </div>
        </aside>

        <main className="hidden flex-col md:flex md:flex-1 overflow-hidden">
          <div className="border-b border-[#27272a] px-4 py-2.5">
            {selectedProject ? (
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-1.5">
                  <span className="text-sm font-medium text-[#fafafa]">{selectedProject.repo}</span>
                  <span className="text-[#3f3f46]">/</span>
                  <span className="text-sm text-[#71717a]">{selectedProject.branch}</span>
                </div>
                {!activeProposalSession && (
                  <button
                    onClick={handleCreateProposalSession}
                    disabled={isLoading}
                    className="flex items-center gap-1.5 rounded-md bg-[#6366f1] px-2.5 py-1 text-xs font-medium text-white transition-colors hover:bg-[#4f46e5] disabled:opacity-50"
                  >
                    <Plus className="size-3" />
                    Add Proposal
                  </button>
                )}
              </div>
            ) : (
              <span className="text-sm text-[#52525b]">Orchestration overview</span>
            )}
          </div>

          {selectedProject && (
            <>
              {/* Proposal session tabs */}
              {currentProjectSessions.length > 0 && (
                <ProposalSessionTabs
                  sessions={currentProjectSessions}
                  activeSessionId={store.state.activeProposalSessionId}
                  onSelectSession={(sessionId) => {
                    store.setActiveProposalSession(sessionId);
                    persistUiState(UI_STATE_KEYS.activeProposalSessionId, sessionId);
                  }}
                  onCreateSession={handleCreateProposalSession}
                  onCloseSession={(sid) => {
                    const s = currentProjectSessions.find((x) => x.id === sid);
                    if (s?.is_dirty) {
                      setCloseSessionTarget(sid);
                    } else {
                      handleForceCloseSession(sid);
                    }
                  }}
                />
              )}
            </>
          )}

          {/* Show ProposalChat when a session is active, otherwise show overview/normal panels */}
          {activeProposalSession && store.state.selectedProjectId ? (
            <ProposalChat
              projectId={store.state.selectedProjectId}
              sessionId={activeProposalSession.id}
              onBack={handleBackFromProposal}
              onMerge={handleMergeProposalSession}
              onClose={handleCloseProposalSession}
              onClickChange={handleClickChange}
              isLoading={isLoading}
            />
          ) : selectedProject ? (
            <div className="flex flex-1 overflow-hidden">
              <div className="flex w-72 shrink-0 flex-col border-r border-[#27272a]">
                {/* Tab switcher for Changes/Worktrees */}
                <div className="flex border-b border-[#27272a]">
                  {(['changes', 'worktrees'] as DesktopCenterTab[]).map((tab) => (
                    <button
                      key={tab}
                      onClick={() => selectDesktopCenterTab(tab)}
                      className={`flex-1 py-2 text-xs font-medium transition-colors ${
                        desktopCenterTab === tab
                          ? 'border-b-2 border-[#6366f1] text-[#fafafa]'
                          : 'text-[#52525b] hover:text-[#a1a1aa]'
                      }`}
                    >
                      {tab.charAt(0).toUpperCase() + tab.slice(1)}
                    </button>
                  ))}
                </div>
                <div className="flex-1 overflow-y-auto">
                  {desktopCenterTab === 'changes' ? (
                    <ChangesPanel
                      projects={store.state.projects}
                      selectedProjectId={store.state.selectedProjectId}
                      onClickChange={handleClickChange}
                      selectedChangeId={store.state.fileBrowseContext?.type === 'change' ? store.state.fileBrowseContext.changeId : null}
                    />
                  ) : (
                    <WorktreesPanel
                      worktrees={selectedProjectWorktrees}
                      selectedProjectId={store.state.selectedProjectId}
                      onMerge={handleMergeWorktree}
                      onDelete={handleDeleteWorktreeClick}
                      onCreate={() => setIsCreateWorktreeOpen(true)}
                      onRefresh={handleRefreshWorktrees}
                      onClickWorktree={handleClickWorktree}
                      selectedWorktreeBranch={store.state.fileBrowseContext?.type === 'worktree' ? store.state.fileBrowseContext.worktreeBranch : null}
                      isLoading={isLoading}
                      activeCommands={selectedProjectActiveCommands}
                    />
                  )}
                </div>
              </div>

              <div className="flex flex-1 flex-col overflow-hidden">
                {/* Right pane tab switcher: Logs / Files (Logs hidden when worktree selected) */}
                {store.state.fileBrowseContext?.type === 'worktree' ? (
                  <>
                    <div className="flex border-b border-[#27272a]">
                      <div className="flex-1 py-2 text-xs font-medium border-b-2 border-[#6366f1] text-[#fafafa] text-center">
                        Files
                      </div>
                    </div>
                    <div className="flex flex-1 overflow-hidden">
                      <FileViewPanel
                        projectId={store.state.selectedProjectId}
                        context={store.state.fileBrowseContext}
                      />
                    </div>
                  </>
                ) : (
                  <>
                    <div className="flex border-b border-[#27272a]">
                      {(['logs', 'files'] as DesktopRightTab[]).map((tab) => (
                        <button
                          key={tab}
                          onClick={() => selectDesktopRightTab(tab)}
                          className={`flex-1 py-2 text-xs font-medium transition-colors ${
                            desktopRightTab === tab
                              ? 'border-b-2 border-[#6366f1] text-[#fafafa]'
                              : 'text-[#52525b] hover:text-[#a1a1aa]'
                          }`}
                        >
                          {tab.charAt(0).toUpperCase() + tab.slice(1)}
                        </button>
                      ))}
                    </div>
                    <div className="flex flex-1 overflow-hidden">
                      {desktopRightTab === 'logs' ? (
                        <LogsPanel
                          logs={selectedProjectLogs}
                          selectedProjectId={store.state.selectedProjectId}
                        />
                      ) : (
                        <FileViewPanel
                          projectId={store.state.selectedProjectId}
                          context={store.state.fileBrowseContext}
                        />
                      )}
                    </div>
                  </>
                )}
              </div>
            </div>
          ) : (
            <OverviewDashboard />
          )}
        </main>

        {/* Mobile layout */}
        <div className="flex flex-1 flex-col md:hidden">
          <div className="flex border-b border-[#27272a]">
            {((['projects', 'changes', 'worktrees', 'logs', 'files'] as TabName[])
              .filter((tab) => !(tab === 'logs' && store.state.fileBrowseContext?.type === 'worktree'))
            ).map((tab) => (
              <button
                key={tab}
                onClick={() => selectMobileTab(tab)}
                className={`flex-1 py-2.5 text-xs font-medium transition-colors ${
                  activeTab === tab
                    ? 'border-b-2 border-[#6366f1] text-[#fafafa]'
                    : 'text-[#52525b] hover:text-[#a1a1aa]'
                }`}
              >
                {tab.charAt(0).toUpperCase() + tab.slice(1)}
              </button>
            ))}
          </div>

          <div className="flex-1 overflow-hidden">
            {!selectedProject ? (
              <OverviewDashboard />
            ) : (
              <>
                {activeTab === 'projects' && (
                  <div className="flex h-full flex-col">
                    <div className="flex items-center justify-between border-b border-[#27272a] px-3 py-2">
                      <span className="text-xs font-medium text-[#52525b] uppercase tracking-wider">Projects</span>
                      <button
                        onClick={() => setIsAddProjectOpen(true)}
                        className="rounded p-0.5 text-[#52525b] transition-colors hover:text-[#6366f1]"
                        aria-label="Add project"
                      >
                        <Plus className="size-4" />
                      </button>
                    </div>
                    <div className="flex-1 overflow-y-auto">
                      <ProjectsPanel {...panelProps} />
                    </div>
                  </div>
                )}
                {activeTab === 'changes' && (
                  <ChangesPanel
                    projects={store.state.projects}
                    selectedProjectId={store.state.selectedProjectId}
                    onClickChange={handleClickChange}
                    selectedChangeId={store.state.fileBrowseContext?.type === 'change' ? store.state.fileBrowseContext.changeId : null}
                  />
                )}
                {activeTab === 'worktrees' && (
                  <WorktreesPanel
                    worktrees={selectedProjectWorktrees}
                    selectedProjectId={store.state.selectedProjectId}
                    onMerge={handleMergeWorktree}
                    onDelete={handleDeleteWorktreeClick}
                    onCreate={() => setIsCreateWorktreeOpen(true)}
                    onRefresh={handleRefreshWorktrees}
                    onClickWorktree={handleClickWorktree}
                    selectedWorktreeBranch={store.state.fileBrowseContext?.type === 'worktree' ? store.state.fileBrowseContext.worktreeBranch : null}
                    isLoading={isLoading}
                    activeCommands={selectedProjectActiveCommands}
                  />
                )}
                {activeTab === 'logs' && (
                  <LogsPanel
                    logs={selectedProjectLogs}
                    selectedProjectId={store.state.selectedProjectId}
                  />
                )}
                {activeTab === 'files' && (
                  <FileViewPanel
                    projectId={store.state.selectedProjectId}
                    context={store.state.fileBrowseContext}
                  />
                )}
              </>
            )}
          </div>
        </div>
      </div>

      <DeleteDialog
        isOpen={deleteTarget !== null}
        projectName={deleteTarget?.name || ''}
        onConfirm={handleDeleteConfirm}
        onCancel={() => setDeleteTarget(null)}
        isLoading={isLoading}
      />

      <DeleteWorktreeDialog
        isOpen={deleteWorktreeTarget !== null}
        branchName={deleteWorktreeTarget || ''}
        onConfirm={handleDeleteWorktreeConfirm}
        onCancel={() => setDeleteWorktreeTarget(null)}
        isLoading={isLoading}
      />

      <AddProjectDialog
        isOpen={isAddProjectOpen}
        onSubmit={handleAddProject}
        onCancel={() => setIsAddProjectOpen(false)}
        isLoading={isLoading}
      />

      <CreateWorktreeDialog
        isOpen={isCreateWorktreeOpen}
        onSubmit={handleCreateWorktree}
        onCancel={() => setIsCreateWorktreeOpen(false)}
        isLoading={isLoading}
      />

      <CloseSessionDialog
        isOpen={closeSessionTarget !== null}
        uncommittedFiles={closeTargetSession?.uncommitted_files || []}
        onForceClose={() => handleForceCloseSession()}
        onCancel={() => setCloseSessionTarget(null)}
        isLoading={isLoading}
      />

      <Toaster
        position="bottom-right"
        theme="dark"
        toastOptions={{
          style: {
            background: '#18181b',
            border: '1px solid #27272a',
            color: '#fafafa',
          },
        }}
      />
    </div>
  );
}

export default App;
