// ========================================
// MCP Manager Page
// ========================================
// Manage MCP servers (Model Context Protocol) with tabbed interface
// Supports Templates, Servers, and Cross-CLI tabs

import { useState, useMemo } from 'react';
import { useIntl } from 'react-intl';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import {
  Server,
  Plus,
  Search,
  RefreshCw,
  Globe,
  Folder,
  Power,
  PowerOff,
  Edit,
  Trash2,
  ChevronDown,
  ChevronUp,
  BookmarkPlus,
  AlertTriangle,
  Link,
  Eye,
  EyeOff,
} from 'lucide-react';
import { Card } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { Badge } from '@/components/ui/Badge';
import { McpServerDialog } from '@/components/mcp/McpServerDialog';
import { CliModeToggle, type CliMode } from '@/components/mcp/CliModeToggle';
import { CodexMcpEditableCard } from '@/components/mcp/CodexMcpEditableCard';
import { CcwToolsMcpCard } from '@/components/mcp/CcwToolsMcpCard';
import { McpTemplatesSection, TemplateSaveDialog } from '@/components/mcp/McpTemplatesSection';
import { RecommendedMcpSection } from '@/components/mcp/RecommendedMcpSection';
import { WindowsCompatibilityWarning } from '@/components/mcp/WindowsCompatibilityWarning';
import { CrossCliSyncPanel } from '@/components/mcp/CrossCliSyncPanel';
import { AllProjectsTable } from '@/components/mcp/AllProjectsTable';
import { OtherProjectsSection } from '@/components/mcp/OtherProjectsSection';
import { TabsNavigation } from '@/components/ui/TabsNavigation';
import { useMcpServers, useMcpServerMutations, useNotifications } from '@/hooks';
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
import {
  fetchCodexMcpServers,
  fetchCcwMcpConfig,
  fetchCcwMcpConfigForCodex,
  updateCcwConfig,
  updateCcwConfigForCodex,
  installCcwMcp,
  uninstallCcwMcpFromScope,
  codexRemoveServer,
  codexToggleServer,
  saveMcpTemplate,
  type McpServer,
  type McpServerConflict,
  type CcwMcpConfig,
  isHttpMcpServer,
  isStdioMcpServer,
} from '@/lib/api';
import { cn } from '@/lib/utils';

// ========== MCP Server Card Component ==========

interface McpServerCardProps {
  server: McpServer;
  isExpanded: boolean;
  onToggleExpand: () => void;
  onToggle: (serverName: string, enabled: boolean) => void;
  onEdit: (server: McpServer) => void;
  onDelete: (server: McpServer) => void;
  onSaveAsTemplate: (server: McpServer) => void;
  conflictInfo?: McpServerConflict;
}

/**
 * Mask a header value for display (show first 4 chars + ***)
 */
function maskHeaderValue(value: string): string {
  if (!value || value.length <= 4) return '****';
  return value.substring(0, 4) + '****';
}

function McpServerCard({ server, isExpanded, onToggleExpand, onToggle, onEdit, onDelete, onSaveAsTemplate, conflictInfo }: McpServerCardProps) {
  const { formatMessage } = useIntl();
  const [showHeaderValues, setShowHeaderValues] = useState<Record<string, boolean>>({});

  const isHttp = isHttpMcpServer(server);
  const isStdio = isStdioMcpServer(server);

  // Get display text for server summary line
  const getServerSummary = () => {
    if (isHttp) {
      return server.url;
    }
    return `${server.command} ${server.args?.join(' ') || ''}`.trim();
  };

  // Get all headers for HTTP server (combine Claude + Codex formats)
  const getHttpHeaders = (): Array<{ name: string; value: string; isEnvVar?: boolean }> => {
    if (!isHttp) return [];

    const headers: Array<{ name: string; value: string; isEnvVar?: boolean }> = [];

    // Claude format: headers object
    if (server.headers) {
      Object.entries(server.headers).forEach(([name, value]) => {
        headers.push({ name, value });
      });
    }

    // Codex format: httpHeaders object
    if (server.httpHeaders) {
      Object.entries(server.httpHeaders).forEach(([name, value]) => {
        headers.push({ name, value });
      });
    }

    // Codex format: bearerTokenEnvVar (adds Authorization header)
    if (server.bearerTokenEnvVar) {
      headers.push({
        name: 'Authorization',
        value: `Bearer $${server.bearerTokenEnvVar}`,
        isEnvVar: true,
      });
    }

    return headers;
  };

  // Toggle header value visibility
  const toggleHeaderValue = (headerName: string) => {
    setShowHeaderValues(prev => ({
      ...prev,
      [headerName]: !prev[headerName]
    }));
  };

  return (
    <Card className={cn('overflow-hidden', !server.enabled && 'opacity-60')}>
      {/* Header */}
      <div
        className="p-4 cursor-pointer hover:bg-muted/50 transition-colors"
        onClick={onToggleExpand}
      >
        <div className="flex items-start justify-between gap-2">
          <div className="flex items-start gap-3">
            <div className={cn(
              'p-2 rounded-lg',
              server.enabled ? 'bg-primary/10' : 'bg-muted'
            )}>
              <Server className={cn(
                'w-5 h-5',
                server.enabled ? 'text-primary' : 'text-muted-foreground'
              )} />
            </div>
            <div>
              <div className="flex items-center gap-2">
                <span className="text-sm font-medium text-foreground">
                  {server.name}
                </span>
                {/* Transport type badge */}
                {isHttp && (
                  <Badge variant="outline" className="text-xs text-blue-600 border-blue-300">
                    <Link className="w-3 h-3 mr-1" />
                    {formatMessage({ id: 'mcp.transport.http' })}
                  </Badge>
                )}
                <Badge variant={server.scope === 'global' ? 'default' : 'secondary'} className="text-xs">
                  {server.scope === 'global' ? (
                    <><Globe className="w-3 h-3 mr-1" />{formatMessage({ id: 'mcp.scope.global' })}</>
                  ) : (
                    <><Folder className="w-3 h-3 mr-1" />{formatMessage({ id: 'mcp.scope.project' })}</>
                  )}
                </Badge>
                {conflictInfo && (
                  <Badge variant="outline" className="text-xs text-orange-500 border-orange-300">
                    <AlertTriangle className="w-3 h-3 mr-1" />
                    {formatMessage({ id: 'mcp.conflict.badge' })}
                  </Badge>
                )}
                {server.enabled && (
                  <Badge variant="outline" className="text-xs text-green-600">
                    {formatMessage({ id: 'mcp.status.enabled' })}
                  </Badge>
                )}
              </div>
              <p className="text-sm text-muted-foreground mt-1 font-mono truncate max-w-md" title={getServerSummary()}>
                {getServerSummary()}
              </p>
            </div>
          </div>
          <div className="flex items-center gap-2">
            <Button
              variant="ghost"
              size="sm"
              className="h-8 w-8 p-0"
              onClick={(e) => {
                e.stopPropagation();
                onToggle(server.name, !server.enabled);
              }}
            >
              {server.enabled ? <Power className="w-4 h-4 text-green-600" /> : <PowerOff className="w-4 h-4" />}
            </Button>
            <Button
              variant="ghost"
              size="sm"
              className="h-8 w-8 p-0"
              onClick={(e) => {
                e.stopPropagation();
                onSaveAsTemplate(server);
              }}
              title={formatMessage({ id: 'mcp.templates.actions.saveAsTemplate' })}
            >
              <BookmarkPlus className="w-4 h-4 text-primary" />
            </Button>
            <Button
              variant="ghost"
              size="sm"
              className="h-8 w-8 p-0"
              onClick={(e) => {
                e.stopPropagation();
                onEdit(server);
              }}
            >
              <Edit className="w-4 h-4" />
            </Button>
            <Button
              variant="ghost"
              size="sm"
              className="h-8 w-8 p-0"
              onClick={(e) => {
                e.stopPropagation();
                onDelete(server);
              }}
            >
              <Trash2 className="w-4 h-4 text-destructive" />
            </Button>
            {isExpanded ? (
              <ChevronUp className="w-5 h-5 text-muted-foreground" />
            ) : (
              <ChevronDown className="w-5 h-5 text-muted-foreground" />
            )}
          </div>
        </div>
      </div>

      {/* Expanded Content */}
      {isExpanded && (
        <div className="border-t border-border p-4 space-y-3 bg-muted/30">
          {/* HTTP Server Details */}
          {isHttp && (
            <>
              {/* URL */}
              <div>
                <p className="text-xs text-muted-foreground mb-1">{formatMessage({ id: 'mcp.http.url' })}</p>
                <code className="text-sm bg-background px-2 py-1 rounded block overflow-x-auto break-all">
                  {server.url}
                </code>
              </div>

              {/* HTTP Headers */}
              {getHttpHeaders().length > 0 && (
                <div>
                  <p className="text-xs text-muted-foreground mb-1">{formatMessage({ id: 'mcp.http.headers' })}</p>
                  <div className="space-y-1">
                    {getHttpHeaders().map((header) => (
                      <div key={header.name} className="flex items-center gap-2 text-sm">
                        <Badge variant="secondary" className="font-mono">{header.name}</Badge>
                        <span className="text-muted-foreground">=</span>
                        <code className="text-xs bg-background px-2 py-1 rounded flex-1 overflow-x-auto">
                          {showHeaderValues[header.name] ? header.value : maskHeaderValue(header.value)}
                        </code>
                        <Button
                          variant="ghost"
                          size="sm"
                          className="h-6 w-6 p-0"
                          onClick={() => toggleHeaderValue(header.name)}
                          title={showHeaderValues[header.name]
                            ? formatMessage({ id: 'mcp.http.hideValue' })
                            : formatMessage({ id: 'mcp.http.showValue' })}
                        >
                          {showHeaderValues[header.name] ? (
                            <EyeOff className="w-3 h-3 text-muted-foreground" />
                          ) : (
                            <Eye className="w-3 h-3 text-muted-foreground" />
                          )}
                        </Button>
                        {header.isEnvVar && (
                          <Badge variant="outline" className="text-xs text-blue-500">
                            {formatMessage({ id: 'mcp.http.envVar' })}
                          </Badge>
                        )}
                      </div>
                    ))}
                  </div>
                </div>
              )}
            </>
          )}

          {/* STDIO Server Details */}
          {isStdio && (
            <>
              {/* Command details */}
              <div>
                <p className="text-xs text-muted-foreground mb-1">{formatMessage({ id: 'mcp.command' })}</p>
                <code className="text-sm bg-background px-2 py-1 rounded block overflow-x-auto">
                  {server.command}
                </code>
              </div>

              {/* Args */}
              {server.args && server.args.length > 0 && (
                <div>
                  <p className="text-xs text-muted-foreground mb-1">{formatMessage({ id: 'mcp.args' })}</p>
                  <div className="flex flex-wrap gap-1">
                    {server.args.map((arg, idx) => (
                      <Badge key={idx} variant="outline" className="font-mono text-xs">
                        {arg}
                      </Badge>
                    ))}
                  </div>
                </div>
              )}

              {/* Environment variables */}
              {server.env && Object.keys(server.env).length > 0 && (
                <div>
                  <p className="text-xs text-muted-foreground mb-1">{formatMessage({ id: 'mcp.env' })}</p>
                  <div className="space-y-1">
                    {Object.entries(server.env).map(([key, value]) => (
                      <div key={key} className="flex items-center gap-2 text-sm">
                        <Badge variant="secondary" className="font-mono">{key}</Badge>
                        <span className="text-muted-foreground">=</span>
                        <code className="text-xs bg-background px-2 py-1 rounded flex-1 overflow-x-auto">
                          {value as string}
                        </code>
                      </div>
                    ))}
                  </div>
                </div>
              )}
            </>
          )}

          {/* Conflict warning panel */}
          {conflictInfo && (
            <div className="p-3 bg-orange-50 dark:bg-orange-950/30 border border-orange-200 dark:border-orange-800 rounded-lg space-y-1">
              <div className="flex items-center gap-2 text-orange-700 dark:text-orange-400">
                <AlertTriangle className="w-4 h-4" />
                <span className="text-sm font-medium">{formatMessage({ id: 'mcp.conflict.title' })}</span>
              </div>
              <p className="text-xs text-orange-600 dark:text-orange-400/80">
                {formatMessage({ id: 'mcp.conflict.description' }, { scope: formatMessage({ id: `mcp.scope.${conflictInfo.effectiveScope}` }) })}
              </p>
              <p className="text-xs text-orange-600 dark:text-orange-400/80">
                {formatMessage({ id: 'mcp.conflict.resolution' })}
              </p>
            </div>
          )}
        </div>
      )}
    </Card>
  );
}

// ========== Main Page Component ==========

export function McpManagerPage() {
  const { formatMessage } = useIntl();
  const [activeTab, setActiveTab] = useState<'templates' | 'servers' | 'cross-cli'>('servers');
  const [searchQuery, setSearchQuery] = useState('');
  const [scopeFilter, setScopeFilter] = useState<'all' | 'project' | 'global'>('all');
  const [expandedServers, setExpandedServers] = useState<Set<string>>(new Set());
  const [dialogOpen, setDialogOpen] = useState(false);
  const [editingServer, setEditingServer] = useState<McpServer | undefined>(undefined);
  const [cliMode, setCliMode] = useState<CliMode>('claude');
  const [codexExpandedServers, setCodexExpandedServers] = useState<Set<string>>(new Set());
  const [saveTemplateDialogOpen, setSaveTemplateDialogOpen] = useState(false);
  const [serverToSaveAsTemplate, setServerToSaveAsTemplate] = useState<McpServer | undefined>(undefined);

  const queryClient = useQueryClient();
  const notifications = useNotifications();

  const {
    servers,
    projectServers,
    globalServers,
    conflicts,
    totalCount,
    enabledCount,
    isLoading,
    isFetching,
    refetch,
  } = useMcpServers({
    scope: scopeFilter === 'all' ? undefined : scopeFilter,
  });

  // Fetch Codex MCP servers when in codex mode
  const codexQuery = useQuery({
    queryKey: ['codexMcpServers'],
    queryFn: fetchCodexMcpServers,
    enabled: cliMode === 'codex',
    staleTime: 2 * 60 * 1000, // 2 minutes
  });

  const projectPath = useWorkflowStore(selectProjectPath);

  // Fetch CCW Tools MCP configuration (Claude mode only)
  const ccwMcpQuery = useQuery({
    queryKey: ['ccwMcpConfig', projectPath],
    queryFn: () => fetchCcwMcpConfig(projectPath ?? undefined),
    enabled: cliMode === 'claude',
    staleTime: 5 * 60 * 1000, // 5 minutes
  });

  // Fetch CCW Tools MCP configuration (Codex mode only)
  const ccwMcpCodexQuery = useQuery({
    queryKey: ['ccwMcpConfigCodex'],
    queryFn: fetchCcwMcpConfigForCodex,
    enabled: cliMode === 'codex',
    staleTime: 5 * 60 * 1000, // 5 minutes
  });

  const {
    toggleServer,
    deleteServer,
  } = useMcpServerMutations();

  const toggleExpand = (serverName: string) => {
    setExpandedServers((prev) => {
      const next = new Set(prev);
      if (next.has(serverName)) {
        next.delete(serverName);
      } else {
        next.add(serverName);
      }
      return next;
    });
  };

  const toggleCodexExpand = (serverName: string) => {
    setCodexExpandedServers((prev) => {
      const next = new Set(prev);
      if (next.has(serverName)) {
        next.delete(serverName);
      } else {
        next.add(serverName);
      }
      return next;
    });
  };

  const handleToggle = (serverName: string, enabled: boolean) => {
    toggleServer(serverName, enabled);
  };

  const handleDelete = async (server: McpServer) => {
    if (confirm(formatMessage({ id: 'mcp.deleteConfirm' }, { name: server.name }))) {
      try {
        await deleteServer(server.name, server.scope);
        notifications.success(
          formatMessage({ id: 'mcp.actions.delete' }),
          server.name
        );
        refetch();
      } catch (error) {
        console.error('Failed to delete MCP server:', error);
        notifications.error(
          formatMessage({ id: 'mcp.actions.delete' }),
          error instanceof Error ? error.message : String(error)
        );
      }
    }
  };

  const handleEdit = (server: McpServer) => {
    setEditingServer(server);
    setDialogOpen(true);
  };

  const handleAddClick = () => {
    setEditingServer(undefined);
    setDialogOpen(true);
  };

  const handleDialogClose = () => {
    setDialogOpen(false);
    setEditingServer(undefined);
  };

  const handleDialogSave = () => {
    setDialogOpen(false);
    setEditingServer(undefined);
    refetch();
  };

  const handleModeChange = (mode: CliMode) => {
    setCliMode(mode);
  };

  // CCW MCP handlers
  const ccwConfig = ccwMcpQuery.data ?? {
    isInstalled: false,
    enabledTools: [],
    projectRoot: undefined,
    allowedDirs: undefined,
    enableSandbox: undefined,
    installedScopes: [] as ('global' | 'project')[],
  };

  const ccwMcpQueryKey = ['ccwMcpConfig', projectPath];

  const handleToggleCcwTool = async (tool: string, enabled: boolean) => {
    // Read latest from cache to avoid stale closures
    const currentConfig = queryClient.getQueryData<CcwMcpConfig>(ccwMcpQueryKey) ?? ccwConfig;
    const currentTools = currentConfig.enabledTools;
    const previousConfig = queryClient.getQueryData<CcwMcpConfig>(ccwMcpQueryKey);

    const updatedTools = enabled
      ? (currentTools.includes(tool) ? currentTools : [...currentTools, tool])
      : currentTools.filter((t) => t !== tool);

    // Optimistic cache update for immediate UI response
    queryClient.setQueryData(ccwMcpQueryKey, (old: CcwMcpConfig | undefined) => {
      if (!old) return old;
      return { ...old, enabledTools: updatedTools };
    });

    try {
      await updateCcwConfig({ ...currentConfig, enabledTools: updatedTools });
    } catch (error) {
      notifications.error(formatMessage({ id: 'mcp.actions.toggle.error' }), error instanceof Error ? error.message : String(error));
      console.error('Failed to toggle CCW tool:', error);
      queryClient.setQueryData(ccwMcpQueryKey, previousConfig);
    }
    ccwMcpQuery.refetch();
  };

  const handleUpdateCcwConfig = async (config: Partial<CcwMcpConfig>) => {
    // Read BEFORE optimistic update to capture previous state for rollback
    const previousConfig = queryClient.getQueryData<CcwMcpConfig>(ccwMcpQueryKey);

    // Optimistic cache update for immediate UI response
    queryClient.setQueryData(ccwMcpQueryKey, (old: CcwMcpConfig | undefined) => {
      if (!old) return old;
      return { ...old, ...config };
    });

    // Read AFTER optimistic update to get the latest merged state
    const currentConfig = queryClient.getQueryData<CcwMcpConfig>(ccwMcpQueryKey) ?? ccwConfig;

    try {
      // Only pass the fields that updateCcwConfig expects
      await updateCcwConfig({
        enabledTools: currentConfig.enabledTools,
        projectRoot: currentConfig.projectRoot,
        allowedDirs: currentConfig.allowedDirs,
        enableSandbox: currentConfig.enableSandbox,
      });
    } catch (error) {
      notifications.error(formatMessage({ id: 'mcp.actions.update.error' }), error instanceof Error ? error.message : String(error));
      console.error('Failed to update CCW config:', error);
      queryClient.setQueryData(ccwMcpQueryKey, previousConfig);
    }
    ccwMcpQuery.refetch();
  };

  const handleCcwInstall = () => {
    ccwMcpQuery.refetch();
  };

  // Build conflict map for quick lookup
  const conflictMap = useMemo(() => {
    const map = new Map<string, McpServerConflict>();
    for (const c of conflicts) map.set(c.name, c);
    return map;
  }, [conflicts]);

  // CCW scope-specific handlers
  const handleCcwInstallToScope = async (scope: 'global' | 'project') => {
    try {
      await installCcwMcp(scope, scope === 'project' ? projectPath ?? undefined : undefined);
      ccwMcpQuery.refetch();
    } catch (error) {
      notifications.error(formatMessage({ id: 'mcp.actions.install.error' }), error instanceof Error ? error.message : String(error));
      console.error('Failed to install CCW MCP to scope:', error);
    }
  };

  const handleCcwUninstallFromScope = async (scope: 'global' | 'project') => {
    try {
      await uninstallCcwMcpFromScope(scope, scope === 'project' ? projectPath ?? undefined : undefined);
      ccwMcpQuery.refetch();
      queryClient.invalidateQueries({ queryKey: ['mcpServers'] });
    } catch (error) {
      notifications.error(formatMessage({ id: 'mcp.actions.uninstall.error' }), error instanceof Error ? error.message : String(error));
      console.error('Failed to uninstall CCW MCP from scope:', error);
    }
  };

  // CCW MCP handlers for Codex mode
  const ccwCodexConfig = ccwMcpCodexQuery.data ?? {
    isInstalled: false,
    enabledTools: [] as string[],
    projectRoot: undefined,
    allowedDirs: undefined,
    enableSandbox: undefined,
    installedScopes: [] as ('global' | 'project')[],
  };

  const handleToggleCcwToolCodex = async (tool: string, enabled: boolean) => {
    const currentConfig = queryClient.getQueryData<CcwMcpConfig>(['ccwMcpConfigCodex']) ?? ccwCodexConfig;
    const currentTools = currentConfig.enabledTools;
    const previousConfig = queryClient.getQueryData<CcwMcpConfig>(['ccwMcpConfigCodex']);

    const updatedTools = enabled
      ? (currentTools.includes(tool) ? currentTools : [...currentTools, tool])
      : currentTools.filter((t) => t !== tool);

    queryClient.setQueryData(['ccwMcpConfigCodex'], (old: CcwMcpConfig | undefined) => {
      if (!old) return old;
      return { ...old, enabledTools: updatedTools };
    });

    try {
      await updateCcwConfigForCodex({ ...currentConfig, enabledTools: updatedTools });
    } catch (error) {
      notifications.error(formatMessage({ id: 'mcp.actions.toggle.error' }), error instanceof Error ? error.message : String(error));
      console.error('Failed to toggle CCW tool (Codex):', error);
      queryClient.setQueryData(['ccwMcpConfigCodex'], previousConfig);
    }
    ccwMcpCodexQuery.refetch();
  };

  const handleUpdateCcwConfigCodex = async (config: Partial<CcwMcpConfig>) => {
    const currentConfig = queryClient.getQueryData<CcwMcpConfig>(['ccwMcpConfigCodex']) ?? ccwCodexConfig;
    const previousConfig = queryClient.getQueryData<CcwMcpConfig>(['ccwMcpConfigCodex']);

    queryClient.setQueryData(['ccwMcpConfigCodex'], (old: CcwMcpConfig | undefined) => {
      if (!old) return old;
      return { ...old, ...config };
    });

    try {
      await updateCcwConfigForCodex({ ...currentConfig, ...config });
    } catch (error) {
      notifications.error(formatMessage({ id: 'mcp.actions.update.error' }), error instanceof Error ? error.message : String(error));
      console.error('Failed to update CCW config (Codex):', error);
      queryClient.setQueryData(['ccwMcpConfigCodex'], previousConfig);
    }
    ccwMcpCodexQuery.refetch();
  };

  const handleCcwInstallCodex = () => {
    ccwMcpCodexQuery.refetch();
  };

  // Template handlers
  const handleInstallTemplate = (template: any) => {
    const serverConfig = template?.serverConfig ?? {};
    const isHttp =
      serverConfig.type === 'http' ||
      serverConfig.transport === 'http' ||
      typeof serverConfig.url === 'string';

    if (isHttp) {
      setEditingServer({
        name: template.name,
        transport: 'http',
        url: serverConfig.url ?? '',
        scope: 'project',
        enabled: true,
      });
    } else {
      setEditingServer({
        name: template.name,
        transport: 'stdio',
        command: serverConfig.command,
        args: serverConfig.args || [],
        env: serverConfig.env,
        scope: 'project',
        enabled: true,
      });
    }
    setDialogOpen(true);
  };

  const handleSaveServerAsTemplate = (server: McpServer) => {
    setServerToSaveAsTemplate(server);
    setSaveTemplateDialogOpen(true);
  };

  const handleSaveAsTemplate = async (
    name: string,
    category: string,
    description: string,
    serverConfig: { command: string; args: string[]; env: Record<string, string> },
  ) => {
    try {
      const result = await saveMcpTemplate({
        name,
        description: description || undefined,
        category: category || 'custom',
        serverConfig: {
          command: serverConfig.command,
          args: serverConfig.args.length > 0 ? serverConfig.args : undefined,
          env: Object.keys(serverConfig.env).length > 0 ? serverConfig.env : undefined,
        },
      });
      if (result.success) {
        notifications.success(
          formatMessage({ id: 'mcp.templates.feedback.saveSuccess' }),
          name
        );
        setSaveTemplateDialogOpen(false);
        setServerToSaveAsTemplate(undefined);
      } else {
        notifications.error(
          formatMessage({ id: 'mcp.templates.feedback.saveError' }),
          result.error || ''
        );
      }
    } catch (error) {
      notifications.error(
        formatMessage({ id: 'mcp.templates.feedback.saveError' }),
        error instanceof Error ? error.message : String(error)
      );
    }
  };

  // Codex MCP handlers
  const handleCodexRemove = async (serverName: string) => {
    try {
      await codexRemoveServer(serverName);
      notifications.success(
        formatMessage({ id: 'mcp.actions.delete' }),
        serverName
      );
      codexQuery.refetch();
    } catch (error) {
      console.error('Failed to remove Codex MCP server:', error);
      notifications.error(
        formatMessage({ id: 'mcp.actions.delete' }),
        error instanceof Error ? error.message : String(error)
      );
    }
  };

  const handleCodexToggle = async (serverName: string, enabled: boolean) => {
    try {
      await codexToggleServer(serverName, enabled);
      codexQuery.refetch();
    } catch (error) {
      notifications.error(formatMessage({ id: 'mcp.actions.toggle.error' }), error instanceof Error ? error.message : String(error));
      console.error('Failed to toggle Codex MCP server:', error);
    }
  };

  // Filter servers by search query
  const filteredServers = servers.filter((s) => {
    const query = searchQuery.toLowerCase();
    const nameMatch = s.name.toLowerCase().includes(query);
    // For STDIO servers, search in command; for HTTP servers, search in url
    const transportMatch = isHttpMcpServer(s)
      ? s.url?.toLowerCase().includes(query)
      : s.command?.toLowerCase().includes(query);
    return nameMatch || transportMatch;
  });

  // Filter Codex servers by search query
  const codexServers = codexQuery.data?.servers ?? [];
  const codexConfigPath = codexQuery.data?.configPath ?? '';
  const filteredCodexServers = codexServers.filter((s) => {
    const query = searchQuery.toLowerCase();
    const nameMatch = s.name.toLowerCase().includes(query);
    // For STDIO servers, search in command; for HTTP servers, search in url
    const transportMatch = isHttpMcpServer(s)
      ? s.url?.toLowerCase().includes(query)
      : s.command?.toLowerCase().includes(query);
    return nameMatch || transportMatch;
  });

  const currentServers = cliMode === 'codex' ? filteredCodexServers : filteredServers;
  const currentExpanded = cliMode === 'codex' ? codexExpandedServers : expandedServers;
  const currentToggleExpand = cliMode === 'codex' ? toggleCodexExpand : toggleExpand;
  const currentIsLoading = cliMode === 'codex' ? codexQuery.isLoading : isLoading;
  const currentIsFetching = cliMode === 'codex' ? codexQuery.isFetching : isFetching;
  const currentRefetch = cliMode === 'codex' ? (() => codexQuery.refetch()) : refetch;

  return (
    <div className="space-y-6">
      {/* Page Header */}
      <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
        <div className="flex items-center gap-3">
          <div>
            <h1 className="text-2xl font-bold text-foreground flex items-center gap-2">
              <Server className="w-6 h-6 text-primary" />
              {formatMessage({ id: 'mcp.title' })}
            </h1>
            <p className="text-muted-foreground mt-1">
              {formatMessage({ id: 'mcp.description' })}
            </p>
          </div>
          {/* CLI Mode Badge Switcher */}
          <div className="ml-3 flex-shrink-0">
            <CliModeToggle
              currentMode={cliMode}
              onModeChange={handleModeChange}
              codexConfigPath={codexConfigPath}
            />
          </div>
        </div>
        <div className="flex gap-2">
          <Button variant="outline" onClick={() => currentRefetch()} disabled={currentIsFetching}>
            <RefreshCw className={cn('w-4 h-4 mr-2', currentIsFetching && 'animate-spin')} />
            {formatMessage({ id: 'common.actions.refresh' })}
          </Button>
          {cliMode === 'claude' && (
            <Button onClick={handleAddClick}>
              <Plus className="w-4 h-4 mr-2" />
              {formatMessage({ id: 'mcp.actions.add' })}
            </Button>
          )}
        </div>
      </div>

      {/* Tabbed Interface */}
      <TabsNavigation
        value={activeTab}
        onValueChange={(value) => setActiveTab(value as 'templates' | 'servers' | 'cross-cli')}
        tabs={[
          { value: 'templates', label: formatMessage({ id: 'mcp.tabs.templates' }) },
          { value: 'servers', label: formatMessage({ id: 'mcp.tabs.servers' }) },
          { value: 'cross-cli', label: formatMessage({ id: 'mcp.tabs.crossCli' }) },
        ]}
      />

      {/* Tab Content: Templates */}
      {activeTab === 'templates' && (
        <div className="mt-4 space-y-4">
          {/* Recommended MCP Servers */}
          <RecommendedMcpSection onInstallComplete={() => refetch()} />

          {/* Templates Section */}
          <McpTemplatesSection
            onInstallTemplate={handleInstallTemplate}
            onSaveAsTemplate={handleSaveAsTemplate}
          />
        </div>
      )}

      {/* Tab Content: Servers */}
      {activeTab === 'servers' && (
        <div className="mt-4 space-y-4">
          {/* Windows Compatibility Warning */}
          <WindowsCompatibilityWarning />

          {/* Stats Cards - Claude mode only */}
      {cliMode === 'claude' && (
        <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
          <Card className="p-4">
            <div className="flex items-center gap-2">
              <Server className="w-5 h-5 text-primary" />
              <span className="text-2xl font-bold">{totalCount}</span>
            </div>
            <p className="text-sm text-muted-foreground mt-1">{formatMessage({ id: 'mcp.stats.total' })}</p>
          </Card>
          <Card className="p-4">
            <div className="flex items-center gap-2">
              <Power className="w-5 h-5 text-green-600" />
              <span className="text-2xl font-bold">{enabledCount}</span>
            </div>
            <p className="text-sm text-muted-foreground mt-1">{formatMessage({ id: 'mcp.stats.enabled' })}</p>
          </Card>
          <Card className="p-4">
            <div className="flex items-center gap-2">
              <Globe className="w-5 h-5 text-info" />
              <span className="text-2xl font-bold">{globalServers.length}</span>
            </div>
            <p className="text-sm text-muted-foreground mt-1">{formatMessage({ id: 'mcp.stats.global' })}</p>
          </Card>
          <Card className="p-4">
            <div className="flex items-center gap-2">
              <Folder className="w-5 h-5 text-warning" />
              <span className="text-2xl font-bold">{projectServers.length}</span>
            </div>
            <p className="text-sm text-muted-foreground mt-1">{formatMessage({ id: 'mcp.stats.project' })}</p>
          </Card>
        </div>
      )}

      {/* Filters and Search - Claude mode only */}
      {cliMode === 'claude' && (
        <div className="flex flex-col sm:flex-row gap-3">
          <div className="relative flex-1">
            <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
            <Input
              placeholder={formatMessage({ id: 'mcp.filters.searchPlaceholder' })}
              value={searchQuery}
              onChange={(e) => setSearchQuery(e.target.value)}
              className="pl-9"
            />
          </div>
          <div className="flex gap-2">
            <Button
              variant={scopeFilter === 'all' ? 'default' : 'outline'}
              size="sm"
              onClick={() => setScopeFilter('all')}
            >
              {formatMessage({ id: 'mcp.filters.all' })}
            </Button>
            <Button
              variant={scopeFilter === 'global' ? 'default' : 'outline'}
              size="sm"
              onClick={() => setScopeFilter('global')}
            >
              <Globe className="w-4 h-4 mr-1" />
              {formatMessage({ id: 'mcp.scope.global' })}
            </Button>
            <Button
              variant={scopeFilter === 'project' ? 'default' : 'outline'}
              size="sm"
              onClick={() => setScopeFilter('project')}
            >
              <Folder className="w-4 h-4 mr-1" />
              {formatMessage({ id: 'mcp.scope.project' })}
            </Button>
          </div>
        </div>
      )}

      {/* Codex mode search only */}
      {cliMode === 'codex' && (
        <div className="relative flex-1">
          <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
          <Input
            placeholder={formatMessage({ id: 'mcp.filters.searchPlaceholder' })}
            value={searchQuery}
            onChange={(e) => setSearchQuery(e.target.value)}
            className="pl-9"
          />
        </div>
      )}

      {/* CCW Tools MCP Card */}
      {cliMode === 'claude' && (
        <CcwToolsMcpCard
          isInstalled={ccwConfig.isInstalled}
          enabledTools={ccwConfig.enabledTools}
          projectRoot={ccwConfig.projectRoot}
          allowedDirs={ccwConfig.allowedDirs}
          enableSandbox={ccwConfig.enableSandbox}
          onToggleTool={handleToggleCcwTool}
          onUpdateConfig={handleUpdateCcwConfig}
          onInstall={handleCcwInstall}
          installedScopes={ccwConfig.installedScopes}
          onInstallToScope={handleCcwInstallToScope}
          onUninstallScope={handleCcwUninstallFromScope}
        />
      )}
      {cliMode === 'codex' && (
        <CcwToolsMcpCard
          target="codex"
          isInstalled={ccwCodexConfig.isInstalled}
          enabledTools={ccwCodexConfig.enabledTools}
          projectRoot={ccwCodexConfig.projectRoot}
          allowedDirs={ccwCodexConfig.allowedDirs}
          enableSandbox={ccwCodexConfig.enableSandbox}
          onToggleTool={handleToggleCcwToolCodex}
          onUpdateConfig={handleUpdateCcwConfigCodex}
          onInstall={handleCcwInstallCodex}
        />
      )}

      {/* Servers List */}
      {currentIsLoading ? (
        <div className="space-y-3">
          {[1, 2, 3, 4].map((i) => (
            <div key={i} className="h-24 bg-muted animate-pulse rounded-lg" />
          ))}
        </div>
      ) : currentServers.length === 0 ? (
        <Card className="p-8 text-center">
          <Server className="w-12 h-12 mx-auto text-muted-foreground/50" />
          <h3 className="mt-4 text-lg font-medium text-foreground">{formatMessage({ id: 'mcp.emptyState.title' })}</h3>
          <p className="mt-2 text-muted-foreground">
            {formatMessage({ id: 'mcp.emptyState.message' })}
          </p>
        </Card>
      ) : (
        <div className="space-y-3">
          {currentServers.map((server) => (
            cliMode === 'codex' ? (
              <CodexMcpEditableCard
                key={`${server.name}-${server.scope}`}
                server={server as McpServer}
                enabled={server.enabled}
                isExpanded={currentExpanded.has(server.name)}
                onToggleExpand={() => currentToggleExpand(server.name)}
                isEditable={true}
                onRemove={handleCodexRemove}
                onToggle={handleCodexToggle}
              />
            ) : (
              <McpServerCard
                key={`${server.name}-${server.scope}`}
                server={server}
                isExpanded={currentExpanded.has(`${server.name}-${server.scope}`)}
                onToggleExpand={() => currentToggleExpand(`${server.name}-${server.scope}`)}
                onToggle={handleToggle}
                onEdit={handleEdit}
                onDelete={handleDelete}
                onSaveAsTemplate={handleSaveServerAsTemplate}
                conflictInfo={conflictMap.get(server.name)}
              />
            )
          ))}
        </div>
      )}
        </div>
      )}

      {/* Tab Content: Cross-CLI */}
      {activeTab === 'cross-cli' && (
        <div className="mt-4 space-y-6">
          {/* Section 1: Claude ↔ Codex 同步 */}
          <section>
            <div className="flex items-center gap-2 mb-3">
              <RefreshCw className="w-4 h-4 text-muted-foreground" />
              <h3 className="text-sm font-medium text-foreground">
                {formatMessage({ id: 'mcp.sync.title' })}
              </h3>
            </div>
            <Card className="p-4">
              <CrossCliSyncPanel onSuccess={() => refetch()} />
            </Card>
          </section>

          {/* Section 2: 项目概览 */}
          <section>
            <div className="flex items-center gap-2 mb-3">
              <Folder className="w-4 h-4 text-muted-foreground" />
              <h3 className="text-sm font-medium text-foreground">
                {formatMessage({ id: 'mcp.projects.title' })}
              </h3>
            </div>
            <p className="text-xs text-muted-foreground mb-3">
              {formatMessage({ id: 'mcp.projects.description' })}
            </p>
            <AllProjectsTable
              maxProjects={10}
              onProjectClick={(path) => console.log('Open project:', path)}
              onOpenNewWindow={(path) => window.open(`/?project=${encodeURIComponent(path)}`, '_blank')}
            />
          </section>

          {/* Section 3: 跨项目导入 */}
          <section>
            <div className="flex items-center gap-2 mb-3">
              <Globe className="w-4 h-4 text-muted-foreground" />
              <h3 className="text-sm font-medium text-foreground">
                {formatMessage({ id: 'mcp.crossProject.title' })}
              </h3>
            </div>
            <p className="text-xs text-muted-foreground mb-3">
              {formatMessage({ id: 'mcp.crossProject.description' })}
            </p>
            <OtherProjectsSection
              onImportSuccess={(serverName, sourceProject) => {
                console.log('Imported server:', serverName, 'from:', sourceProject);
                refetch();
              }}
            />
          </section>
        </div>
      )}

      {/* Add/Edit Dialog - Claude mode only (shared across tabs) */}
      {cliMode === 'claude' && (
        <McpServerDialog
          mode={editingServer ? 'edit' : 'add'}
          server={editingServer}
          open={dialogOpen}
          onClose={handleDialogClose}
          onSave={handleDialogSave}
        />
      )}

      {/* Save as Template Dialog */}
      <TemplateSaveDialog
        open={saveTemplateDialogOpen}
        onClose={() => {
          setSaveTemplateDialogOpen(false);
          setServerToSaveAsTemplate(undefined);
        }}
        onSave={handleSaveAsTemplate}
        defaultName={serverToSaveAsTemplate?.name}
        defaultCommand={
          serverToSaveAsTemplate && isStdioMcpServer(serverToSaveAsTemplate)
            ? serverToSaveAsTemplate.command
            : undefined
        }
        defaultArgs={
          serverToSaveAsTemplate && isStdioMcpServer(serverToSaveAsTemplate)
            ? serverToSaveAsTemplate.args
            : undefined
        }
        defaultEnv={
          serverToSaveAsTemplate && isStdioMcpServer(serverToSaveAsTemplate)
            ? (serverToSaveAsTemplate.env as Record<string, string>)
            : undefined
        }
      />
    </div>
  );
}

export default McpManagerPage;
