import { beforeEach, describe, expect, test } from "vitest";
import { db, sqlite } from "~/server/db/db";
import { member, organization, sessionsTable, usersTable } from "~/server/db/schema";
import {
	createMembership,
	createOrganization,
	createSession,
	createUser,
	dropTrigger,
	escapeSqlLiteral,
	randomSlug,
} from "~/test/helpers/user-org";
import { authService } from "../auth.service";

const CLEANUP_USER_ORGS_ROLLBACK_TRIGGER = "cleanup_user_orgs_final_session_abort";

describe("authService.cleanupUserOrganizations", () => {
	beforeEach(async () => {
		dropTrigger(CLEANUP_USER_ORGS_ROLLBACK_TRIGGER);
		await db.delete(member);
		await db.delete(sessionsTable);
		await db.delete(organization);
		await db.delete(usersTable);
	});

	test("reassigns active organization for members whose active org gets deleted", async () => {
		const adminUserId = await createUser(`${randomSlug("admin")}@example.com`);
		const deletedUserId = await createUser(`${randomSlug("deleted")}@example.com`);

		const adminWorkspaceId = await createOrganization("Admin Workspace");
		const deletedWorkspaceId = await createOrganization("Deleted Workspace");

		await createMembership({ userId: adminUserId, organizationId: adminWorkspaceId, role: "owner" });
		await createMembership({ userId: deletedUserId, organizationId: deletedWorkspaceId, role: "owner" });
		await createMembership({ userId: adminUserId, organizationId: deletedWorkspaceId, role: "member" });

		const adminSessionId = await createSession({
			userId: adminUserId,
			activeOrganizationId: deletedWorkspaceId,
		});

		await authService.cleanupUserOrganizations(deletedUserId);

		const remainingSession = await db.query.sessionsTable.findFirst({
			where: { id: adminSessionId },
			columns: { activeOrganizationId: true },
		});
		const deletedWorkspace = await db.query.organization.findFirst({
			where: { id: deletedWorkspaceId },
			columns: { id: true },
		});

		expect(deletedWorkspace).toBeUndefined();
		expect(remainingSession?.activeOrganizationId).toBe(adminWorkspaceId);

		const deletedMembership = await db.query.member.findFirst({
			where: { AND: [{ organizationId: deletedWorkspaceId }, { userId: adminUserId }] },
			columns: { id: true },
		});

		expect(deletedMembership).toBeUndefined();
	});

	test("sets active organization to null when user has no other memberships", async () => {
		const deletedOnlyUserId = await createUser(`${randomSlug("deleted")}@example.com`);

		const deletedWorkspaceId = await createOrganization("Deleted Workspace");

		await createMembership({
			userId: deletedOnlyUserId,
			organizationId: deletedWorkspaceId,
			role: "owner",
		});

		const userSessionId = await createSession({
			userId: deletedOnlyUserId,
			activeOrganizationId: deletedWorkspaceId,
		});

		await authService.cleanupUserOrganizations(deletedOnlyUserId);

		const deletedWorkspace = await db.query.organization.findFirst({
			where: { id: deletedWorkspaceId },
			columns: { id: true },
		});
		const updatedSession = await db.query.sessionsTable.findFirst({
			where: { id: userSessionId },
			columns: { activeOrganizationId: true },
		});

		expect(deletedWorkspace).toBeUndefined();
		expect(updatedSession?.activeOrganizationId).toBeNull();

		const membership = await db.query.member.findFirst({
			where: { userId: deletedOnlyUserId },
			columns: { id: true },
		});

		expect(membership).toBeUndefined();
	});

	test("sets active organization to null for affected members without a fallback organization", async () => {
		const deletedUserId = await createUser(`${randomSlug("deleted")}@example.com`);
		const affectedUserId = await createUser(`${randomSlug("affected")}@example.com`);
		const deletedWorkspaceId = await createOrganization("Deleted Workspace");

		await createMembership({ userId: deletedUserId, organizationId: deletedWorkspaceId, role: "owner" });
		await createMembership({ userId: affectedUserId, organizationId: deletedWorkspaceId, role: "member" });

		const affectedSessionId = await createSession({
			userId: affectedUserId,
			activeOrganizationId: deletedWorkspaceId,
		});

		await authService.cleanupUserOrganizations(deletedUserId);

		const updatedSession = await db.query.sessionsTable.findFirst({
			where: { id: affectedSessionId },
			columns: { activeOrganizationId: true },
		});
		const deletedWorkspace = await db.query.organization.findFirst({
			where: { id: deletedWorkspaceId },
			columns: { id: true },
		});
		const removedMembership = await db.query.member.findFirst({
			where: { AND: [{ organizationId: deletedWorkspaceId }, { userId: affectedUserId }] },
			columns: { id: true },
		});

		expect(deletedWorkspace).toBeUndefined();
		expect(updatedSession?.activeOrganizationId).toBeNull();
		expect(removedMembership).toBeUndefined();
	});

	test("rolls back organization cleanup when session nulling fails", async () => {
		const affectedUserId = await createUser(`${randomSlug("admin")}@example.com`);
		const deletedUserId = await createUser(`${randomSlug("deleted")}@example.com`);

		const fallbackWorkspaceId = await createOrganization("Fallback Workspace");
		const deletedWorkspaceId = await createOrganization("Deleted Workspace");

		await createMembership({ userId: affectedUserId, organizationId: fallbackWorkspaceId, role: "owner" });
		await createMembership({ userId: deletedUserId, organizationId: deletedWorkspaceId, role: "owner" });
		await createMembership({ userId: affectedUserId, organizationId: deletedWorkspaceId, role: "member" });

		const affectedSessionId = await createSession({
			userId: affectedUserId,
			activeOrganizationId: deletedWorkspaceId,
		});
		const deletedUserSessionId = await createSession({
			userId: deletedUserId,
			activeOrganizationId: deletedWorkspaceId,
		});

		sqlite.exec(`
			CREATE TRIGGER ${CLEANUP_USER_ORGS_ROLLBACK_TRIGGER}
			BEFORE UPDATE OF active_organization_id ON sessions_table
			WHEN OLD.user_id = '${escapeSqlLiteral(deletedUserId)}' AND NEW.active_organization_id IS NULL
			BEGIN
				SELECT RAISE(ABORT, 'forced cleanup rollback');
			END;
		`);

		try {
			await expect(authService.cleanupUserOrganizations(deletedUserId)).rejects.toThrow("forced cleanup rollback");
		} finally {
			dropTrigger(CLEANUP_USER_ORGS_ROLLBACK_TRIGGER);
		}

		const deletedWorkspace = await db.query.organization.findFirst({
			where: { id: deletedWorkspaceId },
			columns: { id: true },
		});
		const affectedSession = await db.query.sessionsTable.findFirst({
			where: { id: affectedSessionId },
			columns: { activeOrganizationId: true },
		});
		const deletedUserSession = await db.query.sessionsTable.findFirst({
			where: { id: deletedUserSessionId },
			columns: { activeOrganizationId: true },
		});
		const affectedMembership = await db.query.member.findFirst({
			where: { AND: [{ organizationId: deletedWorkspaceId }, { userId: affectedUserId }] },
			columns: { id: true },
		});

		expect(deletedWorkspace).toEqual({ id: deletedWorkspaceId });
		expect(affectedSession?.activeOrganizationId).toBe(deletedWorkspaceId);
		expect(deletedUserSession?.activeOrganizationId).toBe(deletedWorkspaceId);
		expect(affectedMembership).not.toBeUndefined();
	});
});
