/* @vitest-environment node */

import { describe, it, expect } from "vitest";

import {
	MUST_CHANGE_PASSWORD_GATE_PARAM,
	MUST_CHANGE_PASSWORD_GATE_VALUE,
	isProfilePath,
	sanitizeMustChangePasswordNext,
	shouldRedirectToProfileForPasswordChange,
	buildMustChangePasswordRedirectUrl,
	parseMustChangePasswordGateParams,
	resolveMustChangePasswordResumePath,
} from "./mustChangePasswordGate.js";

describe("lib/frontend/auth/mustChangePasswordGate", () => {
	describe("isProfilePath", () => {
		it("returns true for /profile and profile subpaths", () => {
			expect(isProfilePath("/profile")).toBe(true);
			expect(isProfilePath("/profile/security")).toBe(true);
		});

		it("returns false for non-profile routes", () => {
			expect(isProfilePath("/")).toBe(false);
			expect(isProfilePath("/NL01")).toBe(false);
			expect(isProfilePath("/NL01/search")).toBe(false);
		});
	});

	describe("sanitizeMustChangePasswordNext", () => {
		it("accepts safe internal non-profile targets", () => {
			expect(sanitizeMustChangePasswordNext("/NL01")).toBe("/NL01");
			expect(sanitizeMustChangePasswordNext("/NL01/search?scope=all")).toBe(
				"/NL01/search?scope=all"
			);
		});

		it("rejects unsafe or profile targets", () => {
			expect(sanitizeMustChangePasswordNext("//evil.com")).toBe(null);
			expect(sanitizeMustChangePasswordNext("https://evil.com")).toBe(null);
			expect(sanitizeMustChangePasswordNext("/profile")).toBe(null);
			expect(sanitizeMustChangePasswordNext("/profile?x=1")).toBe(null);
		});
	});

	describe("shouldRedirectToProfileForPasswordChange", () => {
		it("forces redirect for non-profile routes while flag is true", () => {
			expect(
				shouldRedirectToProfileForPasswordChange({
					pathname: "/NL01",
					mustChangePassword: true,
				})
			).toBe(true);
		});

		it("does not force redirect when already on profile or flag is false", () => {
			expect(
				shouldRedirectToProfileForPasswordChange({
					pathname: "/profile",
					mustChangePassword: true,
				})
			).toBe(false);

			expect(
				shouldRedirectToProfileForPasswordChange({
					pathname: "/NL01",
					mustChangePassword: false,
				})
			).toBe(false);
		});
	});

	describe("buildMustChangePasswordRedirectUrl", () => {
		it("builds profile URL with marker and safe next", () => {
			expect(buildMustChangePasswordRedirectUrl("/NL01/search")).toBe(
				"/profile?mustChangePasswordGate=1&next=%2FNL01%2Fsearch"
			);
		});

		it("always includes gate marker and drops invalid next", () => {
			expect(buildMustChangePasswordRedirectUrl("//evil.com")).toBe(
				`/profile?${MUST_CHANGE_PASSWORD_GATE_PARAM}=${MUST_CHANGE_PASSWORD_GATE_VALUE}`
			);
		});
	});

	describe("parseMustChangePasswordGateParams", () => {
		it("parses marker and next from URLSearchParams", () => {
			const sp = new URLSearchParams({
				mustChangePasswordGate: "1",
				next: "/NL01",
			});

			expect(parseMustChangePasswordGateParams(sp)).toEqual({
				isGateMarker: true,
				next: "/NL01",
			});
		});

		it("normalizes invalid input", () => {
			const sp = new URLSearchParams({
				mustChangePasswordGate: "yes",
				next: "/profile",
			});

			expect(parseMustChangePasswordGateParams(sp)).toEqual({
				isGateMarker: false,
				next: null,
			});
		});
	});

	describe("resolveMustChangePasswordResumePath", () => {
		it("returns next only when marker is set, route is profile, and flag is false", () => {
			const sp = new URLSearchParams({
				mustChangePasswordGate: "1",
				next: "/NL01/search",
			});

			expect(
				resolveMustChangePasswordResumePath({
					pathname: "/profile",
					searchParams: sp,
					mustChangePassword: false,
				})
			).toBe("/NL01/search");
		});

		it("returns null when any resume condition is not satisfied", () => {
			const sp = new URLSearchParams({
				mustChangePasswordGate: "1",
				next: "/NL01/search",
			});

			expect(
				resolveMustChangePasswordResumePath({
					pathname: "/profile",
					searchParams: sp,
					mustChangePassword: true,
				})
			).toBe(null);

			expect(
				resolveMustChangePasswordResumePath({
					pathname: "/NL01",
					searchParams: sp,
					mustChangePassword: false,
				})
			).toBe(null);

			expect(
				resolveMustChangePasswordResumePath({
					pathname: "/profile",
					searchParams: new URLSearchParams({
						mustChangePasswordGate: "0",
						next: "/NL01/search",
					}),
					mustChangePassword: false,
				})
			).toBe(null);
		});
	});
});
