import { createInterface } from "node:readline/promises";
import { stdin as defaultInput, stdout as defaultOutput, stderr as defaultError } from "node:process";
import type { Readable, Writable } from "node:stream";
import type { Gateway, GatewaySubmitTurnInput } from "../../../gateway/index.js";
import { connectRemoteGatewayIfAvailable, type ProbeGatewayServerOptions } from "../../../gateway/index.js";
import type { ChannelAdapter, ChannelHandle, ChannelStartDeps } from "../protocol/ChannelAdapter.js";
import { renderCliEvent } from "./cli-render.js";

export type CliChannelOptions = {
  argv?: string[];
  projectKey?: string;
  sessionKey?: string;
  input?: Readable;
  output?: Writable;
  error?: Writable;
  probe?: ProbeGatewayServerOptions | false;
};

export class CliChannel implements ChannelAdapter {
  readonly channelKey = "cli";
  private stopped = false;

  constructor(private readonly options: CliChannelOptions = {}) {}

  async start(deps: ChannelStartDeps): Promise<ChannelHandle> {
    const gateway = await this.resolveGateway(deps.gateway);
    const prompt = this.options.argv?.join(" ").trim();
    if (prompt) {
      await this.submitAndRender(gateway, prompt);
    } else {
      await this.runInteractive(gateway);
    }
    return { stop: async () => this.stop() };
  }

  private async resolveGateway(fallback: Gateway): Promise<Gateway> {
    if (this.options.probe === false) {
      return fallback;
    }
    const remote = await connectRemoteGatewayIfAvailable(this.options.probe);
    if (remote) {
      return remote;
    }
    this.writeError("[pilotdeck] gateway server unavailable; using in-process mode.\n");
    return fallback;
  }

  private async runInteractive(gateway: Gateway): Promise<void> {
    const rl = createInterface({
      input: this.options.input ?? defaultInput,
      output: this.options.output ?? defaultOutput,
      terminal: false,
    });
    for await (const line of rl) {
      if (this.stopped) {
        break;
      }
      const text = line.trim();
      if (!text) {
        continue;
      }
      await this.submitAndRender(gateway, text);
      this.writeOutput("\n");
    }
  }

  private async submitAndRender(gateway: Gateway, message: string): Promise<void> {
    const input: GatewaySubmitTurnInput = {
      sessionKey: this.options.sessionKey ?? defaultCliSessionKey(this.options.projectKey),
      channelKey: "cli",
      projectKey: this.options.projectKey,
      message,
    };
    for await (const event of gateway.submitTurn(input)) {
      const rendered = renderCliEvent(event);
      if (rendered) {
        this.writeOutput(rendered);
      }
    }
  }

  private async stop(): Promise<void> {
    this.stopped = true;
  }

  private writeOutput(value: string): void {
    (this.options.output ?? defaultOutput).write(value);
  }

  private writeError(value: string): void {
    (this.options.error ?? defaultError).write(value);
  }
}

export function defaultCliSessionKey(projectKey = process.cwd()): string {
  return `cli:project=${projectKey}:default`;
}