import { beforeAll, describe, expect, test } from "vitest";
import { createApp } from "~/server/app";
import {
	createTestSession,
	createTestSessionWithGlobalAdmin,
	createTestSessionWithOrgAdmin,
	createTestSessionWithRegularMember,
	getAuthHeaders,
} from "~/test/helpers/auth";
import { account } from "~/server/db/schema";
import { db } from "~/server/db/db";

const app = createApp();

describe("auth controller security", () => {
	let regularUserSession: Awaited<ReturnType<typeof createTestSession>>;
	let regularMemberSession: Awaited<ReturnType<typeof createTestSessionWithRegularMember>>;
	let orgAdminSession: Awaited<ReturnType<typeof createTestSessionWithOrgAdmin>>;
	let globalAdminSession: Awaited<ReturnType<typeof createTestSessionWithGlobalAdmin>>;

	beforeAll(async () => {
		regularUserSession = await createTestSession();
		regularMemberSession = await createTestSessionWithRegularMember();
		orgAdminSession = await createTestSessionWithOrgAdmin();
		globalAdminSession = await createTestSessionWithGlobalAdmin();
	});

	describe("public endpoints - no auth required", () => {
		test("GET /api/v1/auth/status should be accessible without authentication", async () => {
			const res = await app.request("/api/v1/auth/status");
			expect(res.status).toBe(200);
		});

		test("GET /api/v1/auth/sso-providers should be accessible without authentication", async () => {
			const res = await app.request("/api/v1/auth/sso-providers");
			expect(res.status).toBe(200);
		});

		test("GET /api/v1/auth/login-error should be accessible without authentication", async () => {
			const res = await app.request("/api/v1/auth/login-error?error=test");
			expect(res.status).toBe(302);
		});
	});

	describe("authenticated user endpoints - require requireAuth", () => {
		const authenticatedUserEndpoints = [
			{ method: "GET", path: "/api/v1/auth/sso-invitations" },
			{ method: "POST", path: "/api/v1/auth/sso-invitations/test-invitation/verify" },
		];

		for (const { method, path } of authenticatedUserEndpoints) {
			test(`${method} ${path} should return 401 when unauthenticated`, async () => {
				const res = await app.request(path, { method });
				expect(res.status).toBe(401);
				const body = await res.json();
				expect(body.message).toBe("Invalid or expired session");
			});

			test(`${method} ${path} should not return 401 for regular users`, async () => {
				const res = await app.request(path, {
					method,
					headers: {
						...regularUserSession.headers,
						"Content-Type": "application/json",
					},
					body: method === "POST" ? JSON.stringify({}) : undefined,
				});
				expect(res.status).not.toBe(401);
			});
		}
	});

	describe("org admin endpoints - require requireAuth + requireOrgAdmin", () => {
		const orgAdminEndpoints = [
			{ method: "GET", path: "/api/v1/auth/sso-settings" },
			{ method: "DELETE", path: "/api/v1/auth/sso-providers/test-provider" },
			{ method: "PATCH", path: "/api/v1/auth/sso-providers/test-provider/auto-linking" },
			{ method: "DELETE", path: "/api/v1/auth/sso-invitations/test-invitation" },
			{ method: "GET", path: "/api/v1/auth/org-members" },
			{ method: "PATCH", path: "/api/v1/auth/org-members/test-member/role" },
			{ method: "DELETE", path: "/api/v1/auth/org-members/test-member" },
		];

		for (const { method, path } of orgAdminEndpoints) {
			test(`${method} ${path} should return 401 when unauthenticated`, async () => {
				const res = await app.request(path, { method });
				expect(res.status).toBe(401);
				const body = await res.json();
				expect(body.message).toBe("Invalid or expired session");
			});

			test(`${method} ${path} should return 403 for regular members`, async () => {
				const res = await app.request(path, {
					method,
					headers: regularMemberSession.headers,
					body: method !== "GET" && method !== "DELETE" ? JSON.stringify({}) : undefined,
				});
				expect(res.status).toBe(403);
				const body = await res.json();
				expect(body.message).toBe("Forbidden");
			});

			test(`${method} ${path} should be accessible to org admins`, async () => {
				const res = await app.request(path, {
					method,
					headers: orgAdminSession.headers,
					body: method !== "GET" && method !== "DELETE" ? JSON.stringify({}) : undefined,
				});
				// Should not be 401 or 403 - actual response depends on endpoint logic
				expect(res.status).not.toBe(401);
				expect(res.status).not.toBe(403);
			});
		}

		describe("PATCH /api/v1/auth/sso-providers/:providerId/auto-linking specific", () => {
			test("should return 400 for invalid payload", async () => {
				const res = await app.request("/api/v1/auth/sso-providers/test-provider/auto-linking", {
					method: "PATCH",
					headers: {
						...orgAdminSession.headers,
						"Content-Type": "application/json",
					},
					body: JSON.stringify({}),
				});
				expect(res.status).toBe(400);
			});
		});

		describe("PATCH /api/v1/auth/org-members/:memberId/role specific", () => {
			test("should return 400 for invalid payload", async () => {
				const res = await app.request("/api/v1/auth/org-members/test-member/role", {
					method: "PATCH",
					headers: {
						...orgAdminSession.headers,
						"Content-Type": "application/json",
					},
					body: JSON.stringify({}),
				});
				expect(res.status).toBe(400);
			});
		});
	});

	describe("global admin endpoints - require requireAuth + requireAdmin", () => {
		const adminEndpoints = [
			{ method: "GET", path: "/api/v1/auth/admin-users" },
			{ method: "DELETE", path: "/api/v1/auth/admin-users/test-user/accounts/test-account" },
			{ method: "GET", path: "/api/v1/auth/deletion-impact/test-user" },
		];

		for (const { method, path } of adminEndpoints) {
			test(`${method} ${path} should return 401 when unauthenticated`, async () => {
				const res = await app.request(path, { method });
				expect(res.status).toBe(401);
				const body = await res.json();
				expect(body.message).toBe("Invalid or expired session");
			});

			test(`${method} ${path} should return 403 for regular users`, async () => {
				const res = await app.request(path, { method, headers: regularUserSession.headers });
				expect(res.status).toBe(403);
				const body = await res.json();
				expect(body.message).toBe("Forbidden");
			});

			test(`${method} ${path} should return 403 for org admins`, async () => {
				const res = await app.request(path, { method, headers: orgAdminSession.headers });
				expect(res.status).toBe(403);
				const body = await res.json();
				expect(body.message).toBe("Forbidden");
			});

			test(`${method} ${path} should not return 401 for global admins`, async () => {
				const res = await app.request(path, { method, headers: globalAdminSession.headers });
				// Should not be 401 - actual response depends on endpoint logic
				expect(res.status).not.toBe(401);
			});
		}

		test("global admins can delete an account for a user outside their active organization", async () => {
			const target = await createTestSession();

			const retainedAccountId = Bun.randomUUIDv7();
			await db.insert(account).values({
				id: retainedAccountId,
				accountId: `credential-${retainedAccountId}`,
				providerId: "credential",
				userId: target.user.id,
				password: "password-hash",
			});

			const removableAccountId = Bun.randomUUIDv7();
			await db.insert(account).values({
				id: removableAccountId,
				accountId: `oidc-${removableAccountId}`,
				providerId: "oidc-acme",
				userId: target.user.id,
			});

			const res = await app.request(`/api/v1/auth/admin-users/${target.user.id}/accounts/${removableAccountId}`, {
				method: "DELETE",
				headers: globalAdminSession.headers,
			});

			expect(res.status).toBe(200);

			const deletedAccount = await db.query.account.findFirst({
				where: { id: removableAccountId },
				columns: { id: true },
			});

			expect(deletedAccount).toBeUndefined();
		});
	});

	describe("invalid session handling", () => {
		test("should return 401 for invalid session cookie", async () => {
			const res = await app.request("/api/v1/auth/sso-settings", {
				headers: getAuthHeaders("invalid-session-token"),
			});
			expect(res.status).toBe(401);
			const body = await res.json();
			expect(body.message).toBe("Invalid or expired session");
		});

		test("should return 401 when session cookie is missing", async () => {
			const res = await app.request("/api/v1/auth/admin-users");
			expect(res.status).toBe(401);
			const body = await res.json();
			expect(body.message).toBe("Invalid or expired session");
		});
	});

	describe("information disclosure", () => {
		test("should not disclose org members when unauthenticated", async () => {
			const res = await app.request("/api/v1/auth/org-members");
			expect(res.status).toBe(401);
			const body = await res.json();
			expect(body.message).toBe("Invalid or expired session");
		});

		test("should not disclose SSO settings when unauthenticated", async () => {
			const res = await app.request("/api/v1/auth/sso-settings");
			expect(res.status).toBe(401);
			const body = await res.json();
			expect(body.message).toBe("Invalid or expired session");
		});

		test("should not disclose admin users when unauthenticated", async () => {
			const res = await app.request("/api/v1/auth/admin-users");
			expect(res.status).toBe(401);
			const body = await res.json();
			expect(body.message).toBe("Invalid or expired session");
		});
	});
});
