import {
  buildAgentSendCommand,
  buildTmuxListPanesCommand,
  getRosterAgent,
  loadFleetRoster,
  parseTmuxListPanes,
  resolveFleetPaths,
  RUNTIME_ACCEPTABLE_COMMANDS,
  socketArgs,
  type CommandResult,
  type CommandRunner,
  type FleetRoster,
} from '../commands/fleet.js';

export type FleetRuntimeTransportErrorCode =
  | 'forbidden'
  | 'invalid_request'
  | 'not_found'
  | 'unavailable';

/** A roster-bound, verified tmux target. It never accepts a caller-selected socket. */
export interface FleetRuntimeTarget {
  id: string;
  runtimeId: string;
  socketName: string;
}

/** Narrow transport boundary consumed by the runtime provider. */
export interface FleetRuntimeTransport {
  verifySession(sessionId: string): Promise<FleetRuntimeTarget>;
  listSessions(): Promise<FleetRuntimeTarget[]>;
  sendMessage(sessionId: string, message: string, sourceLabel: string): Promise<void>;
  terminate(sessionId: string): Promise<void>;
}

export interface FleetTmuxRuntimeTransportOptions {
  mosaicHome: string;
  rosterPath?: string;
  rosterLoader?: () => Promise<FleetRoster>;
  runner: CommandRunner;
}

/** A typed, fail-closed error for roster, target, identity, and tmux failures. */
export class FleetRuntimeTransportError extends Error {
  constructor(
    readonly code: FleetRuntimeTransportErrorCode,
    message: string,
  ) {
    super(message);
    this.name = FleetRuntimeTransportError.name;
  }
}

/**
 * Roster-bound transport for the local fleet tmux server. Every side-effecting
 * operation verifies an exact roster name, the roster socket, and the runtime
 * command in the exact pane before it invokes the maintained sender or tmux.
 */
export class FleetTmuxRuntimeTransport implements FleetRuntimeTransport {
  private readonly rosterLoader: () => Promise<FleetRoster>;

  constructor(private readonly options: FleetTmuxRuntimeTransportOptions) {
    this.rosterLoader =
      options.rosterLoader ??
      (() =>
        loadFleetRoster(options.rosterPath ?? resolveFleetPaths(options.mosaicHome).rosterPath));
  }

  async verifySession(sessionId: string): Promise<FleetRuntimeTarget> {
    const roster = await this.loadRoster();
    return this.verifyRosterSession(roster, sessionId);
  }

  async listSessions(): Promise<FleetRuntimeTarget[]> {
    const roster = await this.loadRoster();
    const targets = await Promise.all(
      roster.agents.map(async (agent): Promise<FleetRuntimeTarget | undefined> => {
        try {
          return await this.verifyRosterSession(roster, agent.name);
        } catch (error: unknown) {
          if (error instanceof FleetRuntimeTransportError && error.code === 'unavailable') {
            return undefined;
          }
          throw error;
        }
      }),
    );
    return targets.filter((target): target is FleetRuntimeTarget => target !== undefined);
  }

  async sendMessage(sessionId: string, message: string, sourceLabel: string): Promise<void> {
    if (message.length === 0) {
      throw new FleetRuntimeTransportError('invalid_request', 'Fleet message content is required');
    }
    if (sourceLabel.length === 0) {
      throw new FleetRuntimeTransportError(
        'invalid_request',
        'Fleet message source label is required',
      );
    }
    const target = await this.verifySession(sessionId);
    const command = buildAgentSendCommand(
      resolveFleetPaths(this.options.mosaicHome),
      target.id,
      message,
      target.socketName,
      sourceLabel,
    );
    await this.runChecked(command, 'Fleet message delivery failed');
  }

  async terminate(sessionId: string): Promise<void> {
    const target = await this.verifySession(sessionId);
    await this.runChecked(
      ['tmux', ...socketArgs(target.socketName), 'kill-session', '-t', `=${target.id}`],
      'Fleet session termination failed',
    );
  }

  private async loadRoster(): Promise<FleetRoster> {
    try {
      return await this.rosterLoader();
    } catch (error: unknown) {
      const message = error instanceof Error ? error.message : 'Fleet roster is unavailable';
      throw new FleetRuntimeTransportError(
        'unavailable',
        `Fleet roster is unavailable: ${message}`,
      );
    }
  }

  private async verifyRosterSession(
    roster: FleetRoster,
    sessionId: string,
  ): Promise<FleetRuntimeTarget> {
    const agent = this.exactRosterAgent(roster, sessionId);
    const socketName = roster.tmux.socketName;
    const result = await this.run(buildTmuxListPanesCommand(agent.name, socketName));
    if (result.exitCode !== 0) {
      throw new FleetRuntimeTransportError(
        'unavailable',
        `Fleet session is unavailable: ${agent.name}`,
      );
    }
    const pane = parseTmuxListPanes(result.stdout);
    if (pane.dead || pane.command === null) {
      throw new FleetRuntimeTransportError(
        'unavailable',
        `Fleet session is unavailable: ${agent.name}`,
      );
    }
    if (!hasExactRuntimeIdentity(agent.runtime, pane.command)) {
      throw new FleetRuntimeTransportError(
        'forbidden',
        `Fleet runtime identity mismatch: ${agent.name}`,
      );
    }
    return { id: agent.name, runtimeId: agent.runtime, socketName };
  }

  private exactRosterAgent(roster: FleetRoster, sessionId: string): FleetRoster['agents'][number] {
    try {
      const agent = getRosterAgent(roster, sessionId);
      if (agent.name !== sessionId) {
        throw new FleetRuntimeTransportError('not_found', 'Fleet session target must be exact');
      }
      return agent;
    } catch (error: unknown) {
      if (error instanceof FleetRuntimeTransportError) {
        throw error;
      }
      throw new FleetRuntimeTransportError('not_found', 'Fleet session target is not roster-bound');
    }
  }

  private async run(command: string[]): Promise<CommandResult> {
    const [executable, ...args] = command;
    if (executable === undefined) {
      throw new FleetRuntimeTransportError('invalid_request', 'Fleet command is required');
    }
    try {
      return await this.options.runner(executable, args);
    } catch (error: unknown) {
      const message = error instanceof Error ? error.message : 'Fleet command failed';
      throw new FleetRuntimeTransportError('unavailable', message);
    }
  }

  private async runChecked(command: string[], failureMessage: string): Promise<void> {
    const result = await this.run(command);
    if (result.exitCode !== 0) {
      throw new FleetRuntimeTransportError('unavailable', failureMessage);
    }
  }
}

function hasExactRuntimeIdentity(runtimeId: string, paneCommand: string): boolean {
  const expectedCommands = RUNTIME_ACCEPTABLE_COMMANDS[runtimeId];
  return expectedCommands !== undefined && expectedCommands.includes(paneCommand);
}
