import {
  type TailordbKysely,
  type Transaction as KyselyTransaction,
} from "@tailor-platform/sdk/kysely";
import { DB } from "../generated/kysely-tailordb";
import { User } from "./types";

export const recomputeUserPermissions = async <
  T extends { User: object; UserRole: object; RolePermission: object; Permission: object },
>(
  db: TailordbKysely<T> | KyselyTransaction<T>,
  userId: string,
): Promise<User<T>> => {
  const _db = db as unknown as DB;

  const permissionRows = await _db
    .selectFrom("UserRole")
    .innerJoin("RolePermission", "RolePermission.roleId", "UserRole.roleId")
    .innerJoin("Permission", "Permission.id", "RolePermission.permissionId")
    .select("Permission.key")
    .where("UserRole.userId", "=", userId)
    .execute();

  const permissions = [...new Set(permissionRows.map((row) => row.key))].sort();

  const updatedUser = await _db
    .updateTable("User")
    .set({ permissions, updatedAt: new Date() })
    .where("id", "=", userId)
    .returningAll()
    .executeTakeFirstOrThrow();

  return updatedUser as unknown as User<T>;
};

export const recomputePermissionsForUsersWithRole = async <
  T extends { User: object; UserRole: object; RolePermission: object; Permission: object },
>(
  db: TailordbKysely<T> | KyselyTransaction<T>,
  roleId: string,
): Promise<User<T>[]> => {
  const _db = db as unknown as DB;

  const userRoles = await _db
    .selectFrom("UserRole")
    .select("userId")
    .where("roleId", "=", roleId)
    .execute();

  const updatedUsers: User<T>[] = [];
  for (const { userId } of userRoles) {
    const user = await recomputeUserPermissions(db, userId);
    updatedUsers.push(user);
  }
  return updatedUsers;
};
