"use client";

import React, { useContext } from 'react';
import { genCalc, token2CSSVar, useCSSVarRegister, useStyleRegister } from '@ant-design/cssinjs';
import { warning } from 'rc-util';
import useUniqueMemo from '../../_util/hooks/useUniqueMemo';
import { ConfigContext } from '../../config-provider/context';
import { genCommonStyle, genLinkStyle } from '../../style';
import useToken, { ignore, unitless } from '../useToken';
import genMaxMin from './maxmin';
import statisticToken, { merge as mergeToken } from './statistic';
import useResetIconStyle from './useResetIconStyle';
const getDefaultComponentToken = (component, token, getDefaultToken) => {
  var _a;
  if (typeof getDefaultToken === 'function') {
    return getDefaultToken(mergeToken(token, (_a = token[component]) !== null && _a !== void 0 ? _a : {}));
  }
  return getDefaultToken !== null && getDefaultToken !== void 0 ? getDefaultToken : {};
};
const getComponentToken = (component, token, defaultToken, options) => {
  const customToken = Object.assign({}, token[component]);
  if (options === null || options === void 0 ? void 0 : options.deprecatedTokens) {
    const {
      deprecatedTokens
    } = options;
    deprecatedTokens.forEach(_ref => {
      let [oldTokenKey, newTokenKey] = _ref;
      var _a;
      if (process.env.NODE_ENV !== 'production') {
        process.env.NODE_ENV !== "production" ? warning(!(customToken === null || customToken === void 0 ? void 0 : customToken[oldTokenKey]), `Component Token \`${String(oldTokenKey)}\` of ${component} is deprecated. Please use \`${String(newTokenKey)}\` instead.`) : void 0;
      }
      // Should wrap with `if` clause, or there will be `undefined` in object.
      if ((customToken === null || customToken === void 0 ? void 0 : customToken[oldTokenKey]) || (customToken === null || customToken === void 0 ? void 0 : customToken[newTokenKey])) {
        (_a = customToken[newTokenKey]) !== null && _a !== void 0 ? _a : customToken[newTokenKey] = customToken === null || customToken === void 0 ? void 0 : customToken[oldTokenKey];
      }
    });
  }
  const mergedToken = Object.assign(Object.assign({}, defaultToken), customToken);
  // Remove same value as global token to minimize size
  Object.keys(mergedToken).forEach(key => {
    if (mergedToken[key] === token[key]) {
      delete mergedToken[key];
    }
  });
  return mergedToken;
};
const getCompVarPrefix = (component, prefix) => `${[prefix, component.replace(/([A-Z]+)([A-Z][a-z]+)/g, '$1-$2').replace(/([a-z])([A-Z])/g, '$1-$2')].filter(Boolean).join('-')}`;
export default function genComponentStyleHook(componentName, styleFn, getDefaultToken) {
  let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  const cells = Array.isArray(componentName) ? componentName : [componentName, componentName];
  const [component] = cells;
  const concatComponent = cells.join('-');
  // Return new style hook
  return function (prefixCls) {
    let rootCls = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : prefixCls;
    const [theme, realToken, hashId, token, cssVar] = useToken();
    const {
      getPrefixCls,
      iconPrefixCls,
      csp
    } = useContext(ConfigContext);
    const rootPrefixCls = getPrefixCls();
    const type = cssVar ? 'css' : 'js';
    // Use unique memo to share the result across all instances
    const calc = useUniqueMemo(() => {
      const unitlessCssVar = new Set();
      if (cssVar) {
        Object.keys(options.unitless || {}).forEach(key => {
          // Some component proxy the AliasToken (e.g. Image) and some not (e.g. Modal)
          // We should both pass in `unitlessCssVar` to make sure the CSSVar can be unitless.
          unitlessCssVar.add(token2CSSVar(key, cssVar.prefix));
          unitlessCssVar.add(token2CSSVar(key, getCompVarPrefix(component, cssVar.prefix)));
        });
      }
      return genCalc(type, unitlessCssVar);
    }, [type, component, cssVar === null || cssVar === void 0 ? void 0 : cssVar.prefix]);
    const {
      max,
      min
    } = genMaxMin(type);
    // Shared config
    const sharedConfig = {
      theme,
      token,
      hashId,
      nonce: () => csp === null || csp === void 0 ? void 0 : csp.nonce,
      clientOnly: options.clientOnly,
      layer: {
        name: 'antd'
      },
      // antd is always at top of styles
      order: options.order || -999
    };
    // Generate style for all a tags in antd component.
    useStyleRegister(Object.assign(Object.assign({}, sharedConfig), {
      clientOnly: false,
      path: ['Shared', rootPrefixCls]
    }), () => [{
      // Link
      '&': genLinkStyle(token)
    }]);
    // Generate style for icons
    useResetIconStyle(iconPrefixCls, csp);
    const wrapSSR = useStyleRegister(Object.assign(Object.assign({}, sharedConfig), {
      path: [concatComponent, prefixCls, iconPrefixCls]
    }), () => {
      if (options.injectStyle === false) {
        return [];
      }
      const {
        token: proxyToken,
        flush
      } = statisticToken(token);
      const defaultComponentToken = getDefaultComponentToken(component, realToken, getDefaultToken);
      const componentCls = `.${prefixCls}`;
      const componentToken = getComponentToken(component, realToken, defaultComponentToken, {
        deprecatedTokens: options.deprecatedTokens
      });
      if (cssVar) {
        Object.keys(defaultComponentToken).forEach(key => {
          defaultComponentToken[key] = `var(${token2CSSVar(key, getCompVarPrefix(component, cssVar.prefix))})`;
        });
      }
      const mergedToken = mergeToken(proxyToken, {
        componentCls,
        prefixCls,
        iconCls: `.${iconPrefixCls}`,
        antCls: `.${rootPrefixCls}`,
        calc,
        // @ts-ignore
        max,
        // @ts-ignore
        min
      }, cssVar ? defaultComponentToken : componentToken);
      const styleInterpolation = styleFn(mergedToken, {
        hashId,
        prefixCls,
        rootPrefixCls,
        iconPrefixCls
      });
      flush(component, componentToken);
      return [options.resetStyle === false ? null : genCommonStyle(mergedToken, prefixCls, rootCls, options.resetFont), styleInterpolation];
    });
    return [wrapSSR, hashId];
  };
}
export const genSubStyleComponent = (componentName, styleFn, getDefaultToken, options) => {
  const useStyle = genComponentStyleHook(componentName, styleFn, getDefaultToken, Object.assign({
    resetStyle: false,
    // Sub Style should default after root one
    order: -998
  }, options));
  const StyledComponent = _ref2 => {
    let {
      prefixCls,
      rootCls = prefixCls
    } = _ref2;
    useStyle(prefixCls, rootCls);
    return null;
  };
  if (process.env.NODE_ENV !== 'production') {
    StyledComponent.displayName = `SubStyle_${Array.isArray(componentName) ? componentName.join('.') : componentName}`;
  }
  return StyledComponent;
};
const genCSSVarRegister = (component, getDefaultToken, options) => {
  const {
    unitless: compUnitless,
    injectStyle = true,
    prefixToken
  } = options;
  const CSSVarRegister = _ref3 => {
    let {
      rootCls,
      cssVar
    } = _ref3;
    const [, realToken] = useToken();
    useCSSVarRegister({
      path: [component],
      prefix: cssVar.prefix,
      key: cssVar === null || cssVar === void 0 ? void 0 : cssVar.key,
      unitless: compUnitless,
      ignore,
      token: realToken,
      scope: rootCls
    }, () => {
      const defaultToken = getDefaultComponentToken(component, realToken, getDefaultToken);
      const componentToken = getComponentToken(component, realToken, defaultToken, {
        deprecatedTokens: options === null || options === void 0 ? void 0 : options.deprecatedTokens
      });
      Object.keys(defaultToken).forEach(key => {
        componentToken[prefixToken(key)] = componentToken[key];
        delete componentToken[key];
      });
      return componentToken;
    });
    return null;
  };
  const useCSSVar = rootCls => {
    const [,,,, cssVar] = useToken();
    return [node => injectStyle && cssVar ? ( /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(CSSVarRegister, {
      rootCls: rootCls,
      cssVar: cssVar,
      component: component
    }), node)) : node, cssVar === null || cssVar === void 0 ? void 0 : cssVar.key];
  };
  return useCSSVar;
};
export const genStyleHooks = (component, styleFn, getDefaultToken, options) => {
  const componentName = Array.isArray(component) ? component[0] : component;
  function prefixToken(key) {
    return `${componentName}${key.slice(0, 1).toUpperCase()}${key.slice(1)}`;
  }
  // Fill unitless
  const originUnitless = (options === null || options === void 0 ? void 0 : options.unitless) || {};
  const compUnitless = Object.assign(Object.assign({}, unitless), {
    [prefixToken('zIndexPopup')]: true
  });
  Object.keys(originUnitless).forEach(key => {
    compUnitless[prefixToken(key)] = originUnitless[key];
  });
  // Options
  const mergedOptions = Object.assign(Object.assign({}, options), {
    unitless: compUnitless,
    prefixToken
  });
  // Hooks
  const useStyle = genComponentStyleHook(component, styleFn, getDefaultToken, mergedOptions);
  const useCSSVar = genCSSVarRegister(componentName, getDefaultToken, mergedOptions);
  return function (prefixCls) {
    let rootCls = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : prefixCls;
    const [, hashId] = useStyle(prefixCls, rootCls);
    const [wrapCSSVar, cssVarCls] = useCSSVar(rootCls);
    return [wrapCSSVar, hashId, cssVarCls];
  };
};