import { render, screen, fireEvent } from '@testing-library/react';
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/sessions',
      props: {
        auth: { user: { name: 'Admin', email: 'admin@test.com', admin_role: 'super' } },
        features: {
          billing: false,
          socialAuth: false,
          emailVerification: true,
          apiTokens: true,
          userSettings: true,
          notifications: false,
          onboarding: false,
          apiDocs: false,
          twoFactor: false,
          webhooks: 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 { usePage } from '@inertiajs/react';

import type { PaginatedResponse } from '@/types';

import SessionsIndex from './Index';

interface SessionRow {
  session_id: string;
  user_id: number;
  user_name: string;
  user_email: string;
  ip_address: string | null;
  last_activity: string;
}

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

const makeSession = (overrides: Partial<SessionRow> = {}): SessionRow => ({
  session_id: 'abc123',
  user_id: 1,
  user_name: 'Alice Example',
  user_email: 'alice@example.com',
  ip_address: '127.0.0.1',
  last_activity: new Date().toISOString(),
  ...overrides,
});

const defaultStats = { driver: 'database', total: 5, active_users: 3 };
const defaultFilters = { search: '', sort: 'last_activity', dir: 'desc' };

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

  it('renders page heading', () => {
    render(
      <SessionsIndex
        stats={defaultStats}
        sessions={makePagination([makeSession()])}
        filters={defaultFilters}
      />,
    );
    expect(screen.getAllByText('Session Manager').length).toBeGreaterThanOrEqual(1);
  });

  it('shows alert when driver is not database', () => {
    render(
      <SessionsIndex
        stats={{ driver: 'file', total: 0, active_users: 0 }}
        sessions={null}
        filters={defaultFilters}
      />,
    );
    expect(screen.getByText('Database driver required')).toBeInTheDocument();
  });

  it('renders session rows when driver is database', () => {
    render(
      <SessionsIndex
        stats={defaultStats}
        sessions={makePagination([makeSession()])}
        filters={defaultFilters}
      />,
    );
    expect(screen.getByText('Alice Example')).toBeInTheDocument();
    expect(screen.getByText('alice@example.com')).toBeInTheDocument();
  });

  it('shows null sessions as empty table', () => {
    render(
      <SessionsIndex stats={defaultStats} sessions={makePagination([])} filters={defaultFilters} />,
    );
    expect(screen.getByText('No sessions found')).toBeInTheDocument();
  });

  it('renders IP address for sessions with IP', () => {
    render(
      <SessionsIndex
        stats={defaultStats}
        sessions={makePagination([makeSession({ ip_address: '192.168.1.1' })])}
        filters={defaultFilters}
      />,
    );
    expect(screen.getByText('192.168.1.1')).toBeInTheDocument();
  });

  it('renders dash for sessions with null IP', () => {
    render(
      <SessionsIndex
        stats={defaultStats}
        sessions={makePagination([makeSession({ ip_address: null })])}
        filters={defaultFilters}
      />,
    );
    expect(screen.getByText('—')).toBeInTheDocument();
  });

  it('opens confirm dialog on invalidate button click', () => {
    render(
      <SessionsIndex
        stats={defaultStats}
        sessions={makePagination([makeSession()])}
        filters={defaultFilters}
      />,
    );
    const invalidateBtns = screen.getAllByRole('button');
    const invalidateBtn = invalidateBtns[invalidateBtns.length - 1];
    fireEvent.click(invalidateBtn);
    expect(screen.getByText('Invalidate session?')).toBeInTheDocument();
  });

  it('does not show invalidate button for viewer role', () => {
    vi.mocked(usePage).mockReturnValueOnce({
      url: '/admin/sessions',
      props: {
        auth: { user: { name: 'Viewer', email: 'viewer@test.com', admin_role: 'viewer' } },
        features: {},
      },
    } as unknown as ReturnType<typeof usePage>);

    render(
      <SessionsIndex
        stats={defaultStats}
        sessions={makePagination([makeSession()])}
        filters={defaultFilters}
      />,
    );
    // Session row renders without the invalidate button
    expect(screen.getByText('Alice Example')).toBeInTheDocument();
    // Assert the invalidate button itself is not rendered for viewers (not just the confirm dialog,
    // which would only appear after clicking — asserting dialog absence is vacuous).
    expect(screen.queryByRole('button', { name: /invalidate/i })).not.toBeInTheDocument();
  });
});
