import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { toast } from 'sonner';
import { describe, it, expect, vi, beforeEach } from 'vitest';

vi.mock('@inertiajs/react', async () => {
  const actual = await vi.importActual('@inertiajs/react');
  return {
    ...actual,
    Head: ({ title }: { title: string }) => <title>{title}</title>,
    usePage: vi.fn(() => ({
      url: '/admin/circuit-breaker',
      props: {
        auth: { user: { name: 'Admin', email: 'admin@test.com', is_admin: true } },
        features: {
          billing: false,
          socialAuth: false,
          emailVerification: true,
          apiTokens: true,
          userSettings: true,
          notifications: false,
          admin: true,
        },
      },
    })),
    Link: ({
      children,
      href,
      ...rest
    }: {
      children: React.ReactNode;
      href: string;
      className?: string;
      [key: string]: unknown;
    }) => (
      <a href={href} {...rest}>
        {children}
      </a>
    ),
    router: {
      patch: vi.fn(),
      delete: vi.fn(),
      post: vi.fn(),
      get: vi.fn(),
      on: vi.fn(() => vi.fn()),
    },
  };
});

vi.mock('@/Components/theme/use-theme', () => ({
  useTheme: vi.fn(() => ({ theme: 'system', setTheme: vi.fn(), resolvedTheme: 'light' })),
}));

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

import { router } from '@inertiajs/react';

import CircuitBreaker from './CircuitBreaker';

const makeCircuit = (overrides = {}) => ({
  state: 'open' as const,
  failure_count: 5,
  failure_threshold: 5,
  last_failure_at: '2026-01-15T10:00:00Z',
  ...overrides,
});

const defaultCircuits = {
  gsc_api: makeCircuit(),
  openai_api: makeCircuit({ state: 'closed' as const, failure_count: 0 }),
};

describe('AdminCircuitBreaker', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('renders the Circuit Breaker heading', () => {
    render(<CircuitBreaker circuits={defaultCircuits} />);
    expect(screen.getByRole('heading', { name: /Circuit Breaker/i, level: 1 })).toBeInTheDocument();
  });

  it('renders service names', () => {
    render(<CircuitBreaker circuits={defaultCircuits} />);
    expect(screen.getByText('Google Search Console')).toBeInTheDocument();
    expect(screen.getByText('OpenAI API')).toBeInTheDocument();
  });

  it('shows empty state when no circuits', () => {
    render(<CircuitBreaker circuits={{}} />);
    expect(screen.getByText(/no circuits configured/i)).toBeInTheDocument();
  });

  it('disables Reset button for closed circuit with 0 failures', () => {
    render(<CircuitBreaker circuits={defaultCircuits} />);
    // OpenAI API is closed with 0 failures — its Reset button should be disabled
    const resetButtons = screen.getAllByRole('button', { name: /Reset/i });
    // The second reset button corresponds to openai_api (closed, 0 failures)
    // At least one reset button should be disabled
    const disabledButtons = resetButtons.filter((btn) => btn.hasAttribute('disabled'));
    expect(disabledButtons.length).toBeGreaterThanOrEqual(1);
  });

  // ADMFE-02: useMutationButton adoption tests
  describe('ResetButton (useMutationButton)', () => {
    /**
     * Helper: open the ConfirmDialog for the first enabled Reset button and
     * confirm, capturing router.post options.
     */
    async function triggerReset() {
      const resetButtons = screen.getAllByRole('button', { name: /Reset/i });
      const enabledReset = resetButtons.find((btn) => !btn.hasAttribute('disabled'));
      expect(enabledReset).toBeDefined();
      fireEvent.click(enabledReset!);

      // ConfirmDialog appears — wait for the dialog title
      expect(await screen.findByText(/Reset circuit breaker/i)).toBeInTheDocument();

      // The dialog has its own "Reset" confirm button — it is the last Reset button in DOM
      const allResets = screen.getAllByRole('button', { name: /Reset/i });
      const confirmBtn = allResets[allResets.length - 1];
      fireEvent.click(confirmBtn);
    }

    it('calls router.post to reset the circuit after confirm', async () => {
      let capturedOptions: {
        onSuccess?: () => void;
        onFinish?: () => void;
        onError?: (errors: Record<string, string>) => void;
      } = {};
      vi.mocked(router.post).mockImplementation((_url, _data, options) => {
        capturedOptions = (options ?? {}) as typeof capturedOptions;
      });

      render(<CircuitBreaker circuits={defaultCircuits} />);
      await triggerReset();

      await waitFor(() => {
        expect(router.post).toHaveBeenCalledOnce();
      });
      expect(router.post).toHaveBeenCalledWith(
        expect.stringContaining('gsc_api'),
        {},
        expect.objectContaining({ onFinish: expect.any(Function), onError: expect.any(Function) }),
      );

      act(() => {
        capturedOptions.onFinish?.();
      });
    });

    it('shows success toast after router.post calls onSuccess', async () => {
      let capturedOptions: {
        onSuccess?: () => void;
        onFinish?: () => void;
        onError?: (errors: Record<string, string>) => void;
      } = {};
      vi.mocked(router.post).mockImplementation((_url, _data, options) => {
        capturedOptions = (options ?? {}) as typeof capturedOptions;
      });

      render(<CircuitBreaker circuits={defaultCircuits} />);
      await triggerReset();

      await waitFor(() => expect(router.post).toHaveBeenCalledOnce());

      act(() => {
        capturedOptions.onSuccess?.();
      });

      expect(toast.success).toHaveBeenCalledWith(expect.stringContaining('Google Search Console'));

      act(() => {
        capturedOptions.onFinish?.();
      });
    });

    it('renders inline error when router.post calls onError', async () => {
      let capturedOptions: {
        onSuccess?: () => void;
        onFinish?: () => void;
        onError?: (errors: Record<string, string>) => void;
      } = {};
      vi.mocked(router.post).mockImplementation((_url, _data, options) => {
        capturedOptions = (options ?? {}) as typeof capturedOptions;
      });

      render(<CircuitBreaker circuits={defaultCircuits} />);
      await triggerReset();

      await waitFor(() => expect(router.post).toHaveBeenCalledOnce());

      act(() => {
        capturedOptions.onError?.({ reset: 'Service is not in a resettable state.' });
      });

      await waitFor(() => {
        expect(screen.getByText('Service is not in a resettable state.')).toBeInTheDocument();
      });
    });
  });
});
