/* eslint-disable no-console */
const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");

const backendRoot = path.resolve(__dirname, "..");

const resolveDatabaseUrl = (rawUrl) => {
  const defaultDbPath = path.resolve(backendRoot, "prisma/dev.db");

  if (!rawUrl || String(rawUrl).trim().length === 0) {
    return `file:${defaultDbPath}`;
  }

  if (!String(rawUrl).startsWith("file:")) {
    return String(rawUrl);
  }

  const filePath = String(rawUrl).replace(/^file:/, "");
  const prismaDir = path.resolve(backendRoot, "prisma");
  const normalizedRelative = filePath.replace(/^\.\/?/, "");
  const hasLeadingPrismaDir =
    normalizedRelative === "prisma" || normalizedRelative.startsWith("prisma/");

  const absolutePath = path.isAbsolute(filePath)
    ? filePath
    : path.resolve(
        hasLeadingPrismaDir ? backendRoot : prismaDir,
        normalizedRelative,
      );

  return `file:${absolutePath}`;
};

const databaseUrl = resolveDatabaseUrl(process.env.DATABASE_URL);
process.env.DATABASE_URL = databaseUrl;

const nodeEnv = process.env.NODE_ENV || "development";

const runCapture = (cmd) => {
  try {
    const stdout = execSync(cmd, {
      cwd: backendRoot,
      encoding: "utf8",
      stdio: ["ignore", "pipe", "pipe"],
      env: { ...process.env, DATABASE_URL: databaseUrl },
    });
    return { ok: true, stdout: stdout || "", stderr: "" };
  } catch (error) {
    const err = error;
    const stderr =
      err && err.stderr
        ? Buffer.isBuffer(err.stderr)
          ? err.stderr.toString("utf8")
          : String(err.stderr)
        : "";
    const stdout =
      err && err.stdout
        ? Buffer.isBuffer(err.stdout)
          ? err.stdout.toString("utf8")
          : String(err.stdout)
        : "";
    return { ok: false, stdout, stderr, error: err };
  }
};

const run = (cmd) => {
  execSync(cmd, {
    cwd: backendRoot,
    stdio: "inherit",
    env: { ...process.env, DATABASE_URL: databaseUrl },
  });
};

const getDbFilePath = () => {
  if (!databaseUrl.startsWith("file:")) return null;
  return databaseUrl.replace(/^file:/, "");
};

const backupDbIfPresent = () => {
  const dbPath = getDbFilePath();
  if (!dbPath) return null;
  if (!fs.existsSync(dbPath)) return null;

  const dir = path.dirname(dbPath);
  const base = path.basename(dbPath, path.extname(dbPath));
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
  const backupPath = path.join(dir, `${base}.${stamp}.backup`);

  fs.copyFileSync(dbPath, backupPath);
  return backupPath;
};

const isNonProd = nodeEnv !== "production";
const isFileDb = databaseUrl.startsWith("file:");

let deploy = runCapture("npx prisma migrate deploy");

if (!deploy.ok) {
  console.warn(
    `[predev] Prisma migrate deploy failed. Attempting pnpm exec...`,
  );
  deploy = runCapture("pnpm exec prisma migrate deploy");
}

if (deploy.ok) {
  if (deploy.stdout) process.stdout.write(deploy.stdout);
} else {
  if (deploy.stdout) process.stdout.write(deploy.stdout);
  if (deploy.stderr) process.stderr.write(deploy.stderr);

  const stderr = deploy.stderr || "";
  const isP3005 = stderr.includes("P3005");

  // Common when an older dev.db exists but migrations weren't used previously.
  if (isNonProd && isFileDb && isP3005) {
    const backupPath = backupDbIfPresent();
    console.warn(
      `[predev] Prisma migrate baseline required (P3005). Resetting local SQLite database.\n` +
        `  DATABASE_URL=${databaseUrl}\n` +
        (backupPath ? `  Backup: ${backupPath}\n` : "") +
        `  If you need to preserve local data, restore the backup and baseline manually.`,
    );

    // check for npx, if not present try pnpm exec
    try {
      console.log(
        `[predev] Running: npx prisma migrate reset --force --skip-seed`,
      );
      run("npx prisma migrate reset --force --skip-seed");
    } catch {
      console.warn(`[predev] npx not found, trying pnpm exec...`);
      run("pnpm exec prisma migrate reset --force --skip-seed");
    }
  } else {
    throw deploy.error;
  }
}
