import { HttpResponse, http } from "msw";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { mockUser } from "../../test/fixtures/user.fixture";
import { server } from "../../test/mocks/server";
import { useAuthStore } from "../store/useAuthStore";
import { api } from "./apiClient";

const VITE_API_URL = "http://piku-server";

beforeEach(() => {
  useAuthStore.setState({
    accessToken: null,
    user: null,
    isInitializing: false,
  });
  vi.stubEnv("VITE_API_URL", VITE_API_URL);
});

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

describe("request interceptor", () => {
  it("should attach Bearer token from the auth store to outgoing requests", async () => {
    useAuthStore.getState().setAuth("my-token", mockUser);

    let capturedAuthHeader = "";
    server.use(
      http.get(`${VITE_API_URL}/api/auth/me/`, ({ request }) => {
        capturedAuthHeader = request.headers.get("Authorization") ?? "";
        return HttpResponse.json(mockUser);
      }),
    );

    await api.get("/api/auth/me/");

    expect(capturedAuthHeader).toBe("Bearer my-token");
  });

  it("should not send Authorization header when the store has no token", async () => {
    let capturedAuthHeader: string | null = "";
    server.use(
      http.get(`${VITE_API_URL}/api/auth/me/`, ({ request }) => {
        capturedAuthHeader = request.headers.get("Authorization");
        return HttpResponse.json({});
      }),
    );

    await api.get("/api/auth/me/");

    expect(capturedAuthHeader).toBeNull();
  });
});

describe("response interceptor", () => {
  it("should call /refresh once on 401, then retry the original request with the new token", async () => {
    useAuthStore.getState().setAuth("expired-token", mockUser);
    let meApiCallCount = 0;
    let _refreshApiCallCount = 0;

    server.use(
      http.get(`${VITE_API_URL}/api/auth/me/`, ({ request }) => {
        meApiCallCount++;
        if (request.headers.get("Authorization") === "Bearer expired-token") {
          return new HttpResponse(null, { status: 401 });
        }
        return HttpResponse.json(mockUser);
      }),
      http.post(`${VITE_API_URL}/api/auth/refresh/`, () => {
        _refreshApiCallCount++;
        return HttpResponse.json({ access: "refreshed-token" });
      }),
    );

    const response = await api.get("/api/auth/me/");

    expect(_refreshApiCallCount).toBe(1);
    expect(meApiCallCount).toBe(2);
    expect(response.data).toEqual(mockUser);
  });

  it("should update the auth store access token after a successful refresh", async () => {
    useAuthStore.getState().setAuth("expired-token", mockUser);

    server.use(
      http.get(`${VITE_API_URL}/api/auth/me/`, ({ request }) => {
        if (request.headers.get("Authorization") === "Bearer expired-token") {
          return new HttpResponse(null, { status: 401 });
        }
        return HttpResponse.json(mockUser);
      }),
      http.post(`${VITE_API_URL}/api/auth/refresh/`, () =>
        HttpResponse.json({ access: "refreshed-token" }),
      ),
    );

    await api.get("/api/auth/me/");

    expect(useAuthStore.getState().accessToken).toBe("refreshed-token");
  });

  it("should call clearAuth and return the latest error when refresh also fails", async () => {
    useAuthStore.getState().setAuth("expired-token", mockUser);

    server.use(
      http.get(
        `${VITE_API_URL}/api/auth/me/`,
        () =>
          new HttpResponse(JSON.stringify({ detail: "Invalid token" }), {
            status: 401,
          }),
      ),
      http.post(
        `${VITE_API_URL}/api/auth/refresh/`,
        () =>
          new HttpResponse(JSON.stringify({ detail: "Refresh failed" }), {
            status: 401,
          }),
      ),
    );

    await expect(api.get("/api/auth/me/")).rejects.toThrow(
      expect.objectContaining({
        response: expect.objectContaining({
          data: { detail: "Refresh failed" },
        }),
      }),
    );
    expect(useAuthStore.getState().accessToken).toBeNull();
    expect(useAuthStore.getState().user).toBeNull();
  });
});
