import type { Client, ClientChannel } from "ssh2";

export function execCommand(
  client: Client,
  command: string,
  timeoutMs = 30000,
): Promise<{
  stdout: string;
  stderr: string;
  code: number | null;
}> {
  return new Promise((resolve, reject) => {
    let settled = false;
    let stream: ClientChannel | null = null;

    const timeout = setTimeout(() => {
      if (!settled) {
        settled = true;
        cleanup();
        reject(new Error(`Command timeout after ${timeoutMs}ms: ${command}`));
      }
    }, timeoutMs);

    const cleanup = () => {
      clearTimeout(timeout);
      if (stream) {
        try {
          stream.removeAllListeners();
          if (stream.stderr) {
            stream.stderr.removeAllListeners();
          }
          stream.destroy();
        } catch {
          // expected - cleanup errors ignored
        }
      }
    };

    client.exec(command, { pty: false }, (err, _stream) => {
      if (err) {
        if (!settled) {
          settled = true;
          cleanup();
          reject(err);
        }
        return;
      }

      stream = _stream;
      let stdout = "";
      let stderr = "";
      let exitCode: number | null = null;

      stream
        .on("close", (code: number | undefined) => {
          if (!settled) {
            settled = true;
            exitCode = typeof code === "number" ? code : null;
            cleanup();
            resolve({ stdout, stderr, code: exitCode });
          }
        })
        .on("data", (data: Buffer) => {
          stdout += data.toString("utf8");
        })
        .on("error", (streamErr: Error) => {
          if (!settled) {
            settled = true;
            cleanup();
            reject(streamErr);
          }
        });

      if (stream.stderr) {
        stream.stderr
          .on("data", (data: Buffer) => {
            stderr += data.toString("utf8");
          })
          .on("error", (stderrErr: Error) => {
            if (!settled) {
              settled = true;
              cleanup();
              reject(stderrErr);
            }
          });
      }
    });
  });
}

export function toFixedNum(
  n: number | null | undefined,
  digits = 2,
): number | null {
  if (typeof n !== "number" || !Number.isFinite(n)) return null;
  return Number(n.toFixed(digits));
}

export function kibToGiB(kib: number): number {
  return kib / (1024 * 1024);
}
