import { test, expect } from "@playwright/test";
import { existsSync } from "node:fs";
import { join } from "node:path";
import { BASE_PATH } from "../../playwright.config";
import {
  STANDALONE_DIR,
  sendmailEnvFor,
  startApp,
  startFakeSendmail,
  startFakeSmtp,
  smtpEnvFor,
  validSubmission,
} from "../helpers/harness";

/**
 * Regression cover for the outage on test and prod.
 *
 * The suite used to hand SMTP settings to the server as process environment
 * variables, which is not how the deployed server gets them. On the VPS the
 * settings live in `.env.local`, and `.next/standalone/server.js` chdirs into
 * its own directory before Next reads env files — so a `.env.local` sitting at
 * the repo root is never seen. Every page still rendered; only sendMail failed,
 * with `ECONNREFUSED 127.0.0.1:465`, because SMTP_HOST was undefined.
 *
 * These tests start the bundle with a deliberately bare environment, so the
 * only way they can pass is if the deploy assembly step really does put the
 * config where the running server looks for it.
 */

test.describe.configure({ mode: "serial" });

test("the build step copies .env.local into the standalone bundle", () => {
  // `npm run build:standalone` copies it when present. It is absent in CI,
  // where the settings arrive as real environment variables instead.
  const rootEnv = existsSync(join(STANDALONE_DIR, "..", "..", ".env.local"));
  test.skip(!rootEnv, "no .env.local at the repo root (expected in CI)");

  expect(
    existsSync(join(STANDALONE_DIR, ".env.local")),
    "Run `npm run build:standalone`: the standalone server reads .env.local from its own " +
      "directory, so the deploy must copy it in beside server.js.",
  ).toBe(true);
});

test("sends mail with config supplied only via the bundle's .env.local", async () => {
  const smtp = await startFakeSmtp();
  const app = await startApp({
    env: {}, // bare: nothing but PATH/PORT/HOSTNAME, exactly like the systemd unit
    envFile: smtpEnvFor(smtp.port),
    basePath: BASE_PATH,
  });

  try {
    const health = await app.get("/api/health/mail");
    expect(health.status, `health check failed:\n${app.output()}`).toBe(200);

    const res = await app.post("/api/contact", validSubmission());

    expect(res.status, `contact POST failed:\n${app.output()}`).toBe(200);
    expect(smtp.messages).toHaveLength(1);
    expect(smtp.messages[0].to).toEqual(["inbox@clearfin.test"]);
  } finally {
    await app.close();
    await smtp.close();
  }
});

// What the VPS actually runs: no credentials in the app, delivery handed to the
// local relay, and the whole config arriving through the bundle's env file.
test("pipes to the local relay with config supplied only via .env.local", async () => {
  const sendmail = startFakeSendmail();
  const app = await startApp({
    env: {},
    envFile: sendmailEnvFor(sendmail),
    basePath: BASE_PATH,
  });

  try {
    const health = await app.get("/api/health/mail");
    expect(health.status, `health check failed:\n${app.output()}`).toBe(200);
    expect(health.json.transport).toBe("sendmail");

    const res = await app.post("/api/contact", validSubmission());

    expect(res.status, `contact POST failed:\n${app.output()}`).toBe(200);
    expect(sendmail.invocations()).toHaveLength(1);
    expect(sendmail.invocations()[0].args.at(-1)).toBe("inbox@clearfin.test");
  } finally {
    await app.close();
    sendmail.cleanup();
  }
});

test("an unconfigured server answers 503 instead of a generic failure", async () => {
  const app = await startApp({ env: {}, envFile: null, basePath: BASE_PATH });

  try {
    const health = await app.get("/api/health/mail");
    expect(health.status).toBe(503);
    // With nothing configured the local relay is assumed, so the addresses are
    // the only things it can report as missing.
    expect(health.json).toMatchObject({ ok: false, configured: false, transport: "sendmail" });
    expect(health.json.missing).toEqual(["SMTP_FROM", "CONTACT_TO"]);

    const res = await app.post("/api/contact", validSubmission());

    // Previously this was a 500 after nodemailer timed out against localhost:465.
    expect(res.status).toBe(503);
    expect(res.json.error).toContain("temporarily unavailable");
    expect(app.output()).toContain("Contact form is not configured");
  } finally {
    await app.close();
  }
});

test("the health check names exactly which variable is missing", async () => {
  const smtp = await startFakeSmtp();
  const { SMTP_PASS: _dropped, ...incomplete } = smtpEnvFor(smtp.port);
  const app = await startApp({ env: {}, envFile: incomplete, basePath: BASE_PATH });

  try {
    const health = await app.get("/api/health/mail");
    expect(health.status).toBe(503);
    expect(health.json.missing).toEqual(["SMTP_PASS"]);
  } finally {
    await app.close();
    await smtp.close();
  }
});
