/**
 * @fileoverview Tests for FileSystemRouter
 */

import { mkdir, rm } from 'fs/promises';
import path from 'path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { FileSystemRouter } from './fs-router.js';

// Mock the OrdoJSLexer and OrdoJSParser
vi.mock('./lexer.js', () => ({
  OrdoJSLexer: class MockLexer {
    tokenize() {
      return [];
    }
  }
}));

vi.mock('./parser.js', () => ({
  OrdoJSParser: class MockParser {
    constructor() {}
    parse() {
      return {
        component: {
          name: 'TestComponent',
          serverBlock: {
            functions: []
          }
        }
      };
    }
  }
}));

describe('FileSystemRouter', () => {
  const testRoutesDir = 'test-routes';
  let router: FileSystemRouter;

  beforeEach(async () => {
    // Create test routes directory
    await mkdir(testRoutesDir, { recursive: true });

    router = new FileSystemRouter({
      routesDir: testRoutesDir,
      extensions: ['.ordo'],
      generateCatchAll: true,
      baseUrl: '/'
    });
  });

  afterEach(async () => {
    // Clean up test directory
    try {
      await rm(testRoutesDir, { recursive: true, force: true });
    } catch (error) {
      // Ignore cleanup errors
    }
  });

  describe('filePathToRoutePath', () => {
    it('should convert index files to root paths', () => {
      const testCases = [
        { input: 'index.ordo', expected: '/' },
        { input: 'about/index.ordo', expected: '/about' },
        { input: 'blog/posts/index.ordo', expected: '/blog/posts' }
      ];

      for (const { input, expected } of testCases) {
        const filePath = path.join(testRoutesDir, input);
        const routePath = (router as any).filePathToRoutePath(filePath);
        expect(routePath).toBe(expected);
      }
    });

    it('should convert dynamic routes correctly', () => {
      const testCases = [
        { input: 'users/[id].ordo', expected: '/users/:id' },
        { input: 'blog/[slug].ordo', expected: '/blog/:slug' },
        { input: 'posts/[category]/[id].ordo', expected: '/posts/:category/:id' }
      ];

      for (const { input, expected } of testCases) {
        const filePath = path.join(testRoutesDir, input);
        const routePath = (router as any).filePathToRoutePath(filePath);
        expect(routePath).toBe(expected);
      }
    });

    it('should convert catch-all routes correctly', () => {
      const testCases = [
        { input: 'docs/[...slug].ordo', expected: '/docs/*slug' },
        { input: '[...path].ordo', expected: '/*path' }
      ];

      for (const { input, expected } of testCases) {
        const filePath = path.join(testRoutesDir, input);
        const routePath = (router as any).filePathToRoutePath(filePath);
        expect(routePath).toBe(expected);
      }
    });
  });

  describe('extractRouteParams', () => {
    it('should extract dynamic parameters', () => {
      const testCases = [
        { path: '/users/:id', expected: ['id'] },
        { path: '/posts/:category/:id', expected: ['category', 'id'] },
        { path: '/docs/*slug', expected: ['slug'] },
        { path: '/static', expected: [] }
      ];

      for (const { path, expected } of testCases) {
        const params = (router as any).extractRouteParams(path);
        expect(params).toEqual(expected);
      }
    });
  });

  describe('calculateRoutePriority', () => {
    it('should assign correct priorities', () => {
      const testCases = [
        { path: '/about', isDynamic: false, isCatchAll: false, expectedPriority: 2 }, // 1 + 1 slash
        { path: '/users/:id', isDynamic: true, isCatchAll: false, expectedPriority: 5 }, // 2 + 1 param + 2 slashes
        { path: '/docs/*slug', isDynamic: false, isCatchAll: true, expectedPriority: 102 }, // 100 + 2 slashes
        { path: '/', isDynamic: false, isCatchAll: false, expectedPriority: 2 } // 1 + 1 slash
      ];

      for (const { path, isDynamic, isCatchAll, expectedPriority } of testCases) {
        const priority = (router as any).calculateRoutePriority(path, isDynamic, isCatchAll);
        expect(priority).toBe(expectedPriority);
      }
    });
  });

  describe('matchRoute', () => {
    it('should match static routes correctly', () => {
      const route = {
        path: '/about',
        isDynamic: false,
        isCatchAll: false,
        params: []
      };

      expect((router as any).matchRoute(route, '/about')).toBe(true);
      expect((router as any).matchRoute(route, '/about/')).toBe(false);
      expect((router as any).matchRoute(route, '/contact')).toBe(false);
    });

    it('should match dynamic routes correctly', () => {
      const route = {
        path: '/users/:id',
        isDynamic: true,
        isCatchAll: false,
        params: ['id']
      };

      expect((router as any).matchRoute(route, '/users/123')).toBe(true);
      expect((router as any).matchRoute(route, '/users/abc')).toBe(true);
      expect((router as any).matchRoute(route, '/users')).toBe(false);
      expect((router as any).matchRoute(route, '/users/123/edit')).toBe(false);
    });

    it('should match catch-all routes correctly', () => {
      const route = {
        path: '/docs/*slug',
        isDynamic: false,
        isCatchAll: true,
        params: ['slug']
      };

      expect((router as any).matchRoute(route, '/docs/getting-started')).toBe(true);
      expect((router as any).matchRoute(route, '/docs/api/reference')).toBe(true);
      expect((router as any).matchRoute(route, '/docs')).toBe(false);
      expect((router as any).matchRoute(route, '/blog/post')).toBe(false);
    });
  });

  describe('extractParams', () => {
    it('should extract parameters from matched paths', () => {
      const testCases = [
        {
          route: { path: '/users/:id', params: ['id'], isDynamic: true, isCatchAll: false },
          pathname: '/users/123',
          expected: { id: '123' }
        },
        {
          route: { path: '/posts/:category/:id', params: ['category', 'id'], isDynamic: true, isCatchAll: false },
          pathname: '/posts/tech/456',
          expected: { category: 'tech', id: '456' }
        },
        {
          route: { path: '/docs/*slug', params: ['slug'], isDynamic: false, isCatchAll: true },
          pathname: '/docs/getting-started/installation',
          expected: { slug: 'getting-started/installation' }
        },
        {
          route: { path: '/about', params: [], isDynamic: false, isCatchAll: false },
          pathname: '/about',
          expected: {}
        }
      ];

      for (const { route, pathname, expected } of testCases) {
        const params = router.extractParams(route as any, pathname);
        expect(params).toEqual(expected);
      }
    });
  });

  describe('generateNavigationUtils', () => {
    it('should generate navigation utilities with route map', async () => {
      // Mock routes
      (router as any).routes = [
        { path: '/', componentName: 'HomePage' },
        { path: '/users', componentName: 'UsersPage' },
        { path: '/users/:id', componentName: 'UserDetailPage', params: ['id'] }
      ];

      // Generate navigation utilities
      const navigationUtils = router.generateNavigationUtils();

      expect(navigationUtils).toContain('export const ROUTES');
      expect(navigationUtils).toContain('HomePage');
      expect(navigationUtils).toContain('UsersPage');
      expect(navigationUtils).toContain('UserDetailPage');
      expect(navigationUtils).toContain('export function generateUrl');
      expect(navigationUtils).toContain('export function navigate');
      expect(navigationUtils).toContain('export function replace');
      expect(navigationUtils).toContain('export function getCurrentRoute');
    });
  });

  describe('generateCodeSplittingConfig', () => {
    it('should generate code splitting configuration', async () => {
      // Mock routes
      (router as any).routes = [
        {
          path: '/',
          componentName: 'HomePage',
          filePath: path.join(testRoutesDir, 'index.ordo')
        },
        {
          path: '/blog',
          componentName: 'BlogPage',
          filePath: path.join(testRoutesDir, 'blog', 'index.ordo')
        },
        {
          path: '/admin',
          componentName: 'AdminPage',
          filePath: path.join(testRoutesDir, 'admin', 'index.ordo')
        }
      ];

      // Generate code splitting config
      const config = router.generateCodeSplittingConfig();

      expect(config).toHaveProperty('index');
      expect(config).toHaveProperty('blog');
      expect(config).toHaveProperty('admin');

      expect(config.index).toContain(path.join(testRoutesDir, 'index.ordo'));
      expect(config.blog).toContain(path.join(testRoutesDir, 'blog', 'index.ordo'));
      expect(config.admin).toContain(path.join(testRoutesDir, 'admin', 'index.ordo'));
    });
  });

  describe('route filtering methods', () => {
    it('should filter routes correctly', async () => {
      // Mock routes
      (router as any).routes = [
        {
          path: '/about',
          componentName: 'AboutPage',
          isDynamic: false,
          isCatchAll: false,
          prerender: false
        },
        {
          path: '/users/:id',
          componentName: 'UserPage',
          isDynamic: true,
          isCatchAll: false,
          prerender: false
        },
        {
          path: '/docs/*slug',
          componentName: 'DocsPage',
          isDynamic: false,
          isCatchAll: true,
          prerender: false
        },
        {
          path: '/blog',
          componentName: 'BlogPage',
          isDynamic: false,
          isCatchAll: false,
          prerender: true
        }
      ];

      // Test filtering methods
      const staticRoutes = router.getStaticRoutes();
      const dynamicRoutes = router.getDynamicRoutes();
      const prerenderRoutes = router.getPrerenderRoutes();

      expect(staticRoutes).toHaveLength(2); // about and blog
      expect(dynamicRoutes).toHaveLength(1); // users/:id
      expect(prerenderRoutes).toHaveLength(1); // blog

      expect(staticRoutes.some(r => r.path === '/about')).toBe(true);
      expect(dynamicRoutes.some(r => r.path === '/users/:id')).toBe(true);
      expect(prerenderRoutes.some(r => r.path === '/blog')).toBe(true);
    });
  });
});
