import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import type { Request, Response, NextFunction } from "express";

const ORIGINAL_ENV = { ...process.env };

function fakeReqRes(authHeader?: string) {
  const req = { get: (name: string) => (name === "Authorization" ? authHeader : undefined) } as unknown as Request;
  const json = vi.fn();
  const status = vi.fn().mockReturnValue({ json });
  const res = { status } as unknown as Response;
  const next = vi.fn() as unknown as NextFunction;
  return { req, res, next, status, json };
}

// auth.ts reads UPSTREAM_API_SECRET and NODE_ENV at import time, so every
// test that needs a specific combination re-imports the module fresh via
// vi.resetModules() rather than mutating a module already loaded once.
async function importAuth() {
  vi.resetModules();
  return import("./auth");
}

describe("requireUpstreamSecret", () => {
  beforeEach(() => {
    process.env = { ...ORIGINAL_ENV };
  });
  afterEach(() => {
    process.env = { ...ORIGINAL_ENV };
  });

  it("refuses to even load in production with no secret configured", async () => {
    process.env.NODE_ENV = "production";
    delete process.env.UPSTREAM_API_SECRET;
    await expect(importAuth()).rejects.toThrow(/refusing to start/i);
  });

  it("loads fine in production once a secret is configured", async () => {
    process.env.NODE_ENV = "production";
    process.env.UPSTREAM_API_SECRET = "real-secret";
    await expect(importAuth()).resolves.toBeDefined();
  });

  it("in dev mode with no secret, lets every request through", async () => {
    process.env.NODE_ENV = "development";
    delete process.env.UPSTREAM_API_SECRET;
    const { requireUpstreamSecret } = await importAuth();
    const { req, res, next } = fakeReqRes();
    requireUpstreamSecret(req, res, next);
    expect(next).toHaveBeenCalledOnce();
  });

  it("rejects a request with no bearer token once a secret is configured", async () => {
    process.env.NODE_ENV = "development";
    process.env.UPSTREAM_API_SECRET = "real-secret";
    const { requireUpstreamSecret } = await importAuth();
    const { req, res, next, status, json } = fakeReqRes();
    requireUpstreamSecret(req, res, next);
    expect(next).not.toHaveBeenCalled();
    expect(status).toHaveBeenCalledWith(401);
    expect(json).toHaveBeenCalledWith(
      expect.objectContaining({ error: expect.objectContaining({ code: "UNAUTHORIZED" }) })
    );
  });

  it("rejects a request with the wrong bearer token", async () => {
    process.env.NODE_ENV = "development";
    process.env.UPSTREAM_API_SECRET = "real-secret";
    const { requireUpstreamSecret } = await importAuth();
    const { req, res, next, status } = fakeReqRes("Bearer wrong-secret");
    requireUpstreamSecret(req, res, next);
    expect(next).not.toHaveBeenCalled();
    expect(status).toHaveBeenCalledWith(401);
  });

  it("accepts a request with the correct bearer token", async () => {
    process.env.NODE_ENV = "development";
    process.env.UPSTREAM_API_SECRET = "real-secret";
    const { requireUpstreamSecret } = await importAuth();
    const { req, res, next, status } = fakeReqRes("Bearer real-secret");
    requireUpstreamSecret(req, res, next);
    expect(next).toHaveBeenCalledOnce();
    expect(status).not.toHaveBeenCalled();
  });
});
