import { logError } from "@/lib/logger";
import type { TConsoleUserRepository } from "@/internal/console-users/console-users.repository";
import type { TDashboardRepository } from "./dashboard.repository";

/**
 * The ONE place dashboard visibility is decided.
 *
 * FAIL-CLOSED, and deliberately the opposite of the promo-code access list: a
 * user who is neither a super admin nor a dashboard admin, and who has zero
 * rows in console_user_dashboard_access, sees NOTHING. "No grants" means "no
 * dashboards", never "all dashboards".
 *
 * Every dashboard-scoped read path calls these helpers server-side. The
 * frontend's admin_scopes JWT cannot express per-resource grants at all, so it
 * is never consulted.
 */

export const DASHBOARD_FEATURE_SLUG = "dashboard";

type TDashboardAccessServiceDeps = {
  DashboardRepository: TDashboardRepository;
  ConsoleUserRepository: TConsoleUserRepository;
};

/** Shape we rely on from ConsoleUserRepository.FindEntryById. */
type PrivilegeRow = {
  slug?: string | null;
  create?: boolean | null;
  read?: boolean | null;
  update?: boolean | null;
  delete?: boolean | null;
};
type UserWithRoles = {
  id: string;
  email?: string | null;
  is_super_admin?: boolean | null;
  roles?: { privileges?: PrivilegeRow[] }[];
};

export const DashboardAccessServices = ({
  DashboardRepository,
  ConsoleUserRepository,
}: TDashboardAccessServiceDeps) => {
  const loadUser = async (userId: string): Promise<UserWithRoles | null> => {
    try {
      // FindEntryById throws when the id doesn't resolve; a missing user is
      // "no access", not a 500.
      return (await ConsoleUserRepository.FindEntryById(
        userId,
      )) as unknown as UserWithRoles;
    } catch (err) {
      logError(err, `Failed loading console user ${userId} for dashboard access`);
      return null;
    }
  };

  /**
   * "Dashboard admin" for this whole feature = super admin, OR holds
   * create-or-update on the dashboard feature. Managing dashboards implies
   * seeing all of them; read-only privilege does NOT.
   */
  const isDashboardAdmin = (user: UserWithRoles | null): boolean => {
    if (!user) return false;
    if (user.is_super_admin === true) return true;
    for (const role of user.roles ?? []) {
      for (const privilege of role.privileges ?? []) {
        if (
          privilege.slug === DASHBOARD_FEATURE_SLUG &&
          (privilege.create === true || privilege.update === true)
        ) {
          return true;
        }
      }
    }
    return false;
  };

  const isDashboardAdminByUserId = async (userId: string): Promise<boolean> =>
    isDashboardAdmin(await loadUser(userId));

  /**
   * canView(user, dashboardId) =
   *      user.is_super_admin
   *   OR dashboard:create-or-update privilege
   *   OR an explicit console_user_dashboard_access row
   *   OTHERWISE false.
   */
  const canUserViewDashboard = async (
    userId: string,
    dashboardId: string,
  ): Promise<boolean> => {
    if (!userId || !dashboardId) return false;
    if (await isDashboardAdminByUserId(userId)) return true;
    return DashboardRepository.HasAccessGrant(userId, dashboardId);
  };

  /**
   * List-scoping counterpart. "ALL" means unrestricted; an array — including an
   * EMPTY array — means exactly those ids and nothing else.
   */
  const getViewableDashboardIds = async (
    userId: string,
  ): Promise<"ALL" | string[]> => {
    if (!userId) return [];
    if (await isDashboardAdminByUserId(userId)) return "ALL";
    return DashboardRepository.ListDashboardIdsForUser(userId);
  };

  return {
    isDashboardAdmin,
    isDashboardAdminByUserId,
    canUserViewDashboard,
    getViewableDashboardIds,
    loadUser,
  };
};

export type TDashboardAccessServices = ReturnType<typeof DashboardAccessServices>;
