import { act, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
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/gsc-connections',
      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;
    }) => (
      <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 GscConnectionsIndex from './Index';

const makePagination = <T,>(data: T[]) => ({
  data,
  current_page: 1,
  per_page: 25,
  total: data.length,
  last_page: 1,
  from: data.length > 0 ? 1 : 0,
  to: data.length,
  links: [],
});

const makeConnection = (overrides = {}) => ({
  id: 1,
  site_id: 5,
  site_name: 'My Blog',
  site_domain: 'myblog.com',
  user_name: 'Alice Example',
  user_email: 'alice@example.com',
  gsc_property_url: 'sc-domain:myblog.com',
  sync_status: 'synced',
  last_synced_at: '2026-01-15T10:00:00Z',
  last_sync_failed_at: null,
  sync_error: null,
  token_expired: false,
  failed_refresh_attempts: 0,
  created_at: '2026-01-01T00:00:00Z',
  ...overrides,
});

const defaultStats = { total: 1, synced: 1, syncing: 0, failed: 0, token_expired: 0 };

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

  it('renders the GSC Connections heading', () => {
    render(
      <GscConnectionsIndex
        connections={makePagination([makeConnection()])}
        stats={defaultStats}
        filters={{}}
      />,
    );
    expect(screen.getByRole('heading', { name: /GSC Connections/i, level: 1 })).toBeInTheDocument();
  });

  it('renders site name in row', () => {
    render(
      <GscConnectionsIndex
        connections={makePagination([makeConnection()])}
        stats={defaultStats}
        filters={{}}
      />,
    );
    expect(screen.getByText('My Blog')).toBeInTheDocument();
  });

  it('renders GSC property URL', () => {
    render(
      <GscConnectionsIndex
        connections={makePagination([makeConnection()])}
        stats={defaultStats}
        filters={{}}
      />,
    );
    expect(screen.getByText('sc-domain:myblog.com')).toBeInTheDocument();
  });

  it('renders synced status badge', () => {
    render(
      <GscConnectionsIndex
        connections={makePagination([makeConnection()])}
        stats={defaultStats}
        filters={{}}
      />,
    );
    expect(screen.getByText('synced')).toBeInTheDocument();
  });

  it('renders failed status badge for failed connection', () => {
    render(
      <GscConnectionsIndex
        connections={makePagination([makeConnection({ sync_status: 'failed', sync_error: 'Token revoked' })])}
        stats={{ ...defaultStats, synced: 0, failed: 1 }}
        filters={{}}
      />,
    );
    expect(screen.getByText('failed')).toBeInTheDocument();
  });

  it('shows token_expired badge when applicable', () => {
    render(
      <GscConnectionsIndex
        connections={makePagination([makeConnection({ token_expired: true, sync_status: 'failed' })])}
        stats={{ ...defaultStats, synced: 0, token_expired: 1 }}
        filters={{}}
      />,
    );
    // Token expired connections should show as failed or have an expired indicator
    expect(screen.getByText('My Blog')).toBeInTheDocument();
  });

  it('shows empty state when no connections', () => {
    render(
      <GscConnectionsIndex
        connections={makePagination([])}
        stats={{ total: 0, synced: 0, syncing: 0, failed: 0, token_expired: 0 }}
        filters={{}}
      />,
    );
    expect(screen.getByText('No GSC connections found')).toBeInTheDocument();
  });

  it('renders stat cards', () => {
    render(
      <GscConnectionsIndex
        connections={makePagination([makeConnection()])}
        stats={defaultStats}
        filters={{}}
      />,
    );
    expect(screen.getByText('Total')).toBeInTheDocument();
  });

  // ADMFE-02: useMutationButton adoption tests
  describe('SyncButton (useMutationButton)', () => {
    it('calls router.post for the correct connection when sync is triggered', async () => {
      const user = userEvent.setup();
      let capturedOptions: {
        onFinish?: () => void;
        onError?: (errors: Record<string, string>) => void;
      } = {};
      vi.mocked(router.post).mockImplementation((_url, _data, options) => {
        capturedOptions = (options ?? {}) as typeof capturedOptions;
      });

      render(
        <GscConnectionsIndex
          connections={makePagination([makeConnection({ id: 7 })])}
          stats={defaultStats}
          filters={{}}
        />,
      );

      // Open the dropdown — Radix DropdownMenuContent renders into a portal when open
      const actionsButton = screen.getByRole('button', { name: /GSC connection actions/i });
      await user.click(actionsButton);

      // After the click the content is portalled into the DOM
      const syncItem = await screen.findByText(/Trigger re-sync/i);
      await user.click(syncItem);

      expect(router.post).toHaveBeenCalledOnce();
      // Route mock returns the pattern; assert against the route name pattern used
      expect(router.post).toHaveBeenCalledWith(
        expect.stringContaining('gsc-connections'),
        {},
        expect.objectContaining({ onFinish: expect.any(Function), onError: expect.any(Function) }),
      );

      // Settle to avoid act() warnings
      act(() => {
        capturedOptions.onFinish?.();
      });
    });

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

      render(
        <GscConnectionsIndex
          connections={makePagination([makeConnection()])}
          stats={defaultStats}
          filters={{}}
        />,
      );

      // Open dropdown and click sync
      const actionsButton = screen.getByRole('button', { name: /GSC connection actions/i });
      await user.click(actionsButton);
      const syncItem = await screen.findByText(/Trigger re-sync/i);
      await user.click(syncItem);

      // Simulate an onError from the request lifecycle
      act(() => {
        capturedOptions.onError?.({ sync: 'GSC sync is already in progress.' });
      });

      await waitFor(() => {
        expect(screen.getByText('GSC sync is already in progress.')).toBeInTheDocument();
      });
    });

    it('clears error state after onFinish fires', async () => {
      const user = userEvent.setup();
      let capturedOptions: {
        onFinish?: () => void;
        onError?: (errors: Record<string, string>) => void;
      } = {};
      vi.mocked(router.post).mockImplementation((_url, _data, options) => {
        capturedOptions = (options ?? {}) as typeof capturedOptions;
      });

      render(
        <GscConnectionsIndex
          connections={makePagination([makeConnection()])}
          stats={defaultStats}
          filters={{}}
        />,
      );

      const actionsButton = screen.getByRole('button', { name: /GSC connection actions/i });
      await user.click(actionsButton);
      const syncItem = await screen.findByText(/Trigger re-sync/i);
      await user.click(syncItem);

      expect(router.post).toHaveBeenCalledOnce();

      // Settling via onFinish should not throw
      act(() => {
        capturedOptions.onFinish?.();
      });

      // After settle there is no residual error
      expect(screen.queryByText('GSC sync is already in progress.')).not.toBeInTheDocument();
    });
  });
});
