import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { AutomationServiceIndicator } from '@/components/layout/AutomationServiceIndicator';
import {
  fetchAutomationServiceMonitorState,
  restartAutomationServiceFromDevServer,
  type AutomationServiceMonitorState,
} from '@/services/automationControlService';
import { toast } from 'sonner';

vi.mock('@/services/automationControlService', async () => {
  const actual = await vi.importActual<typeof import('@/services/automationControlService')>('@/services/automationControlService');
  return {
    ...actual,
    fetchAutomationServiceMonitorState: vi.fn(),
    restartAutomationServiceFromDevServer: vi.fn(),
  };
});

vi.mock('sonner', () => ({
  toast: {
    success: vi.fn(),
    error: vi.fn(),
  },
}));

describe('AutomationServiceIndicator', () => {
  let queryClient: QueryClient;

  const fetchAutomationServiceMonitorStateMock = vi.mocked(fetchAutomationServiceMonitorState);
  const restartAutomationServiceFromDevServerMock = vi.mocked(restartAutomationServiceFromDevServer);
  const toastSuccessMock = vi.mocked(toast.success);
  const toastErrorMock = vi.mocked(toast.error);
  const onlineState: AutomationServiceMonitorState = {
    status: 'online',
    canStart: true,
    message: '自动化服务在线',
    source: 'vite-dev-server',
    checkedAt: '2026-04-22T09:30:00.000Z',
  };

  beforeEach(() => {
    queryClient = new QueryClient({
      defaultOptions: {
        queries: {
          retry: false,
          gcTime: 0,
        },
      },
    });

    fetchAutomationServiceMonitorStateMock.mockResolvedValue(onlineState);
    restartAutomationServiceFromDevServerMock.mockResolvedValue({
      ...onlineState,
      status: 'starting',
      message: '已发送自动化服务重启请求',
      checkedAt: '2026-04-22T09:31:00.000Z',
    });
  });

  afterEach(() => {
    queryClient.clear();
    vi.clearAllMocks();
  });

  function renderIndicator() {
    return render(
      <QueryClientProvider client={queryClient}>
        <AutomationServiceIndicator />
      </QueryClientProvider>,
    );
  }

  it('requires confirmation before restarting the automation service', async () => {
    renderIndicator();

    const restartTrigger = await screen.findByRole('button', { name: '重启服务' });
    fireEvent.click(restartTrigger);

    expect(await screen.findByText('确认重启自动化服务')).toBeInTheDocument();
    expect(restartAutomationServiceFromDevServerMock).not.toHaveBeenCalled();

    fireEvent.click(screen.getByRole('button', { name: '取消' }));
    await waitFor(() => {
      expect(screen.queryByText('确认重启自动化服务')).not.toBeInTheDocument();
    });
    expect(restartAutomationServiceFromDevServerMock).not.toHaveBeenCalled();

    fireEvent.click(await screen.findByRole('button', { name: '重启服务' }));
    fireEvent.click(await screen.findByRole('button', { name: '确认重启' }));

    await waitFor(() => expect(restartAutomationServiceFromDevServerMock).toHaveBeenCalledTimes(1));
    await waitFor(() => expect(toastSuccessMock).toHaveBeenCalledWith('自动化服务在线'));
    expect(toastSuccessMock).not.toHaveBeenCalledWith('已发送自动化服务重启请求');
  });

  it('shows restart success only after the monitor confirms the API is online again', async () => {
    fetchAutomationServiceMonitorStateMock
      .mockResolvedValueOnce(onlineState)
      .mockResolvedValueOnce({
        ...onlineState,
        status: 'restart-sent',
        message: '已发送自动化服务重启请求',
        checkedAt: '2026-04-22T09:31:00.000Z',
      })
      .mockResolvedValueOnce({
        ...onlineState,
        message: '自动化服务已恢复在线',
        checkedAt: '2026-04-22T09:31:03.000Z',
      });

    renderIndicator();

    fireEvent.click(await screen.findByRole('button', { name: '重启服务' }));
    fireEvent.click(await screen.findByRole('button', { name: '确认重启' }));

    await waitFor(() => expect(restartAutomationServiceFromDevServerMock).toHaveBeenCalledTimes(1));
    expect(toastSuccessMock).not.toHaveBeenCalled();

    await queryClient.invalidateQueries({ queryKey: ['automation', 'service', 'monitor'] });

    await waitFor(() => expect(toastSuccessMock).toHaveBeenCalledWith('自动化服务已恢复在线'));
  });

  it('keeps waiting when the API is briefly offline during restart', async () => {
    fetchAutomationServiceMonitorStateMock
      .mockResolvedValueOnce(onlineState)
      .mockResolvedValueOnce({
        ...onlineState,
        status: 'offline',
        message: '自动化服务离线，目标地址 http://127.0.0.1:3030',
        checkedAt: '2026-04-22T09:31:01.000Z',
      })
      .mockResolvedValueOnce({
        ...onlineState,
        message: '自动化服务已恢复在线',
        checkedAt: '2026-04-22T09:31:03.000Z',
      });

    renderIndicator();

    fireEvent.click(await screen.findByRole('button', { name: '重启服务' }));
    fireEvent.click(await screen.findByRole('button', { name: '确认重启' }));
    await waitFor(() => expect(restartAutomationServiceFromDevServerMock).toHaveBeenCalledTimes(1));

    await waitFor(() => expect(screen.getByText('自动化服务离线')).toBeInTheDocument());
    expect(toastErrorMock).not.toHaveBeenCalled();
    expect(toastSuccessMock).not.toHaveBeenCalled();

    await queryClient.invalidateQueries({ queryKey: ['automation', 'service', 'monitor'] });

    await waitFor(() => expect(toastSuccessMock).toHaveBeenCalledWith('自动化服务已恢复在线'));
    expect(toastErrorMock).not.toHaveBeenCalled();
  });
});
