import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { main } from './index.js';
import { TestContext } from '@tests/utils/index.js';
import type { ProcessOptions } from '@core/types/index.js';
import { IFileSystem } from '@services/fs/FileSystemService/IFileSystem.js';
import { MeldFileNotFoundError } from '@core/errors/MeldFileNotFoundError.js';
import { DirectiveService } from '@services/pipeline/DirectiveService/DirectiveService.js';
import fs from 'fs';
import { TestDebuggerService } from '@tests/utils/debug/TestDebuggerService.js';

// Define the type for main function options
type MainOptions = {
  fs?: IFileSystem;
  format?: 'xml';
  services?: any;
};

describe('SDK Integration Tests', () => {
  let context: TestContext;
  let testFilePath: string;

  beforeEach(async () => {
    context = new TestContext();
    await context.initialize();
    testFilePath = 'test.meld';
  });

  afterEach(async () => {
    await context.cleanup();
    vi.resetModules();
    vi.clearAllMocks();
  });

  describe('Service Management', () => {
    it('should create services in correct initialization order', async () => {
      // Create a new DirectiveService instance to spy on
      const directive = new DirectiveService();
      const initSpy = vi.spyOn(directive, 'initialize');
      
      // Create services object with our spied service
      const services = {
        ...context.services,
        directive
      };
      
      await context.fs.writeFile(testFilePath, '@text greeting = "Hello"');
      await main(testFilePath, { fs: context.fs, services: services as any });
      
      // Verify directive.initialize was called with services in correct order
      expect(initSpy).toHaveBeenCalledWith(
        expect.any(Object), // validation
        expect.any(Object), // state
        expect.any(Object), // path
        expect.any(Object), // filesystem
        expect.any(Object), // parser
        expect.any(Object), // interpreter
        expect.any(Object), // circularity
        expect.any(Object)  // resolution
      );
    });

    it('should allow service injection through options', async () => {
      const customState = context.services.state;
      const spy = vi.spyOn(customState, 'enableTransformation');

      await context.fs.writeFile(testFilePath, '@text greeting = "Hello"');
      await main(testFilePath, {
        fs: context.fs,
        services: { state: customState } as any,
        transformation: true
      });

      expect(spy).toHaveBeenCalledWith(true);
    });
  });

  describe('Transformation Mode', () => {
    it('should enable transformation through options', async () => {
      const content = `@text greeting = "Hello"
@run[echo test]`;
      await context.fs.writeFile(testFilePath, content);
      
      // Start a debug session to capture metrics
      const sessionId = await context.startDebugSession();
      
      const result = await main(testFilePath, {
        fs: context.fs,
        services: context.services as any,
        transformation: true
      });

      // Get debug results
      const debugResult = await context.endDebugSession(sessionId);
      
      // Verify debugging data
      expect(debugResult).toBeDefined();
      expect(debugResult.metrics).toBeDefined();
      expect(debugResult.startTime).toBeLessThan(debugResult.endTime);
      
      // In transformation mode, directives should be replaced
      expect(result).not.toContain('[run directive output placeholder]');
      expect(result).toContain('test');
    });

    it('should respect existing transformation state', async () => {
      const content = '@run [echo test]';
      await context.fs.writeFile(testFilePath, content);
      
      // Enable transformation through context
      context.enableTransformation();
      
      const result = await main(testFilePath, {
        fs: context.fs,
        services: context.services as any
      });
      
      // Should still be in transformation mode
      expect(result).not.toContain('[run directive output placeholder]');
      expect(result).toContain('test');
    });

    it('should handle execution directives correctly', async () => {
      await context.fs.writeFile(testFilePath, '@run [echo test]');
      
      context.enableDebug();
      context.disableTransformation(); // Explicitly disable transformation
      
      const result = await main(testFilePath, {
        fs: context.fs,
        format: 'xml',
        services: context.services as any, // Cast to any to avoid type errors
        debug: true
      });

      // Verify result
      expect(result).toContain('[run directive output placeholder]');
    });

    it('should handle complex meld content with mixed directives', async () => {
      const content = `
@text greeting = "Hello"
@data config = { "value": 123 }
Some text content
@run [echo test]
More text`;
      await context.fs.writeFile(testFilePath, content);
      context.disableTransformation(); // Explicitly disable transformation
      const result = await main(testFilePath, { 
        fs: context.fs,
        services: context.services as any
      });
      
      // Definition directives should be omitted
      expect(result).not.toContain('"identifier": "greeting"');
      expect(result).not.toContain('"value": "Hello"');
      expect(result).not.toContain('"identifier": "config"');
      
      // Text content should be preserved
      expect(result).toContain('Some text content');
      expect(result).toContain('More text');
      
      // Execution directives should show placeholder
      expect(result).toContain('[run directive output placeholder]');
    });
  });

  describe('Debug Mode', () => {
    it('should enable debug mode through options', async () => {
      await context.fs.writeFile(testFilePath, '@text greeting = "Hello"');
      
      await main(testFilePath, {
        fs: context.fs,
        services: context.services as any,
        debug: true
      });
      
      // Verify debug data was captured
      const debugData = await (context.services.debug as any).getDebugData();
      expect(debugData).toBeDefined();
      expect(debugData.operations).toHaveLength(1);
    });
  });

  describe('Format Conversion', () => {
    it('should handle definition directives correctly', async () => {
      await context.fs.writeFile(testFilePath, '@text greeting = "Hello"');
      const result = await main(testFilePath, { 
        fs: context.fs,
        services: context.services as any
      });
      // Definition directives should be omitted from output
      expect(result).toBe('');
    });

    it('should handle execution directives correctly', async () => {
      await context.fs.writeFile(testFilePath, '@run [echo test]');
      
      context.enableDebug();
      context.disableTransformation(); // Explicitly disable transformation
      
      const result = await main(testFilePath, {
        fs: context.fs,
        format: 'xml',
        services: context.services as any, // Cast to any to avoid type errors
        debug: true
      });

      // Verify result
      expect(result).toContain('[run directive output placeholder]');
    });

    it('should handle complex meld content with mixed directives', async () => {
      const content = `
@text greeting = "Hello"
@data config = { "value": 123 }
Some text content
@run [echo test]
More text`;
      await context.fs.writeFile(testFilePath, content);
      context.disableTransformation(); // Explicitly disable transformation
      const result = await main(testFilePath, { 
        fs: context.fs,
        services: context.services as any
      });
      
      // Definition directives should be omitted
      expect(result).not.toContain('"identifier": "greeting"');
      expect(result).not.toContain('"value": "Hello"');
      expect(result).not.toContain('"identifier": "config"');
      
      // Text content should be preserved
      expect(result).toContain('Some text content');
      expect(result).toContain('More text');
      
      // Execution directives should show placeholder
      expect(result).toContain('[run directive output placeholder]');
    });
  });

  describe('Error Handling', () => {
    it('should handle parse errors gracefully', async () => {
      const invalidContent = '@invalid directive';
      await context.fs.writeFile(testFilePath, invalidContent);
      
      await expect(main(testFilePath, { 
        fs: context.fs,
        services: context.services as any
      })).rejects.toThrow();
    });

    it('should handle missing files correctly', async () => {
      const nonExistentFile = 'non-existent.meld';
      
      await expect(main(nonExistentFile, { 
        fs: context.fs,
        services: context.services as any
      })).rejects.toThrow(MeldFileNotFoundError);
    });

    it('should handle service initialization errors', async () => {
      // Create a service that will throw during initialization
      const brokenServices = {
        ...context.services,
        directive: undefined
      };
      
      await context.fs.writeFile(testFilePath, '@text greeting = "Hello"');
      
      await expect(main(testFilePath, { 
        fs: context.fs,
        services: brokenServices as any
      })).rejects.toThrow();
    });
  });

  describe('Full Pipeline Integration', () => {
    it('should handle the complete parse -> interpret -> convert pipeline', async () => {
      const content = `
@text greeting = "Hello"
@run [echo {{greeting}}]`;
      await context.fs.writeFile(testFilePath, content);
      
      const result = await main(testFilePath, {
        fs: context.fs,
        services: context.services as any,
        transformation: true
      });
      
      // In transformation mode, directives should be replaced with their results
      expect(result).toContain('Hello');
      expect(result).not.toContain('@text');
      expect(result).not.toContain('@run');
    });

    it('should preserve state and content in transformation mode', async () => {
      const content = `
@text greeting = "Hello"
@text name = "World"
@run [echo {{greeting}}, {{name}}!]`;
      await context.fs.writeFile(testFilePath, content);
      
      const result = await main(testFilePath, {
        fs: context.fs,
        services: context.services as any,
        transformation: true
      });
      
      // Resolved variables should be outputted
      expect(result).toContain('Hello, World!');
    });
  });

  describe('Edge Cases', () => {
    it.todo('should handle large files efficiently');
    it.todo('should handle deeply nested imports');
  });

  describe('Examples', () => {
    it('should run api-demo-simple.meld example file', async () => {
      // Create a simplified test file
      const content = `
# Simple Example

## Title

@run [echo "This is a simple example"]`;
      await context.fs.writeFile(testFilePath, content);
      
      const result = await main(testFilePath, {
        fs: context.fs,
        services: context.services as any,
        transformation: true
      });
      
      // Verify the output contains the transformed content - now in XML format
      expect(result).toContain('<SimpleExample>');
      expect(result).toContain('<Title>');
      expect(result).toContain('This is a simple example');
    });
  });
}); 