import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import {
  PluginRegistry,
  FormatPlugin,
  SerializeObjectResult,
  SerializationContext,
} from '../plugins';
import type { AdkObject } from '@abapify/adk';
import { ConfigLoader } from '../config/loader';
import { AuthRegistry } from '../config/auth';
import { CliConfig } from '../config/interfaces';
import fs from 'node:fs/promises';
import path from 'node:path';
import { tmpdir } from 'node:os';

/**
 * Mock abapGit plugin for testing
 *
 * Note: This implements the FormatPlugin interface with serializeObject
 * which is the required method.
 */
class MockAbapGitPlugin implements FormatPlugin {
  readonly name = '@abapify/abapgit';
  readonly version = '2.0.0';
  readonly description = 'Mock abapGit format plugin for testing';

  async serializeObject(
    object: AdkObject,
    targetPath: string,
    _context: SerializationContext,
  ): Promise<SerializeObjectResult> {
    const filesCreated: string[] = [];

    await fs.mkdir(path.join(targetPath, 'src'), { recursive: true });

    // Create .abap file
    const abapFile = path.join(
      targetPath,
      'src',
      `${object.name.toLowerCase()}.${object.kind.toLowerCase()}.abap`,
    );
    await fs.writeFile(
      abapFile,
      `* Generated by abapGit plugin\n* Object: ${object.name}`,
      'utf-8',
    );
    filesCreated.push(abapFile);

    // Create .xml file
    const xmlFile = path.join(
      targetPath,
      'src',
      `${object.name.toLowerCase()}.${object.kind.toLowerCase()}.xml`,
    );
    const xmlContent = `<?xml version="1.0" encoding="utf-8"?>
<abapGit version="v1.0.0">
  <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
    <asx:values>
      <OBJECT_NAME>${object.name}</OBJECT_NAME>
      <OBJECT_TYPE>${object.kind}</OBJECT_TYPE>
    </asx:values>
  </asx:abap>
</abapGit>`;
    await fs.writeFile(xmlFile, xmlContent, 'utf-8');
    filesCreated.push(xmlFile);

    return {
      success: true,
      filesCreated,
    };
  }

  getSupportedObjectTypes(): string[] {
    return ['Class', 'Interface', 'Program'];
  }

  validateConfig(): { valid: boolean; errors: string[]; warnings?: string[] } {
    return { valid: true, errors: [] };
  }
}

describe('Plugin Architecture E2E Tests', () => {
  let tempDir: string;
  let pluginRegistry: PluginRegistry;
  let configLoader: ConfigLoader;
  let authRegistry: AuthRegistry;

  beforeEach(async () => {
    // Create temporary directory for tests
    tempDir = await fs.mkdtemp(path.join(tmpdir(), 'adt-cli-test-'));

    // Initialize components
    pluginRegistry = new PluginRegistry();
    configLoader = new ConfigLoader();
    authRegistry = new AuthRegistry();

    // Register mock plugin manually for testing
    pluginRegistry.register(new MockAbapGitPlugin());
  });

  afterEach(async () => {
    // Clean up temporary directory
    await fs.rm(tempDir, { recursive: true, force: true });
  });

  it('should load plugins from configuration', async () => {
    const config: CliConfig = {
      auth: {
        type: 'mock',
        mock: { enabled: true },
      },
      plugins: {
        formats: [{ name: '@abapify/abapgit' }],
      },
    };

    // Validate config
    const validation = configLoader.validate(config);
    expect(validation.valid).toBe(true);
    expect(validation.errors).toHaveLength(0);

    // Check available formats
    const formats = pluginRegistry.getAvailableFormats();
    expect(formats).toContain('@abapify/abapgit');
  });

  // Note: Tests for serialize/deserialize methods removed as they use deprecated API
  // The new API uses serializeObject which requires real ADK objects
  // These tests should be rewritten to use proper ADK integration tests

  it('should handle multiple plugins with format selection', async () => {
    // Config would be used in a real CLI scenario - kept for documentation
    // Simulate CLI behavior: one plugin available (abapgit is default)
    const availableFormats = pluginRegistry.getAvailableFormats();
    expect(availableFormats).toHaveLength(1);

    // Select abapgit format
    const selectedFormat = '@abapify/abapgit';
    const plugin = pluginRegistry.getPlugin(selectedFormat);
    expect(plugin).toBeDefined();
    expect(plugin!.name).toBe('@abapify/abapgit');
  });

  it('should handle default format from config', async () => {
    const config: CliConfig = {
      auth: {
        type: 'mock',
        mock: { enabled: true },
      },
      plugins: {
        formats: [{ name: '@abapify/abapgit' }],
      },
      defaults: {
        format: 'abapgit', // Default format specified
      },
    };

    const validation = configLoader.validate(config);
    expect(validation.valid).toBe(true);

    // CLI would use default format without prompting
    const defaultFormat = config.defaults?.format;
    expect(defaultFormat).toBe('abapgit');

    const plugin = pluginRegistry.getPlugin('@abapify/abapgit');
    expect(plugin).toBeDefined();
    expect(plugin!.name).toBe('@abapify/abapgit');
  });

  it('should validate plugin configurations', async () => {
    const config: CliConfig = {
      auth: {
        type: 'mock',
        mock: { enabled: true },
      },
      plugins: {
        formats: [
          {
            name: '@abapify/abapgit',
            config: {
              enabled: true,
            },
          },
        ],
      },
    };

    const validation = configLoader.validate(config);
    expect(validation.valid).toBe(true);

    // Validate individual plugins
    const pluginValidations = pluginRegistry.validatePlugins();
    expect(pluginValidations).toHaveLength(1); // One registered plugin
    expect(pluginValidations.every((v) => v.valid)).toBe(true);
  });

  it('should handle authentication configuration', async () => {
    const mockConfig: CliConfig = {
      auth: {
        type: 'mock',
        mock: { enabled: true },
      },
      plugins: {
        formats: [{ name: '@abapify/abapgit' }],
      },
    };

    const basicConfig: CliConfig = {
      auth: {
        type: 'basic',
        basic: {
          username: 'testuser',
          password: 'testpass',
          host: 'https://test-sap-system.com',
        },
      },
      plugins: {
        formats: [{ name: '@abapify/abapgit' }],
      },
    };

    // Validate mock auth
    const mockValidation = configLoader.validate(mockConfig);
    expect(mockValidation.valid).toBe(true);

    // Validate basic auth
    const basicValidation = configLoader.validate(basicConfig);
    expect(basicValidation.valid).toBe(true);

    // Test auth client creation
    const mockClient = await authRegistry.createClient(mockConfig.auth);
    expect(mockClient).toBeDefined();

    const basicClient = await authRegistry.createClient(basicConfig.auth);
    expect(basicClient).toBeDefined();
  });

  it('should handle configuration errors gracefully', async () => {
    const invalidConfig: CliConfig = {
      auth: {
        type: 'basic',
        basic: {
          username: '',
          password: '',
          host: 'invalid-url',
        },
      },
      plugins: {
        formats: [],
      },
    };

    const validation = configLoader.validate(invalidConfig);
    expect(validation.valid).toBe(false);
    expect(validation.errors).toContain('Username is required for basic auth');
    expect(validation.errors).toContain('Password is required for basic auth');
    expect(validation.errors).toContain(
      'At least one format plugin must be configured',
    );
  });

  it('should support plugin-specific options', async () => {
    const abapGitPlugin = pluginRegistry.getPlugin('@abapify/abapgit');
    expect(abapGitPlugin).toBeDefined();

    const abapGitTypes = abapGitPlugin!.getSupportedObjectTypes();
    expect(abapGitTypes).toContain('Class');
    expect(abapGitTypes).toContain('Interface');
    expect(abapGitTypes).toContain('Program');
  });
});
