import React from 'react';

class DigigovError extends Error {
  constructor(message) {
    super(message); // (1)
    this.name = 'DigigovError'; // (2)
  }
}
const loggedDeprecations: string[] = [];
export interface ErrorWarning {
  /**
   * Will log a warning if true.
   */
  warning?: string | true;
  error?: string | true;
}
export interface PropValueDeprecations {
  values?: Record<string, ErrorWarning>;
}
export type Deprecations = {
  name: string;
  rename?: string;
  props?: Record<string, PropValueDeprecations & ErrorWarning>;
} & ErrorWarning;
const warnOnce = (name, message) => {
  if (loggedDeprecations.includes(name)) {
    return;
  } else {
    loggedDeprecations.push(name);
  }
  console.warn(message);
};
const handleErrorWarning = (
  componentName,
  name: string,
  { error, warning }: (PropValueDeprecations & ErrorWarning) | ErrorWarning,
  type
) => {
  if (warning === true) {
    warnOnce(
      componentName,
      `⚠️  ${componentName}: ${name} ${type} will be deprecated.`
    );
  } else if (typeof warning === 'string') {
    warnOnce(componentName, warning);
  }
  if (error === true) {
    throw new DigigovError(
      `${componentName}: !! ${name} ${type} is deprecated.`
    );
  } else if (typeof error === 'string') {
    throw new DigigovError(
      `Component \`${componentName}\` with \`${name}\` prop: ${error}`
    );
  }
};
export default function withDeprecation<Type>(
  Component: Type,
  deprecations: Deprecations
): Type {
  const componentName =
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    deprecations.name || Component.name || Component?.render?.name;
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  return function withDeprecationComponent(props) {
    if (deprecations.rename) {
      warnOnce(
        componentName,
        `♻️  ${componentName} is renamed to ${deprecations.rename}\n`
      );
    }
    handleErrorWarning(componentName, componentName, deprecations, 'component');
    if (deprecations.props) {
      Object.keys(deprecations.props).forEach((propName) => {
        const value = props[propName];
        if (!value) {
          return;
        }
        const propDeprecation = deprecations?.props?.[propName];
        if (propDeprecation?.values?.[value]) {
          return handleErrorWarning(
            componentName,
            `${propName} ${value}`,
            propDeprecation?.values?.[value],
            'property value'
          );
        }
        if (propDeprecation?.error || propDeprecation?.warning) {
          return handleErrorWarning(
            componentName,
            propName,
            propDeprecation,
            'property'
          );
        }
      });
    }
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    return <Component {...props} />;
  };
}
