// src/utils.ts
import type { Container } from 'dockerode';
import type { Readable } from 'stream';
import fs from 'fs';

/**
 * Stream a container’s stdout+stderr and resolve as soon as we see `pattern`.
 * Rejects if we hit the timeout first.
 */
export function waitForLog(
  container: Container,
  pattern: RegExp,
  timeoutMs: number
): Promise<void> {
  return new Promise((resolve, reject) => {
    let logStream: Readable;

    const timer = setTimeout(() => {
      // on timeout, destroy the stream to stop Docker pulling logs
      logStream.destroy();
      reject(new Error(`Timed out waiting for log ${pattern}`));
    }, timeoutMs);

    // cast to any so TS won't try to pick the wrong overload
    (container.logs as any)(
      {
        stdout: true,
        stderr: true,
        follow: true,
        since: 0,
      },
      (err: any, stream: any) => {
        if (err) {
          clearTimeout(timer);
          return reject(err);
        }

        // At runtime Dockerode gives a Node Readable here
        logStream = stream as Readable;

        logStream.on('data', (chunk: Buffer) => {
          const text = chunk.toString('utf8');
          if (pattern.test(text)) {
            clearTimeout(timer);
            logStream.destroy();
            resolve();
          }
        });

        logStream.on('error', (streamErr: Error) => {
          clearTimeout(timer);
          reject(streamErr);
        });
      }
    );
  });
}

export function extractText(result: any): string {
  const blocks = result?.content;
  if (!Array.isArray(blocks)) return '';

  return blocks
    .filter(
      (b: any): b is { type: string; text: string } =>
        b && typeof b === 'object' && b.type === 'text' && typeof b.text === 'string'
    )
    .map((b) => b.text)
    .join('');
}

/**
 * Expand ${VAR} or $VAR placeholders using the current process.env.
 * Throws if a referenced VAR is not set to avoid silent misconfiguration.
 */
export function expandEnvVars(input: string, env: NodeJS.ProcessEnv = process.env): string {
  if (!input) return input;
  // ${VAR} first
  let out = input.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => {
    const v = env[name];
    if (v === undefined) throw new Error(`Environment variable ${name} is not set`);
    return String(v);
  });
  // then $VAR (avoid $$ and already-expanded)
  out = out.replace(/(?<!\$)\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, name) => {
    const v = env[name];
    if (v === undefined) throw new Error(`Environment variable ${name} is not set`);
    return String(v);
  });
  return out;
}

/** Expand all string values in a key/value env map */
export function expandEnvMap<T extends Record<string, any> | undefined>(
  map: T,
  env: NodeJS.ProcessEnv = process.env
): Record<string, string> | undefined {
  if (!map) return undefined;
  const out: Record<string, string> = {};
  for (const [k, v] of Object.entries(map)) {
    if (v === undefined || v === null) continue;
    out[k] = typeof v === 'string' ? expandEnvVars(v, env) : String(v);
  }
  return out;
}

// Create directory if missing; return true if created in this run
export function ensureDir(dir: string): boolean {
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
    return true;
  }
  return false;
}
