/**
 * @fileoverview Tests for OrdoJS SSR Engine
 */

import { describe, expect, it, vi } from 'vitest';
import { OrdoJSCodeGenerator } from './code-generator-fixed.js';
import { OrdoJSSSR } from './ssr-engine.js';

// Mock the code generator
vi.mock('./code-generator-fixed.js', () => {
  return {
    OrdoJSCodeGenerator: vi.fn().mockImplementation(() => {
      return {
        generateHTML: vi.fn().mockImplementation((ast, props) => {
          return `<div data-ordojs-component="${ast.component.name}" data-ordojs-hydrate="true">
            <h1>Hello, ${props?.name || 'World'}</h1>
            <p>This is a test component</p>
          </div>`;
        })
      };
    })
  };
});

describe('OrdoJSSSR', () => {
  // Create a mock component AST
  const mockAST: any = {
    component: {
      name: 'TestComponent',
      props: [
        { name: 'name', isRequired: false, defaultValue: { expressionType: 'LITERAL', value: 'World' } }
      ],
      clientBlock: {
        reactiveVariables: [
          {
            name: 'count',
            initialValue: { expressionType: 'LITERAL', value: 0 },
            dataType: { name: 'number', isArray: false, isOptional: false, genericTypes: [] }
          }
        ]
      },
      serverBlock: {
        functions: [
          {
            name: 'getServerSideProps',
            parameters: [],
            body: [],
            returnType: { name: 'object', isArray: false, isOptional: false, genericTypes: [] },
            isAsync: true,
            isPublic: true,
            middleware: [],
            permissions: []
          }
        ]
      },
      markupBlock: {
        elements: [],
        textNodes: [],
        interpolations: []
      }
    },
    dependencies: [],
    exports: [],
    sourceMap: {
      version: 3,
      sources: [],
      names: [],
      mappings: '',
      sourcesContent: []
    }
  };

  it('should register components', () => {
    const ssr = new OrdoJSSSR();
    ssr.registerComponent(mockAST);

    // Use a private property accessor for testing
    const registry = (ssr as any).componentRegistry;
    expect(registry.has('TestComponent')).toBe(true);
  });

  it('should render a component to HTML', async () => {
    const ssr = new OrdoJSSSR();
    ssr.registerComponent(mockAST);

    const html = await ssr.renderComponent('TestComponent', { name: 'Tester' });

    // Check that the HTML contains the component name
    expect(html).toContain('data-ordojs-component="TestComponent"');

    // Check that props were passed correctly
    expect(html).toContain('Hello, Tester');

    // Check that hydration data was included
    expect(html).toContain('<script type="application/json" id="ordojs-hydration-data">');
    expect(html).toContain('"componentName": "TestComponent"');
  });

  it('should generate hydration data', () => {
    const ssr = new OrdoJSSSR();
    const hydrationData = ssr.generateHydrationData(mockAST, { name: 'Tester' });

    expect(hydrationData.componentName).toBe('TestComponent');
    expect(hydrationData.props).toEqual({ name: 'Tester' });
    expect(hydrationData.initialState).toEqual({ count: 0 });
    expect(hydrationData.version).toBe('1.0');
  });

  it('should handle data fetching', async () => {
    const ssr = new OrdoJSSSR({
      dataFetcher: async (component, props) => {
        return { serverData: 'fetched data' };
      }
    });
    ssr.registerComponent(mockAST);

    const data = await ssr.handleDataFetching('TestComponent', { name: 'Tester' });

    expect(data.props).toEqual({
      name: 'Tester',
      serverData: 'fetched data'
    });
  });

  it('should render a route', async () => {
    const ssr = new OrdoJSSSR({
      routes: [
        {
          path: '/users/:id',
          component: 'TestComponent',
          dataFetcher: async (params, query) => {
            return {
              userId: params.id,
              filter: query.filter
            };
          }
        }
      ]
    });
    ssr.registerComponent(mockAST);

    const html = await ssr.renderRoute('/users/123?filter=active');

    // Check that route params were passed to the component
    expect(html).toContain('data-ordojs-component="TestComponent"');

    // The mock code generator doesn't actually use these props, but we can verify
    // the route handling by checking that the function was called
    expect(OrdoJSCodeGenerator).toHaveBeenCalled();
  });

  it('should generate a complete HTML document', () => {
    const ssr = new OrdoJSSSR();
    const content = '<div>Test content</div>';
    const document = ssr.generateDocument(content, 'Test Page', ['/js/app.js'], ['/css/styles.css']);

    expect(document).toContain('<!DOCTYPE html>');
    expect(document).toContain('<title>Test Page</title>');
    expect(document).toContain('<div id="app"><div>Test content</div></div>');
    expect(document).toContain('<script src="/js/app.js" defer></script>');
    expect(document).toContain('<link rel="stylesheet" href="/css/styles.css">');
  });

  it('should throw an error when component is not found', async () => {
    const ssr = new OrdoJSSSR();

    await expect(ssr.renderComponent('NonExistentComponent')).rejects.toThrow(
      'Component "NonExistentComponent" not found in registry'
    );
  });

  it('should throw an error when no routes are configured', async () => {
    const ssr = new OrdoJSSSR();

    await expect(ssr.renderRoute('/some-path')).rejects.toThrow(
      'No routes configured for SSR'
    );
  });

  it('should throw an error when no matching route is found', async () => {
    const ssr = new OrdoJSSSR({
      routes: [
        {
          path: '/users/:id',
          component: 'TestComponent'
        }
      ]
    });

    await expect(ssr.renderRoute('/products/123')).rejects.toThrow(
      'No route found for path: /products/123'
    );
  });
});
