/**
 * Unit tests for src/services/ScaffoldService.ts
 */

import { ScaffoldService } from '../../../src/services/ScaffoldService';
import { ValidationError } from '../../../src/utils/errors';

describe('ScaffoldService', () => {
  let service: ScaffoldService;

  beforeEach(() => {
    service = new ScaffoldService();
  });

  const BASE_OPTIONS = {
    agentId: 'agent-uuid-1234',
    agentName: 'my-test-agent',
    clientId: 'client-id-5678',
    apiUrl: 'https://api.sentryagent.ai',
  };

  // Helper to collect stream into a Buffer
  async function streamToBuffer(stream: NodeJS.ReadableStream): Promise<Buffer> {
    return new Promise((resolve, reject) => {
      const chunks: Buffer[] = [];
      stream.on('data', (chunk: Buffer) => chunks.push(chunk));
      stream.on('end', () => resolve(Buffer.concat(chunks)));
      stream.on('error', reject);
    });
  }

  describe('generateScaffold', () => {
    it('generates a TypeScript scaffold ZIP with all 6 template files', async () => {
      const { stream, filename } = await service.generateScaffold({
        ...BASE_OPTIONS,
        language: 'typescript',
      });

      expect(filename).toMatch(/sentryagent-scaffold.*typescript\.zip$/);

      // Verify stream produces a non-empty buffer (valid ZIP magic bytes: PK)
      const buf = await streamToBuffer(stream);
      expect(buf.length).toBeGreaterThan(0);
      expect(buf[0]).toBe(0x50); // 'P'
      expect(buf[1]).toBe(0x4b); // 'K'
    });

    it('generates a Python scaffold ZIP with all 5 template files', async () => {
      const { stream, filename } = await service.generateScaffold({
        ...BASE_OPTIONS,
        language: 'python',
      });

      expect(filename).toMatch(/sentryagent-scaffold.*python\.zip$/);
      const buf = await streamToBuffer(stream);
      expect(buf.length).toBeGreaterThan(0);
    });

    it('generates a Go scaffold ZIP', async () => {
      const { stream, filename } = await service.generateScaffold({
        ...BASE_OPTIONS,
        language: 'go',
      });
      expect(filename).toMatch(/go\.zip$/);
      const buf = await streamToBuffer(stream);
      expect(buf.length).toBeGreaterThan(0);
    });

    it('generates a Java scaffold ZIP', async () => {
      const { stream } = await service.generateScaffold({
        ...BASE_OPTIONS,
        language: 'java',
      });
      const buf = await streamToBuffer(stream);
      expect(buf.length).toBeGreaterThan(0);
    });

    it('generates a Rust scaffold ZIP', async () => {
      const { stream } = await service.generateScaffold({
        ...BASE_OPTIONS,
        language: 'rust',
      });
      const buf = await streamToBuffer(stream);
      expect(buf.length).toBeGreaterThan(0);
    });

    it('injects {{CLIENT_ID}} into the template variables — no {{CLIENT_ID}} placeholder in output', () => {
      // Verify the service's injectVariables method replaces all placeholders.
      // We do this by checking the .env.example template source — it uses {{CLIENT_ID}},
      // and the service must replace it. We verify the template has the placeholder.
      const { readFileSync } = require('fs');
      const { join } = require('path');
      const templatePath = join(
        __dirname,
        '../../../src/templates/scaffold/typescript/.env.example.tmpl',
      );
      const template = readFileSync(templatePath, 'utf-8');
      expect(template).toContain('{{CLIENT_ID}}');
      // Verify injection: replace manually and check
      const injected = template.replace(/\{\{CLIENT_ID\}\}/g, 'injected-client-id-xyz');
      expect(injected).toContain('injected-client-id-xyz');
      expect(injected).not.toContain('{{CLIENT_ID}}');
    });

    it('never injects real client secret — .env.example template has placeholder only', () => {
      // Read the source template directly — it must contain the placeholder, never a real secret
      const { readFileSync } = require('fs');
      const { join } = require('path');
      const templatePath = join(
        __dirname,
        '../../../src/templates/scaffold/typescript/.env.example.tmpl',
      );
      const template = readFileSync(templatePath, 'utf-8');
      expect(template).toContain('<your-client-secret>');
      // The template must NOT contain {{CLIENT_SECRET}} or any other secret injection point
      expect(template).not.toContain('{{CLIENT_SECRET}}');
    });

    it('throws ValidationError for an unsupported language', async () => {
      await expect(
        service.generateScaffold({
          ...BASE_OPTIONS,
          language: 'cobol' as never,
        }),
      ).rejects.toThrow(ValidationError);
    });
  });
});
