import { createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { get } from "node:https";
import { dirname, resolve } from "node:path";
import { spawn, spawnSync } from "node:child_process";

const adminerFile = resolve("tools/adminer/adminer.php");
const localPhp = resolve("tools/php/php.exe");
const host = process.env.ADMINER_HOST || "127.0.0.1";
const port = process.env.ADMINER_PORT || "8080";
const adminerDownloadUrl = process.env.ADMINER_DOWNLOAD_URL || "https://www.adminer.org/latest.php";
const phpCommand = existsSync(localPhp) ? localPhp : "php";
const usesLocalPhp = existsSync(localPhp);

if (!commandExists(phpCommand)) {
  console.error("Adminer needs PHP CLI. Install PHP first, then run npm run adminer again.");
  process.exit(1);
}

if (usesLocalPhp) {
  ensureLocalPhpMysqlExtensions();
}

if (!existsSync(adminerFile)) {
  mkdirSync(dirname(adminerFile), { recursive: true });
  await downloadFile(adminerDownloadUrl, adminerFile);
}

const child = spawn(phpCommand, ["-S", `${host}:${port}`, adminerFile], {
  stdio: "inherit",
  shell: true,
});

child.on("exit", (code, signal) => {
  if (signal) {
    process.kill(process.pid, signal);
    return;
  }

  process.exit(code ?? 0);
});

function commandExists(command: string): boolean {
  const result = spawnSync(command, ["-v"], { stdio: "ignore", shell: true });
  return result.status === 0;
}

function ensureLocalPhpMysqlExtensions(): void {
  const phpDir = dirname(localPhp);
  const phpIni = resolve(phpDir, "php.ini");
  const template = resolve(phpDir, "php.ini-production");
  const source = existsSync(phpIni) ? phpIni : template;

  if (!existsSync(source)) {
    console.warn("Local PHP is missing php.ini-production; Adminer may not have MariaDB extensions enabled.");
    return;
  }

  let contents = readFileSync(source, "utf8");
  contents = ensureIniLine(contents, /^;?\s*extension_dir\s*=.*$/m, 'extension_dir = "ext"');
  contents = ensureIniLine(contents, /^;?\s*extension\s*=\s*mysqli\s*$/m, "extension=mysqli");
  contents = ensureIniLine(contents, /^;?\s*extension\s*=\s*pdo_mysql\s*$/m, "extension=pdo_mysql");
  writeFileSync(phpIni, contents);
}

function ensureIniLine(contents: string, pattern: RegExp, line: string): string {
  if (pattern.test(contents)) return contents.replace(pattern, line);
  return `${contents.trimEnd()}\n${line}\n`;
}

function downloadFile(url: string, target: string): Promise<void> {
  return new Promise((resolveDownload, rejectDownload) => {
    const request = get(url, (response) => {
      if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
        downloadFile(response.headers.location, target).then(resolveDownload, rejectDownload);
        return;
      }

      if (response.statusCode !== 200) {
        rejectDownload(new Error(`Adminer download failed with HTTP ${response.statusCode}`));
        return;
      }

      const file = createWriteStream(target);
      response.pipe(file);
      file.on("finish", () => {
        file.close();
        resolveDownload();
      });
      file.on("error", rejectDownload);
    });

    request.on("error", rejectDownload);
  });
}
