import { existsSync, mkdirSync, readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { spawn } from 'node:child_process';

export function parseArgs(argv) {
  const parsed = { flags: new Set(), values: new Map(), positionals: [] };

  for (let index = 0; index < argv.length; index += 1) {
    const arg = argv[index];
    if (arg === '--') continue;
    if (!arg.startsWith('--')) {
      parsed.positionals.push(arg);
      continue;
    }

    const eq = arg.indexOf('=');
    if (eq !== -1) {
      parsed.values.set(arg.slice(2, eq), arg.slice(eq + 1));
      continue;
    }

    const key = arg.slice(2);
    const next = argv[index + 1];
    if (next && !next.startsWith('--')) {
      parsed.values.set(key, next);
      index += 1;
    } else {
      parsed.flags.add(key);
    }
  }

  return parsed;
}

export function option(args, name, fallback = undefined) {
  return args.values.has(name) ? args.values.get(name) : fallback;
}

export function flag(args, name) {
  return args.flags.has(name);
}

export function readEnvFile(filePath, { optional = false } = {}) {
  if (!existsSync(filePath)) {
    if (optional) return {};
    throw new Error(`Env file not found: ${filePath}`);
  }

  const env = {};
  const content = readFileSync(filePath, 'utf8');
  for (const rawLine of content.split(/\r?\n/)) {
    const line = rawLine.trim();
    if (!line || line.startsWith('#')) continue;
    const eq = line.indexOf('=');
    if (eq === -1) continue;
    const key = line.slice(0, eq).trim();
    let value = line.slice(eq + 1).trim();
    if (
      (value.startsWith('"') && value.endsWith('"')) ||
      (value.startsWith("'") && value.endsWith("'"))
    ) {
      value = value.slice(1, -1);
    }
    env[key] = value;
  }
  return env;
}

export function ensureParentDir(filePath) {
  mkdirSync(dirname(filePath), { recursive: true });
}

export function timestampSlug(date = new Date()) {
  return date.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z');
}

export function shellQuote(value) {
  if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) return value;
  return `'${value.replace(/'/g, "'\\''")}'`;
}

export function commandToString(command, { stdin, stdout } = {}) {
  const rendered = command.map((part) => shellQuote(part)).join(' ');
  const withStdin = stdin ? `${rendered} < ${shellQuote(stdin)}` : rendered;
  return stdout ? `${withStdin} > ${shellQuote(stdout)}` : withStdin;
}

export function composePrefix({ envFile, composeFile }) {
  return ['docker', 'compose', '--env-file', envFile, '-f', composeFile];
}

export function resolvePath(path) {
  return resolve(process.cwd(), path);
}

export function runCommand(command, options = {}) {
  return new Promise((resolveRun, rejectRun) => {
    const child = spawn(command[0], command.slice(1), {
      stdio: options.stdio ?? 'inherit',
      cwd: options.cwd ?? process.cwd(),
    });

    child.on('error', rejectRun);
    child.on('close', (code) => {
      if (code === 0) {
        resolveRun();
      } else {
        rejectRun(new Error(`Command failed (${code}): ${commandToString(command)}`));
      }
    });
  });
}

export function quotePgLiteral(value) {
  return `'${value.replace(/'/g, "''")}'`;
}
