function corsEnabled(url: string): boolean {
  const xhr = new XMLHttpRequest();
  xhr.open('HEAD', url, false);
  try {
    xhr.send();
  } catch {
    return false;
  }
  return xhr.status >= 200 && xhr.status <= 299;
}

export const saveBlobAsFile = (blob: Blob | string, name: string = 'download'): void => {
  const URL = window.URL || window.webkitURL;
  const a = document.createElement('a');
  a.download = name;
  a.rel = 'noopener';

  if (typeof blob === 'string') {
    a.href = blob;
    if (a.origin !== window.location.origin && corsEnabled(a.href)) {
      const xhr = new XMLHttpRequest();
      xhr.open('GET', blob);
      xhr.responseType = 'blob';
      xhr.onload = () => saveBlobAsFile(xhr.response, name);
      xhr.onerror = () => console.error('Could not download file');
      xhr.send();
    } else {
      a.dispatchEvent(new MouseEvent('click'));
    }
  } else {
    a.href = URL.createObjectURL(blob);
    setTimeout(() => URL.revokeObjectURL(a.href), 40000); // 40 seconds
    setTimeout(() => a.dispatchEvent(new MouseEvent('click')), 0);
  }
};

export async function downloadFileByUrl(
  href: string,
  name: string | null | undefined,
  subscribe?: {
    onLoad?: (state: boolean) => void;
    onProcess?: (progress: number) => void;
    onSuccess?: () => void;
    onError?: () => void;
  },
) {
  const nameWithExtension = href.split('/').pop()!;
  const splitDefaultName = nameWithExtension.split('.');
  const extension = splitDefaultName.pop();
  const defaultName = splitDefaultName.join('.');

  const fileName = `${name || defaultName}.${extension}`;

  try {
    const streamSaver = (await import('streamsaver')).default;
    streamSaver.mitm = '/streamsaver/mitm.html';

    const resp = await fetch(href, { cache: 'no-store' });
    if (!resp.ok || !resp.body) {
      throw new Error('Fetch failed or no body');
    }

    const totalHeader = resp.headers.get('content-length');
    const total = totalHeader ? Number(totalHeader) : undefined;

    const fileStream = streamSaver.createWriteStream(fileName, total ? { size: total } : undefined);

    if (typeof fileStream.getWriter !== 'function' && typeof resp.body.pipeTo === 'function') {
      subscribe?.onLoad?.(true);
      await resp.body.pipeTo(fileStream);
      subscribe?.onLoad?.(false);
      subscribe?.onSuccess?.();
      return;
    }

    const writer = fileStream.getWriter();
    const reader = resp.body.getReader();
    let loaded = 0;

    subscribe?.onLoad?.(true);
    while (true) {
      // eslint-disable-next-line no-await-in-loop
      const { done, value } = await reader.read();

      if (done) {
        break;
      }

      loaded += value.byteLength;

      if (subscribe?.onProcess) {
        subscribe?.onProcess(total ? Math.round((loaded / total) * 100) : -1);
      }

      // eslint-disable-next-line no-await-in-loop
      await writer.write(value);
    }
    await writer.close();
    subscribe?.onLoad?.(false);
    subscribe?.onSuccess?.();
  } catch {
    subscribe?.onError?.();
  }
}
