import {
  RoomPermissionMatrix,
  RoomPermissions,
  RoomRole
} from '../../../../shared-kernel';
import { SYSTEM_ROLE_IDS } from '../constants/access-control.constants';
import {
  buildRoleLookup,
  buildSystemRole,
  nextRolePosition,
  normalizeName,
  normalizePermissionMatrix,
  roleSortAscending,
  roleSortDescending
} from '../util/access-control.util';

const ROLE_COLORS = {
  everyone: '#6b7280',
  moderator: '#10b981',
  admin: '#60a5fa'
} as const;

function resolveNormalizedRolePosition(
  position: unknown,
  fallbackPosition: number | undefined,
  existingRoles: readonly RoomRole[],
  defaultRoles: readonly RoomRole[]
): number {
  if (typeof position === 'number' && Number.isFinite(position)) {
    return position;
  }

  if (typeof fallbackPosition === 'number') {
    return fallbackPosition;
  }

  return nextRolePosition(existingRoles.length > 0 ? existingRoles : defaultRoles);
}

function normalizeRoomRoleEntry(
  role: RoomRole | null | undefined,
  defaultsById: Map<string, RoomRole>,
  existingRoles: readonly RoomRole[],
  defaultRoles: readonly RoomRole[]
): RoomRole | null {
  if (!role) {
    return null;
  }

  const id = typeof role.id === 'string' ? role.id.trim() : '';
  const fallbackRole = defaultsById.get(id);
  const name = normalizeName(typeof role.name === 'string' ? role.name : (fallbackRole?.name ?? 'Role'));

  if (!id || !name) {
    return null;
  }

  return {
    id,
    name,
    position: resolveNormalizedRolePosition(role.position, fallbackRole?.position, existingRoles, defaultRoles),
    color: typeof role.color === 'string' && role.color.trim() ? role.color.trim() : fallbackRole?.color,
    isSystem: typeof role.isSystem === 'boolean' ? role.isSystem : fallbackRole?.isSystem,
    permissions: normalizePermissionMatrix(role.permissions ?? fallbackRole?.permissions)
  };
}

export function buildDefaultRoomRoles(legacyPermissions?: RoomPermissions): RoomRole[] {
  const everyonePermissions: RoomPermissionMatrix = {
    joinVoice: legacyPermissions?.allowVoice === false ? 'deny' : 'allow',
    shareScreen: legacyPermissions?.allowScreenShare === false ? 'deny' : 'allow',
    uploadFiles: legacyPermissions?.allowFileUploads === false ? 'deny' : 'allow'
  };
  const moderatorPermissions: RoomPermissionMatrix = {
    kickMembers: 'allow',
    deleteMessages: 'allow',
    manageChannels: legacyPermissions?.moderatorsManageRooms ? 'allow' : 'inherit',
    manageIcon: legacyPermissions?.moderatorsManageIcon ? 'allow' : 'inherit'
  };
  const adminPermissions: RoomPermissionMatrix = {
    kickMembers: 'allow',
    banMembers: 'allow',
    manageBans: 'allow',
    deleteMessages: 'allow',
    manageChannels: legacyPermissions?.adminsManageRooms ? 'allow' : 'inherit',
    manageIcon: legacyPermissions?.adminsManageIcon ? 'allow' : 'inherit'
  };

  return [
    buildSystemRole(SYSTEM_ROLE_IDS.everyone, '@everyone', 0, everyonePermissions, ROLE_COLORS.everyone),
    buildSystemRole(SYSTEM_ROLE_IDS.moderator, 'Moderator', 200, moderatorPermissions, ROLE_COLORS.moderator),
    buildSystemRole(SYSTEM_ROLE_IDS.admin, 'Admin', 300, adminPermissions, ROLE_COLORS.admin)
  ];
}

export function sortRolesForDisplay(roles: readonly RoomRole[]): RoomRole[] {
  return [...roles].sort(roleSortDescending);
}

export function normalizeRoomRoles(roles: readonly RoomRole[] | undefined, legacyPermissions?: RoomPermissions): RoomRole[] {
  const defaultRoles = buildDefaultRoomRoles(legacyPermissions);
  const defaultsById = buildRoleLookup(defaultRoles);
  const normalizedById = new Map<string, RoomRole>();

  for (const role of roles ?? []) {
    const normalizedRole = normalizeRoomRoleEntry(role, defaultsById, Array.from(normalizedById.values()), defaultRoles);

    if (normalizedRole) {
      normalizedById.set(normalizedRole.id, normalizedRole);
    }
  }

  for (const [roleId, role] of defaultsById) {
    if (!normalizedById.has(roleId)) {
      normalizedById.set(roleId, role);
    }
  }

  return Array.from(normalizedById.values()).sort(roleSortAscending);
}

export function getRoomRoleById(roles: readonly RoomRole[] | undefined, roleId: string): RoomRole | undefined {
  return (roles ?? []).find((role) => role.id === roleId);
}

export function createCustomRoomRole(name: string, roles: readonly RoomRole[]): RoomRole {
  const normalizedName = normalizeName(name) || 'New Role';

  return {
    id: `role-${crypto.randomUUID()}`,
    name: normalizedName,
    position: nextRolePosition(roles),
    permissions: {}
  };
}

export function reorderRoles(roles: readonly RoomRole[], orderedRoleIds: readonly string[]): RoomRole[] {
  const roleLookup = buildRoleLookup(roles);
  const systemRoles = roles.filter((role) => role.isSystem);
  const customRoles = orderedRoleIds.map((roleId) => roleLookup.get(roleId)).filter((role): role is RoomRole => !!role && !role.isSystem);
  const remainingCustomRoles = roles.filter((role) => !role.isSystem && !orderedRoleIds.includes(role.id));
  const orderedRoles = sortRolesForDisplay(systemRoles).concat(customRoles)
    .concat(sortRolesForDisplay(remainingCustomRoles));

  return orderedRoles
    .map((role, index) => ({
      ...role,
      position: (orderedRoles.length - index - 1) * 100
    }))
    .sort(roleSortAscending);
}

export function withUpdatedRole(roles: readonly RoomRole[], roleId: string, updates: Partial<RoomRole>): RoomRole[] {
  return normalizeRoomRoles(
    roles.map((role) => {
      if (role.id !== roleId) {
        return role;
      }

      return {
        ...role,
        ...updates,
        permissions: normalizePermissionMatrix(updates.permissions ?? role.permissions)
      };
    })
  );
}

export function findAssignableRoles(roles: readonly RoomRole[]): RoomRole[] {
  return sortRolesForDisplay(roles).filter((role) => role.id !== SYSTEM_ROLE_IDS.everyone);
}
