import React, { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { CLI_NOT_FOUND_MARKER } from '@shared/constants/cli';

interface StoreState {
  mcpBrowseCatalog: {
    id: string;
    name: string;
    description: string;
    source: 'official' | 'glama';
    installSpec: null;
    envVars: [];
    tools: [];
    requiresAuth: boolean;
  }[];
  mcpBrowseNextCursor?: string;
  mcpBrowseLoading: boolean;
  mcpBrowseError: string | null;
  mcpBrowse: ReturnType<typeof vi.fn>;
  mcpInstalledServers: { name: string; scope: 'local' | 'user' | 'project' }[];
  mcpInstalledServersByProjectPath?: Record<
    string,
    { name: string; scope: 'local' | 'user' | 'project' }[]
  >;
  fetchMcpGitHubStars: ReturnType<typeof vi.fn>;
  mcpDiagnostics: Record<string, never>;
  mcpDiagnosticsByProjectPath?: Record<string, Record<string, never>>;
  mcpDiagnosticsLoading: boolean;
  mcpDiagnosticsLoadingByProjectPath?: Record<string, boolean>;
  mcpDiagnosticsError: string | null;
  mcpDiagnosticsErrorByProjectPath?: Record<string, string | null>;
  mcpDiagnosticsLastCheckedAt: number | null;
  mcpDiagnosticsLastCheckedAtByProjectPath?: Record<string, number | null>;
  runMcpDiagnostics: ReturnType<typeof vi.fn>;
  cliStatusLoading: boolean;
  cliStatus?: {
    flavor?: 'claude' | 'agent_teams_orchestrator';
    installed?: boolean;
    binaryPath?: string | null;
    launchError?: string | null;
  } | null;
}

const storeState = {} as StoreState;

vi.mock('@renderer/store', () => ({
  useStore: (selector: (state: StoreState) => unknown) => selector(storeState),
}));

vi.mock('zustand/react/shallow', () => ({
  useShallow: (selector: unknown) => selector,
}));

vi.mock('@renderer/components/ui/badge', () => ({
  Badge: ({ children }: React.PropsWithChildren) => React.createElement('span', null, children),
}));

vi.mock('@renderer/components/ui/button', () => ({
  Button: ({
    children,
    onClick,
    type = 'button',
    disabled,
  }: React.PropsWithChildren<{
    onClick?: () => void;
    type?: 'button' | 'submit' | 'reset';
    disabled?: boolean;
  }>) =>
    React.createElement(
      'button',
      {
        type,
        disabled,
        onClick,
      },
      children
    ),
}));

vi.mock('@renderer/components/ui/select', () => ({
  Select: ({ children }: React.PropsWithChildren) => React.createElement('div', null, children),
  SelectTrigger: ({ children }: React.PropsWithChildren) =>
    React.createElement('button', { type: 'button' }, children),
  SelectValue: () => React.createElement('span', null, 'select-value'),
  SelectContent: ({ children }: React.PropsWithChildren) =>
    React.createElement('div', null, children),
  SelectItem: ({ children }: React.PropsWithChildren<{ value: string }>) =>
    React.createElement('button', { type: 'button' }, children),
}));

vi.mock('@renderer/components/extensions/common/SearchInput', () => ({
  SearchInput: ({ value, onChange }: { value: string; onChange: (value: string) => void }) =>
    React.createElement('input', {
      value,
      onChange: (event: React.ChangeEvent<HTMLInputElement>) => onChange(event.target.value),
    }),
}));

vi.mock('@renderer/components/extensions/mcp/McpServerCard', () => ({
  McpServerCard: ({ server }: { server: { id: string; name: string } }) =>
    React.createElement(
      'div',
      { 'data-testid': 'mcp-card', 'data-server-id': server.id },
      server.name
    ),
}));

vi.mock('@renderer/components/extensions/mcp/McpServerDetailDialog', () => ({
  McpServerDetailDialog: ({ open }: { open: boolean }) =>
    open ? React.createElement('div', { 'data-testid': 'mcp-detail' }) : null,
}));

vi.mock('@renderer/utils/formatters', () => ({
  formatRelativeTime: () => 'just now',
}));

vi.mock('lucide-react', () => {
  const Icon = (props: React.SVGProps<SVGSVGElement>) => React.createElement('svg', props);
  return {
    AlertTriangle: Icon,
    RefreshCw: Icon,
    Search: Icon,
    Server: Icon,
  };
});

import { McpServersPanel } from '@renderer/components/extensions/mcp/McpServersPanel';

describe('McpServersPanel initial browse loading', () => {
  beforeEach(() => {
    vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
    storeState.mcpBrowseCatalog = [];
    storeState.mcpBrowseNextCursor = undefined;
    storeState.mcpBrowseLoading = false;
    storeState.mcpBrowseError = null;
    storeState.mcpBrowse = vi.fn();
    storeState.mcpInstalledServers = [];
    storeState.mcpInstalledServersByProjectPath = undefined;
    storeState.fetchMcpGitHubStars = vi.fn();
    storeState.mcpDiagnostics = {};
    storeState.mcpDiagnosticsByProjectPath = undefined;
    storeState.mcpDiagnosticsLoading = false;
    storeState.mcpDiagnosticsLoadingByProjectPath = undefined;
    storeState.mcpDiagnosticsError = null;
    storeState.mcpDiagnosticsErrorByProjectPath = undefined;
    storeState.mcpDiagnosticsLastCheckedAt = null;
    storeState.mcpDiagnosticsLastCheckedAtByProjectPath = undefined;
    storeState.runMcpDiagnostics = vi.fn();
    storeState.cliStatusLoading = false;
    storeState.cliStatus = {
      flavor: 'claude',
      installed: true,
      binaryPath: '/usr/local/bin/claude',
      launchError: null,
    };
  });

  afterEach(() => {
    document.body.innerHTML = '';
    vi.unstubAllGlobals();
  });

  it('loads the catalog once on first mount when browse state is empty', async () => {
    const host = document.createElement('div');
    document.body.appendChild(host);
    const root = createRoot(host);

    await act(async () => {
      root.render(
        React.createElement(McpServersPanel, {
          projectPath: null,
          mcpSearchQuery: '',
          mcpSearch: vi.fn(),
          mcpSearchResults: [],
          mcpSearchLoading: false,
          mcpSearchWarnings: [],
          selectedMcpServerId: null,
          setSelectedMcpServerId: vi.fn(),
        })
      );
      await Promise.resolve();
    });

    expect(storeState.mcpBrowse).toHaveBeenCalledTimes(1);
    expect(storeState.runMcpDiagnostics).toHaveBeenCalledTimes(1);

    await act(async () => {
      root.unmount();
      await Promise.resolve();
    });
  });

  it('does not auto-retry browse after an error with an empty catalog', async () => {
    const host = document.createElement('div');
    document.body.appendChild(host);
    const root = createRoot(host);

    await act(async () => {
      root.render(
        React.createElement(McpServersPanel, {
          projectPath: null,
          mcpSearchQuery: '',
          mcpSearch: vi.fn(),
          mcpSearchResults: [],
          mcpSearchLoading: false,
          mcpSearchWarnings: [],
          selectedMcpServerId: null,
          setSelectedMcpServerId: vi.fn(),
        })
      );
      await Promise.resolve();
    });

    expect(storeState.mcpBrowse).toHaveBeenCalledTimes(1);

    storeState.mcpBrowseError = 'Registry unavailable';
    await act(async () => {
      root.render(
        React.createElement(McpServersPanel, {
          projectPath: null,
          mcpSearchQuery: '',
          mcpSearch: vi.fn(),
          mcpSearchResults: [],
          mcpSearchLoading: false,
          mcpSearchWarnings: [],
          selectedMcpServerId: null,
          setSelectedMcpServerId: vi.fn(),
        })
      );
      await Promise.resolve();
    });

    expect(storeState.mcpBrowse).toHaveBeenCalledTimes(1);

    await act(async () => {
      root.unmount();
      await Promise.resolve();
    });
  });

  it('uses truthful diagnostics copy instead of suggesting a hard-coded CLI command', async () => {
    storeState.mcpBrowseCatalog = [
      {
        id: 'context7',
        name: 'Context7',
        description: 'Docs MCP',
        source: 'official',
        installSpec: null,
        envVars: [],
        tools: [],
        requiresAuth: false,
      },
    ];

    const host = document.createElement('div');
    document.body.appendChild(host);
    const root = createRoot(host);

    await act(async () => {
      root.render(
        React.createElement(McpServersPanel, {
          projectPath: null,
          mcpSearchQuery: '',
          mcpSearch: vi.fn(),
          mcpSearchResults: [],
          mcpSearchLoading: false,
          mcpSearchWarnings: [],
          selectedMcpServerId: null,
          setSelectedMcpServerId: vi.fn(),
        })
      );
      await Promise.resolve();
    });

    expect(host.textContent).toContain('Run diagnostics from this page');
    expect(host.textContent).not.toContain('claude-multimodel mcp diagnose');
    expect(host.textContent).not.toContain('claude mcp list');

    await act(async () => {
      root.unmount();
      await Promise.resolve();
    });
  });

  it('uses runtime-aware missing-runtime copy for multimodel diagnostics failures', async () => {
    storeState.mcpDiagnosticsError = `${CLI_NOT_FOUND_MARKER}: missing runtime`;
    storeState.cliStatus = {
      flavor: 'agent_teams_orchestrator',
    };

    const host = document.createElement('div');
    document.body.appendChild(host);
    const root = createRoot(host);

    await act(async () => {
      root.render(
        React.createElement(McpServersPanel, {
          projectPath: null,
          mcpSearchQuery: '',
          mcpSearch: vi.fn(),
          mcpSearchResults: [],
          mcpSearchLoading: false,
          mcpSearchWarnings: [],
          selectedMcpServerId: null,
          setSelectedMcpServerId: vi.fn(),
        })
      );
      await Promise.resolve();
    });

    expect(host.textContent).toContain('Multimodel runtime not available');
    expect(host.textContent).toContain('MCP health checks require Multimodel runtime');
    expect(host.textContent).not.toContain('Claude CLI not installed');

    await act(async () => {
      root.unmount();
      await Promise.resolve();
    });
  });

  it('does not auto-run diagnostics when the configured runtime is unavailable', async () => {
    storeState.cliStatus = {
      flavor: 'agent_teams_orchestrator',
      installed: false,
      binaryPath: null,
      launchError: null,
    };

    const host = document.createElement('div');
    document.body.appendChild(host);
    const root = createRoot(host);

    await act(async () => {
      root.render(
        React.createElement(McpServersPanel, {
          projectPath: null,
          mcpSearchQuery: '',
          mcpSearch: vi.fn(),
          mcpSearchResults: [],
          mcpSearchLoading: false,
          mcpSearchWarnings: [],
          selectedMcpServerId: null,
          setSelectedMcpServerId: vi.fn(),
        })
      );
      await Promise.resolve();
    });

    expect(storeState.runMcpDiagnostics).not.toHaveBeenCalled();
    expect(host.textContent).toContain(
      'The configured runtime is required. Install or repair it from the Dashboard.'
    );
    const checkStatusButton = Array.from(host.querySelectorAll('button')).find((button) =>
      button.textContent?.includes('Check Status')
    );
    expect(checkStatusButton).toBeDefined();
    expect((checkStatusButton!).disabled).toBe(true);

    await act(async () => {
      root.unmount();
      await Promise.resolve();
    });
  });

  it('waits for runtime hydration before auto-running diagnostics', async () => {
    storeState.cliStatus = null;

    const host = document.createElement('div');
    document.body.appendChild(host);
    const root = createRoot(host);

    await act(async () => {
      root.render(
        React.createElement(McpServersPanel, {
          projectPath: null,
          mcpSearchQuery: '',
          mcpSearch: vi.fn(),
          mcpSearchResults: [],
          mcpSearchLoading: false,
          mcpSearchWarnings: [],
          selectedMcpServerId: null,
          setSelectedMcpServerId: vi.fn(),
        })
      );
      await Promise.resolve();
    });

    expect(storeState.runMcpDiagnostics).not.toHaveBeenCalled();
    expect(host.textContent).toContain('Checking runtime availability...');

    await act(async () => {
      root.unmount();
      await Promise.resolve();
    });
  });

  it('renders provider-neutral waiting copy while diagnostics are still running', async () => {
    storeState.mcpDiagnosticsLoading = true;

    const host = document.createElement('div');
    document.body.appendChild(host);
    const root = createRoot(host);

    await act(async () => {
      root.render(
        React.createElement(McpServersPanel, {
          projectPath: null,
          mcpSearchQuery: '',
          mcpSearch: vi.fn(),
          mcpSearchResults: [],
          mcpSearchLoading: false,
          mcpSearchWarnings: [],
          selectedMcpServerId: null,
          setSelectedMcpServerId: vi.fn(),
        })
      );
      await Promise.resolve();
    });

    expect(host.textContent).toContain('Waiting for diagnostics results...');

    await act(async () => {
      root.unmount();
      await Promise.resolve();
    });
  });

  it('uses the effective runtime status override for diagnostics gating during background refresh', async () => {
    storeState.cliStatus = null;
    storeState.cliStatusLoading = true;

    const host = document.createElement('div');
    document.body.appendChild(host);
    const root = createRoot(host);

    await act(async () => {
      root.render(
        React.createElement(McpServersPanel, {
          projectPath: null,
          mcpSearchQuery: '',
          mcpSearch: vi.fn(),
          mcpSearchResults: [],
          mcpSearchLoading: false,
          mcpSearchWarnings: [],
          selectedMcpServerId: null,
          setSelectedMcpServerId: vi.fn(),
          cliStatus: {
            flavor: 'agent_teams_orchestrator',
            displayName: 'Multimodel runtime',
            installed: true,
            authLoggedIn: false,
            binaryPath: '/usr/local/bin/agent-teams',
            launchError: null,
            providers: [],
          },
          cliStatusLoading: false,
        })
      );
      await Promise.resolve();
    });

    expect(storeState.runMcpDiagnostics).toHaveBeenCalledTimes(1);
    expect(host.textContent).not.toContain('Checking runtime availability...');
    expect(host.textContent).not.toContain('The configured runtime is required.');

    await act(async () => {
      root.unmount();
      await Promise.resolve();
    });
  });

  it('does not block diagnostics when a usable runtime status already exists during background refresh', async () => {
    storeState.cliStatus = {
      flavor: 'agent_teams_orchestrator',
      installed: true,
      binaryPath: '/usr/local/bin/agent-teams',
      launchError: null,
    };
    storeState.cliStatusLoading = true;

    const host = document.createElement('div');
    document.body.appendChild(host);
    const root = createRoot(host);

    await act(async () => {
      root.render(
        React.createElement(McpServersPanel, {
          projectPath: null,
          mcpSearchQuery: '',
          mcpSearch: vi.fn(),
          mcpSearchResults: [],
          mcpSearchLoading: false,
          mcpSearchWarnings: [],
          selectedMcpServerId: null,
          setSelectedMcpServerId: vi.fn(),
        })
      );
      await Promise.resolve();
    });

    expect(storeState.runMcpDiagnostics).toHaveBeenCalledTimes(1);
    expect(host.textContent).not.toContain('Checking runtime status...');

    await act(async () => {
      root.unmount();
      await Promise.resolve();
    });
  });
});
