/**
 * Demotion, not fatality.
 *
 * Every case here is a misconfiguration that an earlier design would have
 * thrown on at boot. The rule is that the server comes up, says exactly what it
 * gave up on, and serves the degraded body — a self-hoster who typed the wrong
 * thing gets a working map and a sentence explaining it, not a process that
 * refuses to start. CONTRACT.md §5.1.
 */

import assert from "node:assert/strict";
import { after, describe, it } from "node:test";
import { buildApp } from "../app.ts";
import { loadConfig } from "../config.ts";
import type { HealthBody, WeatherBody } from "../../../src/server/wire.ts";

function appWith(env: Record<string, string>) {
  const config = loadConfig(env);
  config.logLevel = "silent";
  return { config, app: buildApp(config) };
}

describe("a weather source configured without a contact", () => {
  it("demotes to synthetic and says why", async () => {
    const { config, app } = appWith({ TERA_WEATHER_SOURCE: "nws" });
    after(() => app.close());

    assert.equal(config.weather.source, "none");
    assert.equal(config.degraded.length, 1);
    assert.match(config.degraded[0] ?? "", /TERA_WEATHER_CONTACT/);

    const health = (await app.inject({ method: "GET", url: "/api/v1/health" })).json<HealthBody>();
    assert.equal(health.ok, true);
    assert.equal(health.sources.weather, "none");
    assert.equal(health.degraded.length, 1);

    const weather = (
      await app.inject({ method: "GET", url: "/api/v1/weather" })
    ).json<WeatherBody>();
    assert.equal(weather.synthetic, true);
  });

  it("keeps the source once a contact is present", () => {
    const { config, app } = appWith({
      TERA_WEATHER_SOURCE: "nws",
      TERA_WEATHER_CONTACT: "ops@example.com",
    });
    after(() => app.close());
    assert.equal(config.weather.source, "nws");
    assert.deepEqual(config.degraded, []);
  });
});

describe("other misconfigurations", () => {
  it("falls back on an unknown source name rather than exiting", () => {
    const { config, app } = appWith({ TERA_WEATHER_SOURCE: "accuweather" });
    after(() => app.close());
    assert.equal(config.weather.source, "none");
    assert.match(config.degraded[0] ?? "", /accuweather/);
  });

  it("falls back on a port that is not a number", () => {
    const config = loadConfig({ TERA_PORT: "banana" });
    assert.equal(config.port, 8431);
    assert.match(config.degraded[0] ?? "", /TERA_PORT/);
  });

  it("records that Open-Meteo is a non-commercial tier without disabling it", () => {
    const config = loadConfig({ TERA_WEATHER_SOURCE: "openmeteo" });
    assert.equal(config.weather.source, "openmeteo");
    assert.match(config.degraded[0] ?? "", /non-commercial/);
  });

  it("demotes sso with nowhere to revalidate, taking private offices with it", () => {
    const config = loadConfig({ TERA_AUTH_MODE: "sso", TERA_AUTH_ENTRY_URL: "https://example" });
    assert.equal(config.auth.mode, "none");
    assert.match(config.degraded[0] ?? "", /TERA_AUTH_REVALIDATE_URL/);
  });

  it("demotes jwt with no secret and no JWKS", () => {
    const config = loadConfig({ TERA_AUTH_MODE: "jwt" });
    assert.equal(config.auth.mode, "none");
  });

  it("demotes dump1090 with no path to read", () => {
    const config = loadConfig({ TERA_FLIGHTS_SOURCE: "dump1090" });
    assert.equal(config.flights.source, "sim");
  });

  it("demotes a file marker source with no file", () => {
    const config = loadConfig({ TERA_MARKERS_SOURCE: "file" });
    assert.equal(config.markers.source, "none");
  });

  it("says so when admins are listed on a box where nobody can sign in", () => {
    // Nothing falls back here — there is nothing to fall back to — but the
    // operator who wrote a name into TERA_ADMIN_SUBJECTS is owed the sentence
    // explaining why that name is not getting the time controls.
    const config = loadConfig({ TERA_ADMIN_SUBJECTS: "karti" });
    assert.equal(config.auth.mode, "none");
    assert.deepEqual(config.auth.admins, { everyone: false, subjects: ["karti"] });
    assert.match(config.degraded[0] ?? "", /TERA_ADMIN_SUBJECTS/);
    assert.match(config.degraded[0] ?? "", /mode=none/);
  });

  it("records the wildcard as its own line, on top of any other demotion", () => {
    const config = loadConfig({ TERA_AUTH_MODE: "jwt", TERA_ADMIN_SUBJECTS: "*" });
    // The jwt demotion, the wildcard, and the fact that the wildcard cannot
    // reach anybody on a box that just closed itself: three separate facts, and
    // health prints all three rather than the first one that happened.
    assert.equal(config.degraded.length, 3);
    assert.ok(config.degraded.some((line) => line.includes("TERA_ADMIN_SUBJECTS=*")));
  });
});
