// Tests for backpressure guards — paste truncation and stdout buffer limits.
// Uses bun:test. Tests the logic from Terminal.svelte and sidecar-manager.ts.

import { describe, it, expect } from 'bun:test';

// ── Constants (replicated from source) ──────────────────────────────────────

const MAX_PASTE_CHUNK = 64 * 1024;           // 64 KB (Terminal.svelte)
const MAX_LINE_SIZE = 10 * 1024 * 1024;       // 10 MB (sidecar-manager.ts)
const MAX_PENDING_BUFFER = 50 * 1024 * 1024;  // 50 MB (sidecar-manager.ts)

// ── Replicated truncation logic ──────────────────────────────────────────────

function truncatePaste(payload: string): { text: string; wasTruncated: boolean } {
  if (payload.length > MAX_PASTE_CHUNK) {
    return { text: payload.slice(0, MAX_PASTE_CHUNK), wasTruncated: true };
  }
  return { text: payload, wasTruncated: false };
}

function applyBufferBackpressure(buffer: string): string {
  // If buffer exceeds MAX_PENDING_BUFFER, keep only last MAX_LINE_SIZE bytes
  if (buffer.length > MAX_PENDING_BUFFER) {
    return buffer.slice(-MAX_LINE_SIZE);
  }
  return buffer;
}

function shouldTruncateLine(buffer: string): boolean {
  // If buffer exceeds MAX_LINE_SIZE without a newline, truncate
  return buffer.length > MAX_LINE_SIZE && !buffer.includes('\n');
}

// ── Tests ───────────────────────────────────────────────────────────────────

describe('paste truncation', () => {
  it('passes through text under 64KB', () => {
    const text = 'hello world';
    const result = truncatePaste(text);
    expect(result.wasTruncated).toBe(false);
    expect(result.text).toBe(text);
  });

  it('passes through text exactly at 64KB', () => {
    const text = 'x'.repeat(MAX_PASTE_CHUNK);
    const result = truncatePaste(text);
    expect(result.wasTruncated).toBe(false);
    expect(result.text.length).toBe(MAX_PASTE_CHUNK);
  });

  it('truncates text over 64KB', () => {
    const text = 'x'.repeat(MAX_PASTE_CHUNK + 1000);
    const result = truncatePaste(text);
    expect(result.wasTruncated).toBe(true);
    expect(result.text.length).toBe(MAX_PASTE_CHUNK);
  });

  it('preserves first 64KB of content on truncation', () => {
    const prefix = 'START-';
    const text = prefix + 'x'.repeat(MAX_PASTE_CHUNK + 1000);
    const result = truncatePaste(text);
    expect(result.text.startsWith('START-')).toBe(true);
  });
});

describe('stdout buffer backpressure', () => {
  it('leaves buffer unchanged under 50MB', () => {
    const buffer = 'x'.repeat(1000);
    expect(applyBufferBackpressure(buffer)).toBe(buffer);
  });

  it('truncates buffer over 50MB to last 10MB', () => {
    const buffer = 'x'.repeat(MAX_PENDING_BUFFER + 1000);
    const result = applyBufferBackpressure(buffer);
    expect(result.length).toBe(MAX_LINE_SIZE);
  });

  it('keeps tail of buffer (most recent data)', () => {
    const head = 'H'.repeat(MAX_PENDING_BUFFER);
    const tail = 'T'.repeat(MAX_LINE_SIZE);
    const buffer = head + tail;
    const result = applyBufferBackpressure(buffer);
    // Result should be the last MAX_LINE_SIZE chars, which is all T's
    expect(result).toBe(tail);
  });
});

describe('line size guard', () => {
  it('no truncation for buffer with newlines', () => {
    const buffer = 'x'.repeat(MAX_LINE_SIZE + 100) + '\nmore data';
    expect(shouldTruncateLine(buffer)).toBe(false);
  });

  it('truncates when buffer exceeds MAX_LINE_SIZE without newline', () => {
    const buffer = 'x'.repeat(MAX_LINE_SIZE + 1);
    expect(shouldTruncateLine(buffer)).toBe(true);
  });

  it('no truncation at exactly MAX_LINE_SIZE', () => {
    const buffer = 'x'.repeat(MAX_LINE_SIZE);
    expect(shouldTruncateLine(buffer)).toBe(false);
  });
});
