import { describe, test, expect } from 'bun:test';
// The sidecar is .mjs — node-pty binds a native addon against node's ABI, so this half of the tree is plain
// JavaScript. Only the pure function is imported; nothing here spawns a pty.
import { stripQueries } from './sessions.mjs';

// The scrollback is replayed verbatim when a client re-attaches. Anything in it that ASKS the terminal a
// question is asked again on every reconnect, and xterm answers by writing the reply to its input — which
// the pty receives as a keystroke nobody typed. That is the whole reason this function exists.
//
// It is tested rather than eyeballed because both directions fail silently: strip too little and a reconnect
// injects junk into the shell, strip too much and a replay renders differently from the live screen, and
// neither shows up until someone reconnects at the wrong moment.

describe('stripQueries — removes what would be answered', () => {
  const cases: Array<[string, string]> = [
    ['cursor position report (DSR 6n)', '\x1b[6n'],
    ['status report (DSR 5n)', '\x1b[5n'],
    ['DEC variant (DSR ?6n)', '\x1b[?6n'],
    ['primary device attributes', '\x1b[c'],
    ['secondary device attributes', '\x1b[>c'],
    ['tertiary device attributes', '\x1b[=c'],
    ['DA with parameters', '\x1b[0c'],
    ['mode query (DECRQM)', '\x1b[?2026$p'],
    ['window size report (XTWINOPS 18)', '\x1b[18t'],
    ['text area size (XTWINOPS 14)', '\x1b[14t'],
    ['version query (XTVERSION)', '\x1b[>0q'],
    ['terminfo capability query (XTGETTCAP)', '\x1bP+q544e\x1b\\'],
    ['foreground colour query', '\x1b]10;?\x07'],
    ['background colour query', '\x1b]11;?\x1b\\'],
    ['cursor colour query', '\x1b]12;?\x07'],
    ['palette entry query', '\x1b]4;1;?\x07'],
  ];

  for (const [name, sequence] of cases) {
    test(`strips ${name}`, () => {
      expect(stripQueries(`before${sequence}after`)).toBe('beforeafter');
    });
  }

  test('strips several in one chunk, and repeats of the same one', () => {
    expect(stripQueries('a\x1b[6nb\x1b[cc\x1b[6nd')).toBe('abcd');
  });
});

describe('stripQueries — keeps everything that draws', () => {
  const kept: Array<[string, string]> = [
    ['SGR colour', '\x1b[31mred\x1b[0m'],
    ['256-colour SGR', '\x1b[38;5;208m'],
    ['truecolour SGR', '\x1b[38;2;255;128;0m'],
    ['cursor movement', '\x1b[10;20H'],
    ['cursor up', '\x1b[3A'],
    ['erase in display', '\x1b[2J'],
    ['erase in line', '\x1b[K'],
    ['alt screen on', '\x1b[?1049h'],
    ['alt screen off', '\x1b[?1049l'],
    ['bracketed paste on', '\x1b[?2004h'],
    ['mouse tracking on', '\x1b[?1000h'],
    ['scroll region', '\x1b[1;24r'],
    ['window title (OSC 0)', '\x1b]0;my title\x07'],
    ['window title (OSC 2)', '\x1b]2;my title\x07'],
    ['OSC 52 clipboard write', '\x1b]52;c;aGVsbG8=\x07'],
    ['save cursor', '\x1b7'],
    ['plain text with newlines', 'line one\r\nline two\r\n'],
  ];

  for (const [name, sequence] of kept) {
    test(`keeps ${name}`, () => {
      expect(stripQueries(sequence)).toBe(sequence);
    });
  }

  // The pair that made the XTWINOPS pattern enumerate its parameters instead of wildcarding them: both are
  // `CSI … t`, one is a question and the other changes what a replay renders.
  test('keeps title push/pop (XTWINOPS 22/23) while stripping the reports', () => {
    expect(stripQueries('\x1b[22;0t\x1b[18t\x1b[23;0t')).toBe('\x1b[22;0t\x1b[23;0t');
  });

  test('leaves a realistic prompt untouched', () => {
    const prompt = '\x1b]0;green@edge: ~\x07\x1b[1;32mgreen@edge\x1b[0m:\x1b[1;34m~\x1b[0m$ ';
    expect(stripQueries(prompt)).toBe(prompt);
  });
});
