import type { ReactNode } from 'react';
import { renderHook, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { useAppShell } from '@/business/app-shell/hooks';
import type { ScopeContextValue } from '@/contexts/scope-context';
import type { CurrentUser } from '@/shared/types/auth';

const mockNavigate = vi.fn();
const mockScopeContext = vi.fn<() => ScopeContextValue>();

vi.mock('react-router-dom', async () => {
  const actual = await vi.importActual('react-router-dom');
  return {
    ...actual,
    useNavigate: () => mockNavigate,
  };
});

vi.mock('@/shared/app/scope-context', () => ({
  useScopeContext: () => mockScopeContext(),
}));

vi.mock('@/business/campuses/hooks', () => ({
  useCampusCatalog: () => ({
    campuses: [],
    isLoading: false,
    error: null,
  }),
}));

const globalUser: CurrentUser = {
  id: 'admin-1',
  email: 'admin@example.com',
  permissions: ['READ_PLATFORM_DASHBOARD', 'READ_DASHBOARD'],
  app_role: { name: 'super_admin', globalAccess: true },
};

function createScopeContext(overrides: Partial<ScopeContextValue> = {}): ScopeContextValue {
  return {
    tier: 'global',
    ownTenant: null,
    selectedTenant: null,
    effectiveTenant: null,
    canDrill: true,
    drillInto: vi.fn(),
    setScopeTenant: vi.fn(),
    resetScope: vi.fn(),
    ...overrides,
  };
}

function createWrapper(initialEntry: string | { pathname: string; state?: unknown }) {
  return function Wrapper({ children }: { children: ReactNode }) {
    return <MemoryRouter initialEntries={[initialEntry]}>{children}</MemoryRouter>;
  };
}

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

  it('redirects into the drilled tenant route instead of resetting when scope changes on /platform-dashboard', async () => {
    const resetScope = vi.fn();
    const drilledContext = createScopeContext({
      selectedTenant: { id: 'org-1', level: 'organization', name: 'Demo Academy', logo: null },
      effectiveTenant: { id: 'org-1', level: 'organization', name: 'Demo Academy', logo: null },
      resetScope,
    });
    const ownContext = createScopeContext();
    mockScopeContext
      .mockReturnValueOnce(ownContext)
      .mockReturnValue(drilledContext);

    const { rerender } = renderHook(
      () => useAppShell({ user: globalUser, profile: null, isMobile: false }),
      { wrapper: createWrapper('/platform-dashboard') },
    );

    rerender();

    await waitFor(() => {
      expect(mockNavigate).toHaveBeenCalledWith('/dashboard', {
        replace: true,
        state: {
          __scope: {
            id: 'org-1',
            level: 'organization',
            logo: null,
            name: 'Demo Academy',
          },
        },
      });
    });
    expect(resetScope).not.toHaveBeenCalled();
  });

  it('keeps drilled tenant state on nested attendance detail routes', async () => {
    const selectedTenant = { id: 'org-1', level: 'organization', name: 'Demo Academy', logo: null } as const;
    const resetScope = vi.fn();
    const setScopeTenant = vi.fn();
    mockScopeContext.mockReturnValue(createScopeContext({
      selectedTenant,
      effectiveTenant: selectedTenant,
      resetScope,
      setScopeTenant,
    }));

    renderHook(
      () => useAppShell({
        user: {
          ...globalUser,
          permissions: [...(globalUser.permissions ?? []), 'READ_ATTENDANCE'],
        },
        profile: null,
        isMobile: false,
      }),
      {
        wrapper: createWrapper({
          pathname: '/attendance/details/school/school-1',
          state: { __scope: selectedTenant },
        }),
      },
    );

    await waitFor(() => {
      expect(mockNavigate).not.toHaveBeenCalled();
    });
    expect(resetScope).not.toHaveBeenCalled();
    expect(setScopeTenant).not.toHaveBeenCalled();
  });
});
