import {
  PermissionState,
  Room,
  RoomMember,
  RoomPermissionKey,
  RoomPermissions,
  RoomRole,
  RoomRoleAssignment,
  UserRole
} from '../../../../shared-kernel';
import { SYSTEM_ROLE_IDS } from '../constants/access-control.constants';
import {
  getRolePermissionState,
  permissionStateToBoolean,
  resolveLegacyAllowState
} from '../util/access-control.util';
import type { MemberIdentity } from '../models/access-control.model';
import {
  getAssignedRoleIds,
  normalizeRoomRoleAssignments,
  removeRoleFromAssignments
} from './role-assignment.rules';
import {
  getRoomRoleById,
  normalizeRoomRoles,
  withUpdatedRole
} from './role.rules';
import { normalizeChannelPermissionOverrides, resolveRoomPermission } from './permission.rules';

function applyRolePermissionChanges(
  roles: readonly RoomRole[],
  role: RoomRole | null | undefined,
  changes: Partial<Record<RoomPermissionKey, PermissionState | undefined>>
): RoomRole[] {
  if (!role) {
    return [...roles];
  }

  return withUpdatedRole(roles, role.id, {
    permissions: {
      ...role.permissions,
      ...changes
    }
  });
}

function getEveryoneLegacyPermissionChanges(
  role: RoomRole,
  permissions: Partial<RoomPermissions>
): Partial<Record<RoomPermissionKey, PermissionState | undefined>> {
  return {
    joinVoice: resolveLegacyAllowState(permissions.allowVoice, role.permissions?.joinVoice, 'deny'),
    shareScreen: resolveLegacyAllowState(permissions.allowScreenShare, role.permissions?.shareScreen, 'deny'),
    uploadFiles: resolveLegacyAllowState(permissions.allowFileUploads, role.permissions?.uploadFiles, 'deny')
  };
}

function getModeratorLegacyPermissionChanges(
  role: RoomRole,
  permissions: Partial<RoomPermissions>
): Partial<Record<RoomPermissionKey, PermissionState | undefined>> {
  return {
    manageChannels: resolveLegacyAllowState(permissions.moderatorsManageRooms, role.permissions?.manageChannels, 'inherit'),
    manageIcon: resolveLegacyAllowState(permissions.moderatorsManageIcon, role.permissions?.manageIcon, 'inherit')
  };
}

function getAdminLegacyPermissionChanges(
  role: RoomRole,
  permissions: Partial<RoomPermissions>
): Partial<Record<RoomPermissionKey, PermissionState | undefined>> {
  return {
    manageChannels: resolveLegacyAllowState(permissions.adminsManageRooms, role.permissions?.manageChannels, 'inherit'),
    manageIcon: resolveLegacyAllowState(permissions.adminsManageIcon, role.permissions?.manageIcon, 'inherit')
  };
}

export function resolveLegacyRole(room: Room, identity: MemberIdentity | null | undefined): UserRole {
  if (!identity) {
    return 'member';
  }

  if (room.hostId === identity.id || room.hostId === identity.oderId) {
    return 'host';
  }

  const assignedRoleIds = getAssignedRoleIds(room.roleAssignments, identity);

  if (assignedRoleIds.includes(SYSTEM_ROLE_IDS.admin)) {
    return 'admin';
  }

  if (assignedRoleIds.includes(SYSTEM_ROLE_IDS.moderator)) {
    return 'moderator';
  }

  if (
    resolveRoomPermission(room, identity, 'manageRoles') ||
    resolveRoomPermission(room, identity, 'banMembers') ||
    resolveRoomPermission(room, identity, 'manageBans') ||
    resolveRoomPermission(room, identity, 'manageServer')
  ) {
    return 'admin';
  }

  if (
    resolveRoomPermission(room, identity, 'kickMembers') ||
    resolveRoomPermission(room, identity, 'deleteMessages') ||
    resolveRoomPermission(room, identity, 'manageChannels') ||
    resolveRoomPermission(room, identity, 'manageIcon')
  ) {
    return 'moderator';
  }

  return 'member';
}

export function deriveLegacyRoomPermissions(room: Pick<Room, 'roles' | 'permissions' | 'slowModeInterval'>): RoomPermissions {
  const roles = normalizeRoomRoles(room.roles, room.permissions);
  const everyoneRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.everyone);
  const moderatorRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.moderator);
  const adminRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.admin);

  return {
    allowVoice: permissionStateToBoolean(getRolePermissionState(everyoneRole, 'joinVoice'), true),
    allowScreenShare: permissionStateToBoolean(getRolePermissionState(everyoneRole, 'shareScreen'), true),
    allowFileUploads: permissionStateToBoolean(getRolePermissionState(everyoneRole, 'uploadFiles'), true),
    adminsManageRooms: getRolePermissionState(adminRole, 'manageChannels') === 'allow',
    moderatorsManageRooms: getRolePermissionState(moderatorRole, 'manageChannels') === 'allow',
    adminsManageIcon: getRolePermissionState(adminRole, 'manageIcon') === 'allow',
    moderatorsManageIcon: getRolePermissionState(moderatorRole, 'manageIcon') === 'allow',
    slowModeInterval: room.slowModeInterval ?? room.permissions?.slowModeInterval ?? 0
  };
}

export function withLegacyRoomPermissions(room: Room, permissions: Partial<RoomPermissions>): Room {
  const roles = normalizeRoomRoles(room.roles, room.permissions);
  const everyoneRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.everyone);
  const moderatorRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.moderator);
  const adminRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.admin);

  let nextRoles = applyRolePermissionChanges(roles, everyoneRole, everyoneRole ? getEveryoneLegacyPermissionChanges(everyoneRole, permissions) : {});

  nextRoles = applyRolePermissionChanges(
    nextRoles,
    moderatorRole,
    moderatorRole ? getModeratorLegacyPermissionChanges(moderatorRole, permissions) : {}
  );

  nextRoles = applyRolePermissionChanges(nextRoles, adminRole, adminRole ? getAdminLegacyPermissionChanges(adminRole, permissions) : {});

  return normalizeRoomAccessControl({
    ...room,
    roles: nextRoles,
    slowModeInterval: permissions.slowModeInterval ?? room.slowModeInterval ?? room.permissions?.slowModeInterval ?? 0
  });
}

export function hydrateRoomMembers(room: Room): RoomMember[] {
  const roles = normalizeRoomRoles(room.roles, room.permissions);
  const roleAssignments = normalizeRoomRoleAssignments(room.roleAssignments, room.members, roles);

  return (room.members ?? []).map((member) => {
    const roleIds = getAssignedRoleIds(roleAssignments, member);
    const hydratedRoom: Room = {
      ...room,
      roles,
      roleAssignments
    };

    return {
      ...member,
      roleIds,
      role: resolveLegacyRole(hydratedRoom, member)
    };
  });
}

export function normalizeRoomAccessControl(room: Room): Room {
  const roles = normalizeRoomRoles(room.roles, room.permissions);
  const roleAssignments = normalizeRoomRoleAssignments(room.roleAssignments, room.members, roles);
  const channelPermissions = normalizeChannelPermissionOverrides(room.channelPermissions, roles);
  const slowModeInterval = room.slowModeInterval ?? room.permissions?.slowModeInterval ?? 0;
  const nextRoom: Room = {
    ...room,
    roles,
    roleAssignments,
    channelPermissions,
    slowModeInterval
  };

  nextRoom.permissions = deriveLegacyRoomPermissions(nextRoom);
  nextRoom.members = hydrateRoomMembers(nextRoom);

  return nextRoom;
}

export function removeRole(
  roles: readonly RoomRole[],
  assignments: readonly RoomRoleAssignment[] | undefined,
  roleId: string
): { roles: RoomRole[]; roleAssignments: RoomRoleAssignment[] } {
  const nextRoles = roles.filter((role) => role.id !== roleId || role.isSystem);

  return {
    roles: normalizeRoomRoles(nextRoles),
    roleAssignments: removeRoleFromAssignments(assignments, roleId)
  };
}
