export const PROFILE_AVATAR_ALLOWED_MIME_TYPES = [
  'image/webp',
  'image/gif',
  'image/jpeg'
] as const;

export const PROFILE_AVATAR_ACCEPT_ATTRIBUTE = '.webp,.gif,.jpg,.jpeg,image/webp,image/gif,image/jpeg';
export const PROFILE_AVATAR_OUTPUT_SIZE = 256;
export const PROFILE_AVATAR_EDITOR_FRAME_SIZE = 224;
export const PROFILE_AVATAR_MIN_ZOOM = 1;
export const PROFILE_AVATAR_MAX_ZOOM = 4;

export interface ProfileAvatarDimensions {
  width: number;
  height: number;
}

export interface EditableProfileAvatarSource extends ProfileAvatarDimensions {
  file: File;
  objectUrl: string;
  mime: string;
  name: string;
  preservesAnimation: boolean;
}

export interface ProfileAvatarTransform {
  zoom: number;
  offsetX: number;
  offsetY: number;
}

export interface ProfileAvatarUpdates {
  avatarUrl: string;
  avatarHash: string;
  avatarMime: string;
  avatarUpdatedAt: number;
}

export interface ProcessedProfileAvatar extends ProfileAvatarUpdates, ProfileAvatarDimensions {
  base64: string;
  blob: Blob;
}

export function resolveProfileAvatarStorageFileName(mime: string | null | undefined): string {
  switch (mime?.toLowerCase()) {
    case 'image/gif':
      return 'profile.gif';

    case 'image/jpeg':
    case 'image/jpg':
      return 'profile.jpg';

    default:
      return 'profile.webp';
  }
}

export function clampProfileAvatarZoom(zoom: number): number {
  if (!Number.isFinite(zoom)) {
    return PROFILE_AVATAR_MIN_ZOOM;
  }

  return Math.min(Math.max(zoom, PROFILE_AVATAR_MIN_ZOOM), PROFILE_AVATAR_MAX_ZOOM);
}

export function resolveProfileAvatarBaseScale(
  source: ProfileAvatarDimensions,
  frameSize = PROFILE_AVATAR_EDITOR_FRAME_SIZE
): number {
  return Math.max(frameSize / source.width, frameSize / source.height);
}

export function clampProfileAvatarTransform(
  source: ProfileAvatarDimensions,
  transform: ProfileAvatarTransform,
  frameSize = PROFILE_AVATAR_EDITOR_FRAME_SIZE
): ProfileAvatarTransform {
  const zoom = clampProfileAvatarZoom(transform.zoom);
  const renderedWidth = source.width * resolveProfileAvatarBaseScale(source, frameSize) * zoom;
  const renderedHeight = source.height * resolveProfileAvatarBaseScale(source, frameSize) * zoom;
  const maxOffsetX = Math.max(0, (renderedWidth - frameSize) / 2);
  const maxOffsetY = Math.max(0, (renderedHeight - frameSize) / 2);

  return {
    zoom,
    offsetX: clampOffset(transform.offsetX, maxOffsetX),
    offsetY: clampOffset(transform.offsetY, maxOffsetY)
  };
}

function clampOffset(value: number, maxMagnitude: number): number {
  if (!Number.isFinite(value)) {
    return 0;
  }

  const nextValue = Math.min(Math.max(value, -maxMagnitude), maxMagnitude);

  return Object.is(nextValue, -0) ? 0 : nextValue;
}
