import { Collection, _, loc, createButton, InputOptions } from '@okta/courage';

import AuthenticatorEnrollOptions from '../components/AuthenticatorEnrollOptions';
import AuthenticatorVerifyOptions from '../components/AuthenticatorVerifyOptions';
import { getAuthenticatorDataForEnroll, getAuthenticatorDataForVerification } from '../utils/AuthenticatorUtil';
import { AUTHENTICATOR_KEY, FORMS as RemediationForms } from '../../ion/RemediationConstants';
import IDP from 'util/IDP';
import AdminScopeList from 'v1/views/admin-consent/ScopeList';
import EnduserScopeList from 'v1/views/consent/ScopeList';
import CaptchaView from '../views/captcha/CaptchaView';

const isTextOverflowing = (text, maxWidth) => {
  // Create a temporary element and attach it to the document so we can compare the client width to the 
  // max width allowed.
  const elem = document.createElement('div');
  elem.style.position = 'absolute';
  elem.style.left = '-9999px';
  elem.style.whiteSpace = 'nowrap';
  elem.innerHTML = text;

  document.body.appendChild(elem);
  const result = elem.clientWidth;
  document.body.removeChild(elem);

  return result > maxWidth;
};

const createAuthenticatorEnrollSelectView = (opt) => {
  const optionItems = (opt.options || [])
    .map(opt => {
      return Object.assign({}, opt, getAuthenticatorDataForEnroll(opt));
    });
  return {
    View: AuthenticatorEnrollOptions,
    options: {
      name: opt.name,
      collection: new Collection(optionItems),
    }
  };
};

const createAuthenticatorVerifySelectView = (opt) => {
  let optionItems = (opt.options || []);
  // If webauthn enrollments > 1 just show one entry with a generic namne (first) so user doesnt have to select which
  // one to pick. eg) If there is yubikey5 and another unknown u2f key, user cannot identify that easily. We need to
  // do this at least  until users can give authenticator enrollments custom names.
  const authSet = new Set();
  optionItems = optionItems.filter(opt => {
    let isDuplicate;
    if (opt.authenticatorKey === AUTHENTICATOR_KEY.WEBAUTHN) {
      isDuplicate = authSet.has(opt.authenticatorKey);
      authSet.add(opt.authenticatorKey);
    } else if(opt.authenticatorKey === AUTHENTICATOR_KEY.CUSTOM_APP) {
      // Filter the duplicate enrollment by id for custom app. 
      isDuplicate = authSet.has(opt?.value?.id);
      authSet.add(opt?.value?.id);
    }  
    return !isDuplicate;
  });
  optionItems = optionItems.map(opt => {
    return Object.assign({}, opt, getAuthenticatorDataForVerification(opt));
  });
  return {
    View: AuthenticatorVerifyOptions,
    options: {
      name: opt.name,
      collection: new Collection(optionItems),
    }
  };
};

const createAdminScopesView = () => {
  return {
    View: AdminScopeList,
  };
};
const createEnduserScopesView = () => {
  return {
    View: EnduserScopeList,
  };
};

const createCaptchaView = (opt) => {
  return {
    View: CaptchaView,
    options: {
      name: opt.name,
    }    
  };
};

const inputCreationStrategy = {
  authenticatorEnrollSelect: createAuthenticatorEnrollSelectView,
  authenticatorVerifySelect: createAuthenticatorVerifySelectView,
  ['admin-consent']: createAdminScopesView,
  ['consent']: createEnduserScopesView,
  ['captcha']: createCaptchaView,
};

// TODO: move logic to uiSchemaTransformer
const create = function(uiSchemaObj) {
  const strategyFn = inputCreationStrategy[uiSchemaObj.type] || _.identity;
  return strategyFn(uiSchemaObj);
};

const createPIVButton = (settings, appState) => {
  const pivIdp =
    appState.get('remediations').filter(idp => idp.name === RemediationForms.PIV_IDP);
  if (pivIdp.length < 1) {
    return [];
  }
  const pivConfig = settings.get('piv');
  const className = pivConfig.className || '';
  return [{
    attributes: {
      'data-se': 'piv-card-button',
    },
    className: className + ' piv-button',
    title: pivConfig.text || loc('piv.cac.card', 'login'),
    click: (e) => {
      e.preventDefault();
      appState.trigger('switchForm', RemediationForms.PIV_IDP);
    },
  }];
};

/**
 * Example of `redirect-idp` remediation.
 * {
 *   "name": "redirect-idp",
 *   "type": "MICROSOFT",
 *   "idp": {
 *      "id": "0oa2szc1K1YPgz1pe0g4",
 *      "name": "Microsoft IDP"
 *    },
 *   "href": "http://localhost:3000/sso/idps/0oa2szc1K1YPgz1pe0g4?stateToken=BB...AA",
 *   "method": "GET"
 * }
 *
 */
const createIdpButtons = ({ settings, appState }) => {
  // This is the max allowable text width that we can display for IDP buttons.
  // Generated by taking the 316px width of button minus padding-left (90 px) and padding-right (50px)
  const MAX_IDP_BUTTON_WIDTH = 186;

  const redirectIdpRemediations =
    appState.get('remediations').filter(idp => idp.name === RemediationForms.REDIRECT_IDP);

  if (!Array.isArray(redirectIdpRemediations)) {
    return [];
  }

  // create piv button
  const pivButton = createPIVButton(settings, appState);

  //add buttons from idp object
  const idpButtons = redirectIdpRemediations.map(idpObject => {
    let type = idpObject.type?.toLowerCase();
    let displayName;

    if (!_.contains(IDP.SUPPORTED_SOCIAL_IDPS, type)) {
      type = 'general-idp';
      // OKTA-396684 - makes sure that custom idps always have a name
      displayName = loc('customauth.sign.in.with.label', 'login', [idpObject.idp?.name]);
    } else {
      displayName = loc(`socialauth.${type}.label`, 'login');
    }

    const classNames = [
      'social-auth-button',
      `social-auth-${type}-button`,
    ];

    if (type === 'general-idp') {
      classNames.push('no-translate');
    }

    if (idpObject.idp.className) {
      classNames.push(idpObject.idp.className);
    }

    const button: InputOptions = {
      attributes: {
        'data-se': `social-auth-${type}-button`,
      },
      className: classNames.join(' '),
      title: displayName,
      href: idpObject.href,
    };

    if (isTextOverflowing(displayName, MAX_IDP_BUTTON_WIDTH)) {
      // We add a tooltip in case the name gets truncated if too long
      button.attributes.title = displayName;
    }

    return button;
  });

  return [...pivButton, ...idpButtons];
};

const createCustomButtons = (settings) => {
  const customButtons = settings.get('customButtons');
  return customButtons.map(customButton => {
    const button = {
      attributes: {
        'data-se': customButton.dataAttr
      },
      className: customButton.className + ' default-custom-button',
      title: customButton.title || loc(customButton.i18nKey, 'login'),
      click: customButton.click
    };
    return button;
  });
};

const addCustomButton = (customButtonSettings) => {
  return createButton({
    ...customButtonSettings,
    className: `${customButtonSettings.className} default-custom-button button-primary`,
  });
};

export {
  create,
  createIdpButtons,
  createCustomButtons,
  addCustomButton,
  isTextOverflowing
};
