import { NextRequest } from "next/server";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { clearStorefrontHostCache } from "@/lib/storefront/host";
import { proxy } from "../proxy";

function request(host: string, path = "/") {
  return new NextRequest(`https://${host}${path}`, { headers: { host } });
}

function resolvesTo(slug: string) {
  return vi.fn().mockResolvedValue({
    ok: true,
    json: async () => ({ data: { store_slug: slug } }),
  });
}

function resolvesToNothing() {
  return vi.fn().mockResolvedValue({ ok: false, status: 404, json: async () => ({}) });
}

let fetchMock: ReturnType<typeof vi.fn>;

function stubFetch(mock: ReturnType<typeof vi.fn>) {
  fetchMock = mock;
  vi.stubGlobal("fetch", mock);
}

beforeEach(() => {
  clearStorefrontHostCache();
  stubFetch(resolvesToNothing());
});

afterEach(() => {
  vi.unstubAllGlobals();
});

describe("check-in lane", () => {
  it("404s non-check-in paths on the check-in subdomain", async () => {
    const res = await proxy(request("checkin.alqove.com", "/items"));
    expect(res.status).toBe(404);
  });

  it("serves the check-in lane on its own subdomain", async () => {
    const res = await proxy(request("checkin.alqove.com", "/c/abc123"));
    expect(res.status).toBe(200);
  });
});

describe("storefront custom domains", () => {
  it("passes Alqove hosts through without a lookup", async () => {
    const res = await proxy(request("alqove.com", "/items"));

    expect(fetchMock).not.toHaveBeenCalled();
    expect(res.headers.get("x-middleware-rewrite")).toBeNull();
  });

  it("ignores the port when matching a platform host", async () => {
    await proxy(request("localhost:3000"));
    expect(fetchMock).not.toHaveBeenCalled();
  });

  it("rewrites a resolved custom domain onto the storefront route", async () => {
    stubFetch(resolvesTo("vintage-vault"));

    const res = await proxy(request("shop.example.com", "/"));

    expect(res.headers.get("x-middleware-rewrite")).toContain("/s/vintage-vault");
  });

  it("keeps the rest of the path when rewriting", async () => {
    stubFetch(resolvesTo("vintage-vault"));

    const res = await proxy(request("shop.example.com", "/hours"));

    expect(res.headers.get("x-middleware-rewrite")).toContain("/s/vintage-vault/hours");
  });

  it("caches the lookup instead of hitting the API on every request", async () => {
    stubFetch(resolvesTo("vintage-vault"));

    await proxy(request("shop.example.com"));
    await proxy(request("shop.example.com", "/about"));

    expect(fetchMock).toHaveBeenCalledTimes(1);
  });

  it("caches misses too, so an unknown host does not hammer the API", async () => {
    await proxy(request("unknown.example.com"));
    await proxy(request("unknown.example.com"));

    expect(fetchMock).toHaveBeenCalledTimes(1);
  });

  it("passes through unchanged when the host resolves to nothing", async () => {
    const res = await proxy(request("unknown.example.com"));
    expect(res.headers.get("x-middleware-rewrite")).toBeNull();
  });

  it("passes through when the API is unreachable rather than failing the request", async () => {
    stubFetch(vi.fn().mockRejectedValue(new Error("ECONNREFUSED")));

    const res = await proxy(request("down.example.com"));

    expect(res.headers.get("x-middleware-rewrite")).toBeNull();
  });

  it("does not double-prefix a path already inside the storefront group", async () => {
    stubFetch(resolvesTo("vintage-vault"));

    const res = await proxy(request("shop.example.com", "/s/vintage-vault/about"));

    expect(res.headers.get("x-middleware-rewrite")).toBeNull();
    expect(fetchMock).not.toHaveBeenCalled();
  });
});
