import { isValidElement } from 'react';
import { describe, expect, it } from 'vitest';
import type { RouteObject } from 'react-router-dom';
import { routes } from '@/routes';
import { Placeholder } from '@/spa/placeholder';
import { ChatPage } from '@/spa/pages/chat';
import { ProjectDetailPage } from '@/spa/pages/project-detail';
import { ProjectsPage } from '@/spa/pages/projects';
import { TasksPage } from '@/spa/pages/tasks';

function collectPaths(routeObjects: RouteObject[]): string[] {
  return routeObjects.flatMap((route) => [
    ...(route.path ? [route.path] : []),
    ...(route.children ? collectPaths(route.children) : []),
  ]);
}

function findRoute(routeObjects: RouteObject[], path: string): RouteObject | undefined {
  for (const route of routeObjects) {
    if (route.path === path) return route;
    const nested = route.children ? findRoute(route.children, path) : undefined;
    if (nested) return nested;
  }
  return undefined;
}

describe('SPA route table', () => {
  it('covers every v1 parity route from the Phase P RFC', () => {
    expect(collectPaths(routes).sort()).toEqual(
      [
        '/',
        '/admin',
        '/auth/provider/:provider',
        '/chat',
        '/login',
        '/projects',
        '/projects/:id',
        '/register',
        '/settings',
        '/tasks',
      ].sort(),
    );
  });

  it('separates guest and authenticated route groups', () => {
    const guestPaths = collectPaths(routes.at(0)?.children ?? []);
    const authPaths = collectPaths(routes.at(1)?.children ?? []);
    expect(guestPaths).toContain('/login');
    expect(guestPaths).not.toContain('/chat');
    expect(authPaths).toContain('/chat');
  });

  it.each(['/login', '/register', '/auth/provider/:provider'])(
    'renders a real guest page instead of the P1 placeholder at %s',
    (path) => {
      const element = findRoute(routes, path)?.element;
      expect(isValidElement(element)).toBe(true);
      if (!isValidElement(element)) throw new Error(`Missing route element for ${path}`);
      expect(element.type).not.toBe(Placeholder);
    },
  );

  it('renders the real chat page instead of the P1 placeholder at /chat, inside the authenticated group', () => {
    const authPaths = collectPaths(routes.at(1)?.children ?? []);
    expect(authPaths).toContain('/chat');

    const element = findRoute(routes, '/chat')?.element;
    expect(isValidElement(element)).toBe(true);
    if (!isValidElement(element)) throw new Error('Missing route element for /chat');
    expect(element.type).not.toBe(Placeholder);
    expect(element.type).toBe(ChatPage);
  });

  it.each([
    ['/projects', ProjectsPage],
    ['/projects/:id', ProjectDetailPage],
    ['/tasks', TasksPage],
  ])(
    'renders a real authenticated page instead of the P1 placeholder at %s',
    (path, expectedType) => {
      const element = findRoute(routes, path)?.element;
      expect(isValidElement(element)).toBe(true);
      if (!isValidElement(element)) throw new Error(`Missing route element for ${path}`);
      expect(element.type).not.toBe(Placeholder);
      expect(element.type).toBe(expectedType);
    },
  );

  it.each(['/chat', '/projects', '/projects/:id', '/tasks'])(
    'defines an error boundary for %s',
    (path) => {
      const route = findRoute(routes, path);
      expect(route?.errorElement).toBeTruthy();
      expect(isValidElement(route?.errorElement)).toBe(true);
    },
  );
});
