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

const mocks = vi.hoisted(() => ({
  navigate: vi.fn(),
  navigateElement: vi.fn(),
  setCurrentAppId: vi.fn(),
  location: { pathname: '/' },
  currentApp: null as unknown,
  launchApp: null as unknown,
}));

const crmApp = {
  id: 'crm_pack_app',
  label: 'CRM',
  frontendAppId: 'crm',
  icon: 'LayoutGrid',
  navigationItems: [{
    id: 'nav_account',
    type: 'object',
    label: 'Accounts',
    objectApiName: 'Account',
  }, {
    id: 'nav_lead',
    type: 'object',
    label: 'Leads',
    objectApiName: 'Lead',
  }],
  utilityItems: [],
};

vi.mock('react-router-dom', async (importOriginal) => {
  const actual = await importOriginal<typeof import('react-router-dom')>();
  return {
    ...actual,
    Navigate: (props: { to: string; replace?: boolean }) => {
      mocks.navigateElement(props);
      return null;
    },
    useLocation: () => mocks.location,
    useNavigate: () => mocks.navigate,
  };
});

vi.mock('../src/dashboard-route', () => ({
  CrmDashboardPage: () => null,
  CrmDashboardRoute: () => null,
  crmDashboardWidgets: {},
}));

vi.mock('@nexuscrm/host-sdk', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@nexuscrm/host-sdk')>();
  return {
    ...actual,
    hostApps: { apps: { get: () => undefined } },
    notificationsAPI: {
      list: vi.fn().mockResolvedValue({ notifications: [] }),
      markAsRead: vi.fn(),
      markAllAsRead: vi.fn(),
    },
    useApp: () => ({
      currentApp: mocks.currentApp,
      currentAppId: null,
      currentFrontendAppId: 'crm',
      visibleApps: [mocks.launchApp ?? crmApp],
      loading: false,
      setCurrentAppId: mocks.setCurrentAppId,
    }),
    useRuntime: () => ({
      user: { Id: '005-user', Name: 'Test User', Email: 'test@example.com' },
      logout: vi.fn(),
      usesExternalIdentitySession: false,
    }),
  };
});

describe('CRM app launcher navigation', () => {
  it('does not invent an Account entry when navigation is empty', async () => {
    const { createCrmRuntimeRouteBindings } = await import('../src/runtime-route-bindings');
    const Entry = createCrmRuntimeRouteBindings({ setupComponent: () => null }).routeComponents.appHomeRedirect!;
    mocks.currentApp = { ...crmApp, navigationItems: [] };
    render(<Entry />);
    expect(screen.getByRole('status')).toHaveTextContent('No accessible navigation');
    expect(mocks.navigateElement).not.toHaveBeenCalled();
  }, 10000);

  it('renders a Home-only application without a self redirect', async () => {
    const { createCrmRuntimeRouteBindings } = await import('../src/runtime-route-bindings');
    const Entry = createCrmRuntimeRouteBindings({ setupComponent: () => null }).routeComponents.appHomeRedirect!;
    mocks.currentApp = { ...crmApp, navigationItems: [{ type: 'page', pageUrl: '/' }] };
    render(<Entry />);
    expect(mocks.navigateElement).not.toHaveBeenCalled();
    expect(screen.queryByRole('status')).not.toBeInTheDocument();
  }, 10000);
  beforeEach(() => {
    vi.clearAllMocks();
    mocks.currentApp = null;
    mocks.launchApp = null;
    mocks.location = { pathname: '/' };
  });

  it('opens the selected app on its first navigation item', async () => {
    const { TopBar } = await import('../src/platform/components/shell/TopBar');

    render(
      <TopBar
        mobileMenuOpen={false}
        setMobileMenuOpen={vi.fn()}
        isSidebarCollapsed={false}
      />,
    );

    fireEvent.click(screen.getByTitle('App Launcher'));
    fireEvent.click(screen.getByRole('button', { name: 'CRM' }));

    expect(mocks.setCurrentAppId).toHaveBeenCalledWith('crm_pack_app');
    expect(mocks.navigate).toHaveBeenCalledWith('/object/Account');
  }, 10000);

  it.each(['https://example.test/docs?q=a%20b#part', '/help/document?q=1#part'])('launches web entry %s without invoking the SPA router', async href => {
    const { TopBar } = await import('../src/platform/components/shell/TopBar');
    mocks.launchApp = { ...crmApp, navigationItems: [{ type: 'web', pageUrl: href }] };
    const open = vi.spyOn(window, 'open').mockReturnValue(null);
    try {
      render(<TopBar mobileMenuOpen={false} setMobileMenuOpen={vi.fn()} isSidebarCollapsed={false} />);
      fireEvent.click(screen.getByTitle('App Launcher'));
      fireEvent.click(screen.getByRole('button', { name: 'CRM' }));
      expect(open).toHaveBeenCalledWith(href, '_blank', 'noopener,noreferrer');
      expect(mocks.navigate).not.toHaveBeenCalled();
      expect(mocks.setCurrentAppId).toHaveBeenCalledWith(crmApp.id);
    } finally { open.mockRestore(); }
  }, 10000);

  it('rejects unsafe web navigation at both the launcher and root entry', async () => {
    const { TopBar } = await import('../src/platform/components/shell/TopBar');
    const { createCrmRuntimeRouteBindings } = await import('../src/runtime-route-bindings');
    const Entry = createCrmRuntimeRouteBindings({ setupComponent: () => null }).routeComponents.appHomeRedirect!;
    mocks.launchApp = mocks.currentApp = { ...crmApp, navigationItems: [{ type: 'web', pageUrl: 'javascript:alert(1)' }] };
    const open = vi.spyOn(window, 'open').mockReturnValue(null);
    const replace = vi.fn();
    const originalWindow = window;
    vi.stubGlobal('window', new Proxy(originalWindow, {
      get: (target, key) => key === 'location' ? { replace } : Reflect.get(target, key, target),
    }));
    try {
      render(<><TopBar mobileMenuOpen={false} setMobileMenuOpen={vi.fn()} isSidebarCollapsed={false} /><Entry /></>);
      fireEvent.click(screen.getByTitle('App Launcher'));
      fireEvent.click(screen.getByRole('button', { name: 'CRM' }));
      expect(open).not.toHaveBeenCalled();
      expect(replace).not.toHaveBeenCalled();
      expect(screen.queryByRole('link', { name: 'Open application' })).not.toBeInTheDocument();
      expect(screen.getByRole('status')).toHaveTextContent('No accessible navigation');
    } finally { open.mockRestore(); vi.unstubAllGlobals(); }
  }, 10000);

  it.each(['https://example.test/docs?q=a%20b#part', '/help/document?q=1#part', '/', './', '#part'])('offers web entry %s without automatic document navigation at the root entry', async href => {
    const { createCrmRuntimeRouteBindings } = await import('../src/runtime-route-bindings');
    const Entry = createCrmRuntimeRouteBindings({ setupComponent: () => null }).routeComponents.appHomeRedirect!;
    mocks.currentApp = { ...crmApp, navigationItems: [{ type: 'web', pageUrl: href }] };
    const replace = vi.fn();
    const originalWindow = window;
    vi.stubGlobal('window', new Proxy(originalWindow, {
      get: (target, key) => key === 'location' ? { replace } : Reflect.get(target, key, target),
    }));
    try {
      render(<Entry />);
      expect(replace).not.toHaveBeenCalled();
      expect(screen.getByRole('link', { name: 'Open application' })).toHaveAttribute('href', href);
      expect(mocks.navigateElement).not.toHaveBeenCalled();
    } finally { vi.unstubAllGlobals(); }
  }, 10000);

  it('shows navigation only after the current application is resolved', async () => {
    const { Sidebar } = await import('../src/platform/components/shell/Sidebar');

    const { rerender } = render(
      <Sidebar
        isSidebarCollapsed={false}
        setIsSidebarCollapsed={vi.fn()}
        mobileMenuOpen={false}
        setMobileMenuOpen={vi.fn()}
      />,
    );

    expect(screen.queryByText('Accounts')).not.toBeInTheDocument();
    expect(screen.queryByText('Leads')).not.toBeInTheDocument();
    mocks.currentApp = crmApp;
    rerender(
      <Sidebar isSidebarCollapsed={false} setIsSidebarCollapsed={vi.fn()}
        mobileMenuOpen={false} setMobileMenuOpen={vi.fn()} />,
    );
    expect(screen.getByText('Accounts')).toBeInTheDocument();
    expect(screen.getByText('Leads')).toBeInTheDocument();
  }, 10000);

  it('redirects the CRM home route from the Home tab to the Account list', async () => {
    const { createCrmRuntimeRouteBindings } = await import('../src/runtime-route-bindings');
    const AppHomeRedirect = createCrmRuntimeRouteBindings({
      setupComponent: () => null,
    }).routeComponents.appHomeRedirect!;
    mocks.currentApp = {
      ...crmApp,
      navigationItems: [{
        id: 'nav_home',
        type: 'page',
        label: 'Home',
        objectApiName: 'Home',
        pageUrl: '/',
      }, ...crmApp.navigationItems],
    };

    render(<AppHomeRedirect />);

    expect(mocks.navigateElement).toHaveBeenCalledTimes(1);
    const [redirectProps] = mocks.navigateElement.mock.calls[0];
    expect(redirectProps).toMatchObject({
      to: '/object/Account',
      replace: true,
    });
  }, 10000);
});
