const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const K = 1024;

export const bytesToSize = (bytes: number, decimals = 2): string => {
  if (bytes === 0) {
    return '0 Bytes';
  }

  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  const i = Math.floor(Math.log(bytes) / Math.log(K));
  const size = bytesTo(bytes, i, decimals);

  return `${size} ${sizes[i]}`;
};

export const sizeToBytes = (str: string, decimals = 2): number => {
  if (!str) return 0;

  const [size, unit] = str.split(' ');

  if (!Number(size)) return 0;

  const i = unit === 'Bytes' ? 'B' : unit;

  return bytesFrom(Number(size), i, decimals);
};

export const getUsage = (
  bytes: number,
  maxBytes: number,
  decimals = 0,
): string => {
  const max = bytesToSize(maxBytes, decimals);
  const unit = max.split(' ')[1];
  const size = bytesTo(bytes, unit, decimals);

  return `${size} / ${max}`;
};

// unit - string: ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
// unit - number: [ 0 ,  1  ,  2  ,  3  ,  4  ,  5  ,  6  ,  7  ,  8  ]
// return: size from unit in bytes.
export const bytesFrom = (
  size: number,
  unit: number | string,
  decimals = 2,
): number => {
  if (!size) return 0;
  if (!isValidUnit(unit)) return size;

  const dm = decimals < 0 ? 0 : decimals;
  const i = typeof unit === 'string' ? ston(unit) : unit;

  return parseFloat((size * Math.pow(K, i)).toFixed(dm));
};

// unit - string: ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
// unit - number: [ 0 ,  1  ,  2  ,  3  ,  4  ,  5  ,  6  ,  7  ,  8  ]
// return: size from bytes in unit.
export const bytesTo = (
  bytes: number,
  unit: number | string,
  decimals = 2,
): number => {
  if (bytes <= 0) return 0;
  if (!isValidUnit(unit) || unit === 0) return bytes;

  const dm = decimals < 0 ? 0 : decimals;
  const i = typeof unit === 'string' ? ston(unit) : unit;

  return parseFloat((bytes / Math.pow(K, i)).toFixed(dm));
};

// unit:   ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
// return: [ 0 ,  1  ,  2  ,  3  ,  4  ,  5  ,  6  ,  7  ,  8  ]
export const ston = (unit: string): number => UNITS.indexOf(unit);

export const isValidUnit = (unit: string | number): boolean =>
  (typeof unit === 'string' && isValidStringUnit(unit)) ||
  (typeof unit === 'number' && isValidNumberUnit(unit));

export const isValidStringUnit = (unit: string): boolean =>
  unit === 'B' ||
  unit === 'KB' ||
  unit === 'MB' ||
  unit === 'GB' ||
  unit === 'TB' ||
  unit === 'PB' ||
  unit === 'EB' ||
  unit === 'ZB' ||
  unit === 'YB';

export const isValidNumberUnit = (unit: number): boolean =>
  unit >= 0 && unit <= 8;
