import { IPty, PtyEvent, IDisposable } from '../../src/types'

/**
 * Mock IPty for testing.
 *
 * Implements the full IPty interface with manual trigger methods
 * (emitData, emitExit) and recording of calls (writes, resizes, killed).
 *
 * All properties are mutable for testing convenience.
 */
export interface MockIPty extends IPty {
  /** Recorded write() calls. */
  writes: string[]
  /** Recorded resize() calls. */
  resizes: Array<{ cols: number; rows: number }>
  /** Whether kill() was called. */
  killed: boolean
  /** Manually trigger onData listeners. */
  emitData(data: string): void
  /** Manually trigger onExit listeners. */
  emitExit(exitCode: number, signal?: number): void
}

/**
 * Create a mock PTY for testing.
 *
 * pid, cols, rows are fixed but can be overridden via options for specific tests.
 */
export function createMockPty(options?: {
  pid?: number
  cols?: number
  rows?: number
}): MockIPty {
  const pid = options?.pid ?? 12345
  let cols = options?.cols ?? 80
  let rows = options?.rows ?? 24

  const writes: string[] = []
  const resizes: Array<{ cols: number; rows: number }> = []
  let killed = false

  // Event listeners: store arrays of listeners for each event type
  const dataListeners: Array<(data: string) => void> = []
  const exitListeners: Array<(event: { exitCode: number; signal?: number }) => void> = []

  const onData: PtyEvent<string> = (listener: (e: string) => void): IDisposable => {
    dataListeners.push(listener)
    return {
      dispose() {
        const index = dataListeners.indexOf(listener)
        if (index !== -1) {
          dataListeners.splice(index, 1)
        }
      },
    }
  }

  const onExit: PtyEvent<{ exitCode: number; signal?: number }> = (
    listener: (e: { exitCode: number; signal?: number }) => void,
  ): IDisposable => {
    exitListeners.push(listener)
    return {
      dispose() {
        const index = exitListeners.indexOf(listener)
        if (index !== -1) {
          exitListeners.splice(index, 1)
        }
      },
    }
  }

  const write = (data: string): void => {
    writes.push(data)
  }

  const resize = (newCols: number, newRows: number): void => {
    cols = newCols
    rows = newRows
    resizes.push({ cols: newCols, rows: newRows })
  }

  const kill = (signal?: string): void => {
    killed = true
  }

  const emitData = (data: string): void => {
    for (const listener of dataListeners) {
      listener(data)
    }
  }

  const emitExit = (exitCode: number, signal?: number): void => {
    const event: { exitCode: number; signal?: number } = { exitCode }
    if (signal !== undefined) {
      event.signal = signal
    }
    for (const listener of exitListeners) {
      listener(event)
    }
  }

  return {
    pid,
    get cols() {
      return cols
    },
    get rows() {
      return rows
    },
    onData,
    onExit,
    write,
    resize,
    kill,
    writes,
    resizes,
    get killed() {
      return killed
    },
    emitData,
    emitExit,
  }
}
