// Unit tests for server/src/localdb/manifest.ts (PG Task 1). Pure — a table and
// a lookup. Nothing here fetches anything; that is exactly the property under
// test for the macOS rows.

import { describe, it, expect } from "vitest";
import {
  RUNTIME_MANIFEST,
  UPSTREAM_WINDOWS_ARCHIVE,
  RUNTIME_BASE_URL,
  SHIPPED_POSTGRES_VERSION,
  resolveArtifact,
  runtimeTarballName,
  checksumMatches,
  type ArtifactEntry,
} from "../../server/src/localdb/manifest.js";
import { PLATFORM_IDS, type PlatformId } from "../../server/src/localdb/platform.js";
import { runtimeDirName } from "../../server/src/localdb/paths.js";

describe("RUNTIME_MANIFEST", () => {
  it("has a row for every published platform id — no undefined lookups possible", () => {
    for (const id of PLATFORM_IDS) {
      expect(RUNTIME_MANIFEST[id]).toBeDefined();
      expect(["published", "awaiting-publication", "not-built"]).toContain(RUNTIME_MANIFEST[id].state);
    }
  });

  it("states macOS as typed-absent, not as an entry with empty strings", () => {
    for (const id of ["darwin-arm64", "darwin-x64"] as const) {
      const entry = RUNTIME_MANIFEST[id];
      expect(entry.state).toBe("not-built");
      // The shape itself is the guarantee: there is no `url` or `sha256`
      // property to accidentally read as "" and hand to a downloader.
      expect(entry).not.toHaveProperty("artifact");
      expect(entry).not.toHaveProperty("url");
      expect(entry).not.toHaveProperty("sha256");
    }
  });

  it("carries the Windows row built by `npm run pg:stage` on 2026-08-08", () => {
    const entry = RUNTIME_MANIFEST["win32-x64"];
    expect(entry.state).toBe("published");
    if (entry.state !== "published") return;
    expect(entry.artifact.postgresVersion).toBe("16.10");
    // Fetched from balancedsum.com, never from EnterpriseDB — site/privacy.html
    // publishes that the app talks to exactly one host.
    expect(entry.artifact.url.startsWith(RUNTIME_BASE_URL)).toBe(true);
    expect(entry.artifact.url).not.toContain("enterprisedb");
    // A real digest of a real file, not a placeholder — 64 lowercase hex, and
    // not one of the shapes a "fill this in later" value takes.
    expect(entry.artifact.sha256).toMatch(/^[0-9a-f]{64}$/);
    expect(entry.artifact.sha256).not.toBe("0".repeat(64));
    expect(new Set(entry.artifact.sha256).size).toBeGreaterThan(4);
    // ~24.7 MB, per the decisions document's trimmed-tarball measurement.
    expect(entry.artifact.sizeBytes).toBeGreaterThan(20 * 1024 * 1024);
    expect(entry.artifact.sizeBytes).toBeLessThan(30 * 1024 * 1024);
  });

  it("names the tarball the same way the release script and the runtime directory do", () => {
    const entry = RUNTIME_MANIFEST["win32-x64"];
    if (entry.state !== "published") throw new Error("expected the win32-x64 row to be published");
    expect(entry.artifact.url).toBe(
      `${RUNTIME_BASE_URL}${runtimeTarballName(SHIPPED_POSTGRES_VERSION, "win32-x64")}`);
  });

  it("never points the app at EnterpriseDB, on any platform", () => {
    for (const id of PLATFORM_IDS) {
      expect(JSON.stringify(RUNTIME_MANIFEST[id])).not.toContain("enterprisedb");
    }
  });
});

describe("UPSTREAM_WINDOWS_ARCHIVE", () => {
  it("is the maintainer-side EnterpriseDB zip, verified 2026-08-08", () => {
    expect(UPSTREAM_WINDOWS_ARCHIVE.url).toBe(
      "https://get.enterprisedb.com/postgresql/postgresql-16.10-1-windows-x64-binaries.zip");
    expect(UPSTREAM_WINDOWS_ARCHIVE.postgresVersion).toBe(SHIPPED_POSTGRES_VERSION);
    // Stored lowercase; the document records it uppercase. checksumMatches is
    // the thing that reconciles those, and it is tested below.
    expect(UPSTREAM_WINDOWS_ARCHIVE.sha256).toMatch(/^[0-9a-f]{64}$/);
    expect(checksumMatches(
      UPSTREAM_WINDOWS_ARCHIVE.sha256,
      "EBB3B6AF4FA69DEA9951B66855BC4D42DC04E56CCB9AA7024CE3C58BD89D6B0C",
    )).toBe(true);
    // ~307.6 MB.
    expect(UPSTREAM_WINDOWS_ARCHIVE.sizeBytes).toBeGreaterThan(300 * 1024 * 1024);
  });
});

describe("resolveArtifact", () => {
  it("returns a typed 'not published yet' for macOS rather than a broken download", () => {
    for (const id of ["darwin-arm64", "darwin-x64"] as const) {
      const result = resolveArtifact(id);
      expect(result.ok).toBe(false);
      if (result.ok) return;
      expect(result.reason).toBe("not-built");
      expect(result.platform).toBe(id);
      expect(result.message).toContain("Mac");
    }
  });

  it("hands back the Windows artifact, checksum and all", () => {
    const result = resolveArtifact("win32-x64");
    expect(result.ok).toBe(true);
    if (!result.ok) return;
    expect(result.artifact.postgresVersion).toBe("16.10");
    expect(result.artifact.url).toBe("https://balancedsum.com/runtime/postgresql-16.10-win32-x64.tar.xz");
    expect(result.artifact.sha256).toMatch(/^[0-9a-f]{64}$/);
  });

  it("refuses a row whose tarball has been chosen but not yet built", () => {
    // The state a row occupies between "we picked an upstream build" and
    // "`npm run pg:stage` printed a digest". Exercised against a synthetic
    // table so the branch stays covered even when no real row is in it.
    const notYetBuilt: Readonly<Record<PlatformId, ArtifactEntry>> = {
      ...RUNTIME_MANIFEST,
      "darwin-arm64": {
        state: "awaiting-publication",
        postgresVersion: "16.10",
        plannedUrl: RUNTIME_BASE_URL + runtimeTarballName("16.10", "darwin-arm64"),
        approxSizeBytes: 25_000_000,
        why: "The Mac download hasn't been built yet.",
      },
    };
    const result = resolveArtifact("darwin-arm64", notYetBuilt);
    expect(result.ok).toBe(false);
    if (result.ok) return;
    expect(result.reason).toBe("awaiting-publication");
    expect(result.message.length).toBeGreaterThan(0);
  });

  it("never substitutes another platform's artifact — no Rosetta by accident", () => {
    const onlyIntel: Readonly<Record<PlatformId, ArtifactEntry>> = {
      ...RUNTIME_MANIFEST,
      "darwin-x64": {
        state: "published",
        artifact: {
          url: RUNTIME_BASE_URL + runtimeTarballName("16.10", "darwin-x64"),
          sha256: "b".repeat(64),
          sizeBytes: 1,
          postgresVersion: "16.10",
        },
      },
    };
    const armResult = resolveArtifact("darwin-arm64", onlyIntel);
    expect(armResult.ok).toBe(false);
  });
});

describe("runtimeTarballName", () => {
  it("agrees with the directory name the tarball unpacks into", () => {
    for (const id of PLATFORM_IDS) {
      expect(runtimeTarballName("16.10", id)).toBe(`${runtimeDirName("16.10", id)}.tar.xz`);
    }
  });
});

describe("checksumMatches", () => {
  const digest = "ebb3b6af4fa69dea9951b66855bc4d42dc04e56ccb9aa7024ce3c58bd89d6b0c";

  it("ignores case and surrounding whitespace, because the tools that print digests disagree", () => {
    expect(checksumMatches(digest, digest.toUpperCase())).toBe(true);
    expect(checksumMatches(digest.toUpperCase(), digest)).toBe(true);
    expect(checksumMatches(` ${digest}\n`, digest)).toBe(true);
  });

  it("rejects a different digest, including a one-character difference", () => {
    expect(checksumMatches(digest, `${digest.slice(0, 63)}d`)).toBe(false);
  });

  it("rejects empty, short and non-hex values on BOTH sides — '' must never equal ''", () => {
    expect(checksumMatches("", "")).toBe(false);
    expect(checksumMatches(digest, "")).toBe(false);
    expect(checksumMatches("", digest)).toBe(false);
    expect(checksumMatches(digest.slice(0, 32), digest.slice(0, 32))).toBe(false);
    expect(checksumMatches("z".repeat(64), "z".repeat(64))).toBe(false);
  });
});
