/* eslint-disable import/prefer-default-export */

type GetSafeUrlOptions = {
  returnRaw?: boolean;
  throwOnError?: Boolean;
  // 为 null 时代表不检查 protocol
  allowProtocols?: Array<string> | null;
  // 为 null 时代表不检查 host
  allowDomains?: Array<string> | null;
};

class CustomError extends Error {}

export function getSafeUrl(
  // 只允许 / 开头， ./ 开头和带有 protocol 的 URL
  url: string,
  {
    throwOnError = true,
    returnRaw = false,
    // 根据 URL 的规范，protocol 以 : 结尾
    allowProtocols = ['https:', 'http:'],
    // host 中可能会携带端口号，需要使用 hostname 来获取干净的域名
    allowDomains = undefined,
  }: GetSafeUrlOptions = {}
) {
  if (allowDomains === undefined) {
    // eslint-disable-next-line no-param-reassign
    allowDomains = window.blocklet?.domainAliases || [];
    (allowDomains as unknown as Array<String>).push(window.location.href);
  }
  try {
    let base;
    if (url.startsWith('/')) {
      base = window.location.origin;
    } else if (url.startsWith('./')) {
      base = window.location.href;
    }

    const urlInstance = new URL(url, base);
    const allowHostName = allowDomains
      ? (allowDomains as Array<string>).map((host) => {
          try {
            return new URL(host).hostname;
          } catch {
            // HACK: 如果传入的 allowDomains 不包含协议，则尝试为它添加协议头
            return new URL(`https://${host}`).hostname;
          }
        })
      : (allowDomains as unknown as null);

    if (allowProtocols !== null && !allowProtocols.includes(urlInstance.protocol)) {
      console.error(`Invalid protocol: ${urlInstance.protocol}`);
      if (throwOnError) throw new CustomError(`Invalid protocol: ${urlInstance.protocol}`);
      return null;
    }
    if (allowHostName !== null && !allowHostName.includes(urlInstance.hostname)) {
      console.error(`Invalid host: ${urlInstance.hostname}`);
      if (throwOnError) throw new CustomError(`Invalid host: ${urlInstance.hostname}`);
      return null;
    }

    return returnRaw ? url : urlInstance.href;
  } catch (error) {
    if (error instanceof CustomError) {
      if (throwOnError) throw error;
      return null;
    }
    console.error(`Failed to convert url: ${url}`);
    if (throwOnError) throw new Error(`Failed to convert url: ${url}`);
    return null;
  }
}
