import { logError } from "@/lib/logger";
import type {
  MemberAccessRoleInput,
  TMemberAccessRolesRepository,
} from "./member-access-roles.repository";
import type {
  MemberAccessCapabilities,
  TMemberAccessRepository,
} from "./member-access.repository";

export interface MemberAccess extends MemberAccessCapabilities {
  isSuperAdmin: boolean;
  /** True when the user may open any member, not just an allow-listed slice. */
  unrestrictedScope: boolean;
}

const NONE: MemberAccessCapabilities = {
  can_view_analytics: false,
  can_view_bookings: false,
  can_view_sessions: false,
  can_view_activity_logs: false,
  can_view_saved_searches: false,
  can_view_points: false,
  can_reset_password: false,
  can_sync_profile: false,
  account_type_ids: [],
  account_status_ids: [],
};

const ALL: MemberAccessCapabilities = {
  can_view_analytics: true,
  can_view_bookings: true,
  can_view_sessions: true,
  can_view_activity_logs: true,
  can_view_saved_searches: true,
  can_view_points: true,
  can_reset_password: true,
  can_sync_profile: true,
  account_type_ids: [],
  account_status_ids: [],
};

interface Deps {
  MemberAccessRepository: TMemberAccessRepository;
  /**
   * Not optional: effective access is the union of a user's assigned roles, so
   * every caller needs it. It was optional while roles were being introduced,
   * which let a caller compile and then throw at request time.
   */
  MemberAccessRolesRepository: TMemberAccessRolesRepository;
}

export const MemberAccessServices = ({
  MemberAccessRepository,
  MemberAccessRolesRepository,
}: Deps) => {
  /**
   * Effective member-module access for one console user.
   *
   * The union of the roles assigned to them — capabilities OR'd, slices
   * unioned, and an empty slice on any role means unrestricted (a role that
   * doesn't narrow cannot be narrowed by another). Super admins get everything
   * and are never scoped, same rule as `withConsoleUserRole`.
   *
   * No roles means no access inside the module: the `members` role gets you the
   * accounts list, and nothing here has granted more.
   */
  const GetAccess = async (
    consoleUserId: string,
    isSuperAdmin: boolean,
  ): Promise<MemberAccess> => {
    if (isSuperAdmin) {
      return { ...ALL, isSuperAdmin: true, unrestrictedScope: true };
    }
    try {
      const assigned = await roles().ListForUser(consoleUserId);
      if (assigned.length === 0) {
        return { ...NONE, isSuperAdmin: false, unrestrictedScope: true };
      }

      const unionScope = (lists: string[][]) =>
        lists.some((list) => list.length === 0)
          ? []
          : Array.from(new Set(lists.flat()));

      const capabilities: MemberAccessCapabilities = {
        can_view_analytics: assigned.some((r) => r.can_view_analytics),
        can_view_bookings: assigned.some((r) => r.can_view_bookings),
        can_view_sessions: assigned.some((r) => r.can_view_sessions),
        can_view_activity_logs: assigned.some((r) => r.can_view_activity_logs),
        can_view_saved_searches: assigned.some(
          (r) => r.can_view_saved_searches,
        ),
        can_view_points: assigned.some((r) => r.can_view_points),
        can_reset_password: assigned.some((r) => r.can_reset_password),
        can_sync_profile: assigned.some((r) => r.can_sync_profile),
        account_type_ids: unionScope(
          assigned.map((r) => r.account_type_ids ?? []),
        ),
        account_status_ids: unionScope(
          assigned.map((r) => r.account_status_ids ?? []),
        ),
      };

      return {
        ...capabilities,
        isSuperAdmin: false,
        unrestrictedScope:
          capabilities.account_type_ids.length === 0 &&
          capabilities.account_status_ids.length === 0,
      };
    } catch (err) {
      logError(err);
      throw err;
    }
  };

  const ListAccess = async () => await MemberAccessRepository.ListAll();

  const roles = () => MemberAccessRolesRepository;

  const ListRoles = async () => await roles().List();
  const GetRole = async (id: string) => await roles().Get(id);
  const CreateRole = async (entry: MemberAccessRoleInput, createdBy?: string) =>
    await roles().Create(entry, createdBy);
  const UpdateRole = async (id: string, entry: MemberAccessRoleInput) =>
    await roles().Update(id, entry);
  const RemoveRole = async (id: string) => await roles().Remove(id);

  const ListUserRoles = async (consoleUserId: string) =>
    await roles().ListForUser(consoleUserId);

  const ListRoleAssignments = async () => await roles().ListAssignments();

  const AssignRole = async (
    consoleUserId: string,
    roleId: string,
    assignedBy?: string,
  ) => {
    const role = await roles().Get(roleId);
    if (!role) return null;
    await roles().Assign(consoleUserId, roleId, assignedBy);
    return role;
  };

  const UnassignRole = async (consoleUserId: string, roleId: string) =>
    await roles().Unassign(consoleUserId, roleId);

  const ClearUserRoles = async (consoleUserId: string) =>
    await roles().UnassignAll(consoleUserId);

  return {
    GetAccess,
    ListAccess,
    ListRoles,
    GetRole,
    CreateRole,
    UpdateRole,
    RemoveRole,
    ListUserRoles,
    ListRoleAssignments,
    AssignRole,
    UnassignRole,
    ClearUserRoles,
  };
};

export type TMemberAccessServices = ReturnType<typeof MemberAccessServices>;
