import _ from 'lodash';
import Bugsnag from '@bugsnag/js';
import { Auth } from '@capillarytech/cap-ui-utils';
import {
  STORE2DOOR_PLUS_ENABLED,
  TRAI_DLT,
  CARD_RELATED_TAGS,
  CARD_BASED_SCOPE,
  HOSPITALITY_RELATED_TAGS,
  HOSPITALITY_BASED_SCOPE,
  GIFT_CARDS,
  PROMO_ENGINE,
  EXTENDED_TAG,
  CUSTOMER_EXTENDED_FIELD,
  CUSTOM_TAG,
  REGISTRATION_CUSTOM_FIELD,
  JP_LOCALE_HIDE_FEATURE,
  ENABLE_CUSTOMER_BARCODE_TAG,
  AI_CONTENT_BOT_DISABLED,
  BADGES_UI_ENABLED,
  BADGES_ENROLL,
  EMAIL_UNSUBSCRIBE_TAG_MANDATORY,
  BADGES_ISSUE,
  ENABLE_WECHAT,
  ENABLE_WEBPUSH,
  SUPPORT_CK_EDITOR,
  ENABLE_NEW_MPUSH,
  ENABLE_NEW_EDITOR_FLOW_INAPP,
  SUPPORT_ENGAGEMENT_MODULE,
  ENABLE_CREATIVES_ARCHIVAL,
} from '../constants/unified';
import { apiMessageFormatHandler } from './commonUtils';

export function getUserNameById(userId, allUserList) {
  let userName = "";
  _.forEach(allUserList, (user) => {
    if (user.userId === userId) {
      userName = `${user.firstName} ${user.lastName}`;
    }
  });
  return userName;
}

export function getMergedUserList(userList, isCapUser = true) {
  const mergeUserList = [];
  _.forEach(userList, (data, key) => {
    if (isCapUser || (!isCapUser && key !== 'capUsers')) {
      _.forEach(data, (userObj) => {
        mergeUserList.push(userObj);
      });
    }
  });
  return mergeUserList;
}

export function removeLinksFromHtmlContent(content) {
  const doc = document.createElement('html');
  doc.innerHTML = content;
  let copyContent = doc.cloneNode(true).outerHTML;
  copyContent = copyContent.replace(/<a.+?>/g, '<a>');
  doc.remove();
  return copyContent;
}

export function iframePreviewAdjustWidth(content) {
  const doc = document.createElement('html');
  doc.innerHTML = content;
  let copyContent = doc.cloneNode(true).outerHTML;
  copyContent = copyContent.replace(/<img /g, '<img width="310" ');
  doc.remove();
  return copyContent;
}

export const hasStore2DoorFeature = Auth.hasFeatureAccess.bind(
  null,
  STORE2DOOR_PLUS_ENABLED,
);

export const hasTraiDltFeature = Auth.hasFeatureAccess.bind(
  null,
  TRAI_DLT,
);

export const hasCardBasedScope = Auth.hasFeatureAccess.bind(
  null,
  CARD_BASED_SCOPE,
);

export const hasHospitalityBasedScope = Auth.hasFeatureAccess.bind(
  null,
  HOSPITALITY_BASED_SCOPE,
);
export const hasPromoFeature = Auth.hasFeatureAccess.bind(
  null,
  PROMO_ENGINE,
);
export const hasSupportCKEditor = Auth.hasFeatureAccess.bind(
  null,
  SUPPORT_CK_EDITOR,
);
export const hasSupportEngagementModule = Auth.hasFeatureAccess.bind(
  null,
  SUPPORT_ENGAGEMENT_MODULE,
);

export const hasGiftVoucherFeature = Auth.hasFeatureAccess.bind(
  null,
  GIFT_CARDS,
);

export const hasBadgesFeature = Auth.hasFeatureAccess.bind(
  null,
  BADGES_UI_ENABLED
);

export const hasJPLocaleHideFeatureEnabled = Auth.hasFeatureAccess.bind(
  null,
  JP_LOCALE_HIDE_FEATURE,
);

export const hasWechatFeatureEnabled = Auth.hasFeatureAccess.bind(
  null,
  ENABLE_WECHAT,
);

export const hasCreativesArchivalEnabled = Auth.hasFeatureAccess.bind(
  null,
  ENABLE_CREATIVES_ARCHIVAL,
);

export const hasWebPushFeatureEnabled = Auth.hasFeatureAccess.bind(
  null,
  ENABLE_WEBPUSH,
);

export const hasCustomerBarcodeFeatureEnabled = Auth.hasFeatureAccess.bind(
  null,
  ENABLE_CUSTOMER_BARCODE_TAG,
);

export const isAiContentBotDisabled = Auth.hasFeatureAccess.bind(
  null,
  AI_CONTENT_BOT_DISABLED,
);

// Note: The "EMAIL_UNSUBSCRIBE_TAG_MANDATORY" feature flag determines if the Unsubscribe tag in email is optional. 
// When this flag is enabled for an org, the Unsubscribe tag is NOT mandatory in the email flow.
// This is as per the requirement in the tech doc:
// https://capillarytech.atlassian.net/wiki/spaces/CAM/pages/3941662838/Remove+mandate+for+Unsubscribe+tag+in+email+flow
export const isEmailUnsubscribeTagOptional = Auth.hasFeatureAccess.bind(
  null,
  EMAIL_UNSUBSCRIBE_TAG_MANDATORY,
);

export const hasNewMobilePushFeatureEnabled = Auth.hasFeatureAccess.bind(
  null,
  ENABLE_NEW_MPUSH,
);

export const hasNewEditorFlowInAppEnabled = Auth.hasFeatureAccess.bind(
  null,
  ENABLE_NEW_EDITOR_FLOW_INAPP,
);

//filtering tags based on scope
export const filterTags = (tagsToFilter, tagsList) => tagsList?.filter(
    (tag) => !tagsToFilter?.includes(tag?.definition?.value)
);

export function getTreeStructuredTags({tagsList, userLocale = 'en', offerDetails = [], disableTagsDetails = {}}) {
  const mainTags = {};
  const { disableRelatedTags, childTagsToDisable, parentTagstoDisable} = disableTagsDetails;

  let clonedTags = tagsList;
  if (!hasCardBasedScope()) {
    //filtering CARD_RELATED_TAGS if org does not have CARD_BASED_SCOPE feature enabled
    clonedTags = filterTags(CARD_RELATED_TAGS, tagsList);
  }
  if (!hasHospitalityBasedScope()) {
    //filtering HOSPITALITY_RELATED_TAGS if org does not have HOSPITALITY_BASED_SCOPE feature enabled
    clonedTags = filterTags(HOSPITALITY_RELATED_TAGS, tagsList);
  }
  _.forEach(clonedTags, (temp) => {
    const tag = temp.definition;
    if (!tag['tag-header']) { //if tag doesn't have subtag(s), which means this tag itself is tag and not a tag header.
      mainTags[tag.value] = {
        name: tag.label[userLocale] || tag.label.en,
        value: tag.value,
        disabled: disableRelatedTags ? childTagsToDisable.includes(tag.value) : false,
      };
    } else if (tag['tag-header'] && mainTags[tag.value]) {
      // to check it this tag header is already available in mainTags object,
      //If yes, it will concat the subtag
      mainTags[tag.value].subtags = _.concat(mainTags[tag.value].subtags, tag.subtags);
    } else if (!mainTags[tag.value]) {
      //If tag is not available in mainTag object, new maintag will be added.
      mainTags[tag.value] = {
        'tag-header': true,
        "name": tag.label[userLocale] || tag.label.en,
        "subtags": tag.subtags,
        "value": tag.value,
        "disabled": disableRelatedTags ? parentTagstoDisable.includes(tag.value) : false,
      };
    }
  });

  const mainTagsCloned = _.cloneDeep(mainTags);
  //Insert subtags in tag headers and reomve them from top level
  const result = {};
  _.forEach(mainTagsCloned, (tag, key) => {
    if (tag['tag-header']) {
      const tagWithChildren = populateTagForChildren(tag, mainTagsCloned, key);
      tagWithChildren.resolved = true;
      result[key] = tagWithChildren;
    } else {
      result[key] = tag;
    }
  });

  //Delete extra tag headers
  _.forEach(result, (tag) => {
    if (tag['tag-header']) {
      _.forEach(tag.subtags, (subtag, key) => {
        if (result[key]) {
          result[key].deleted = true;
        }
      });
    }
  });
  //Actually delete them
  _.forEach(result, (tag, key) => {
    if (tag.deleted) {
      delete result[key];
    }
  });
  let combinedTags = result;
  if (offerDetails?.length) {
    if ([BADGES_ENROLL, BADGES_ISSUE].includes(offerDetails?.[0]?.type)) {
      combinedTags = transformBadgeTags(offerDetails, result);
    } else {
      combinedTags = transformCouponTags(offerDetails, result);
    }
  }
  return getTreeStructureData(combinedTags);
}

function transformCouponTags(offerDetails, allTags) {
  const couponTags = {};
  const couponTagsKeys = Object.keys(_.get(allTags, 'coupon.subtags', []));
  const withCouponId = (obj, id) => {
    const tempObj = {};
    if (obj.subtags) {
      tempObj.subtags = _.mapValues(obj.subtags, (tag) => {
        let val = tag.value;
        val = `${val}(${id})`;
        const newObj = {
          ...tag,
          value: val,
        };
        return {
          ...withCouponId(newObj, id),
        };
      });
    }
    return {
      ...obj,
      ...tempObj,
      incentiveSeriesId: id,
    };
  };
  offerDetails.forEach((offer) => {
    const { id, couponName, description, couponSeriesId } = offer;
    const couponId = id || couponSeriesId;
    const couponSubTags = {};
    couponTagsKeys.forEach((couponTagKey) => {
      let val = allTags.coupon.subtags[couponTagKey].value;
      val = `${val}(${couponId})`;
      const newObj = {
        ...allTags.coupon.subtags[couponTagKey],
        value: val,
      };
      const couponSubTagsObj = withCouponId(newObj, couponId);
      couponSubTags[couponTagKey] = couponSubTagsObj;
    });
    const couponNameFinal = couponName || description;
    const key = couponNameFinal || couponId;
    couponTags[key] = {
      'tag-header': true,
      'name': couponNameFinal || couponId,
      'desc': couponNameFinal || couponId,
      'incentiveSeriesId': couponId,
      'subtags': couponSubTags,
      'resolved': true,
      'couponTags': true,
    };
  });
  // eslint-disable-next-line no-param-reassign
  allTags.coupon.subtags = couponTags;
  return allTags;
}

export function transformBadgeTags(badgeDetails = [], allTags) {
  const badgeTags = {};
  const badgeTagsKeys = Object.keys(_.get(allTags, 'Badge.subtags', []));
  const withBadgeId = (obj, id) => {
    const tempObj = {};
    if (obj?.subtags) {
      tempObj.subtags = _.mapValues(obj.subtags, (tag) => ({
        ...withBadgeId(tag, id),
      }));
    }
    return {
      ...obj,
      ...tempObj,
      incentiveSeriesId: id,
    };
  };
  badgeDetails.forEach((offer) => {
    const { badgesMetaId, badgeName } = offer || {};
    const badgeSubTags = {};
    badgeTagsKeys.forEach((badgeTagKey) => {
      const badgeSubTagsObj = withBadgeId(allTags?.Badge?.subtags?.[badgeTagKey], badgesMetaId);
      badgeSubTags[badgeTagKey] = badgeSubTagsObj;
    });
    badgeTags[badgesMetaId] = {
      'tag-header': true,
      'name': badgeName,
      'desc': badgeName,
      'incentiveSeriesId': badgesMetaId,
      'subtags': badgeSubTags,
      'resolved': true,
      'couponTags': true,
    };
  });
  // eslint-disable-next-line no-param-reassign
  allTags.Badge.subtags = badgeTags;
  return allTags;
}

function populateTagForChildren(targetTag, tagsObject, tagValue) {
  const tag = targetTag;
  if (tag && tag['tag-header'] && !tag.resolved) {
    const tempSubTags = {};
    _.forEach(tag.subtags, (subtag) => {
      if (tagsObject[subtag]) {
        const children = populateTagForChildren(tagsObject[subtag], tagsObject, subtag);
        children.resolved = true;
        tempSubTags[subtag] = children;
      }
    });
    tag.subtags = tempSubTags;
    return tag;
  }
  return tagsObject[tagValue];
}

function getTreeStructureData(data) {
  const result = [];
  _.forOwn(data, (mainTag) => {
    const { name, value, subtags = [], disabled } = mainTag;
    if (!mainTag['tag-header']) {
      result.push({
        title: name,
        value: `{{${value}}}`,
        disabled,
      });
    } else {
      const obj = {
        title: name,
        value: `{{${value}}}`,
        selectable: false,
        disabled,
      };
      obj.children = getSubTags(subtags);
      result.push(obj);
    }
  });
  return result;
}

function getSubTags(subtagsParam) {
  const result = [];
  _.forOwn(subtagsParam, (subTag) => {
    const { name, value, subtags = [], disabled } = subTag;
    if (_.isEmpty(subtags)) {
      result.push({
        title: name,
        value: `{{${value}}}`,
        disabled,
      });
    } else {
      const obj = {
        title: name,
        value: `{{${value}}}`,
        selectable: false,
        disabled,
      };
      obj.children = getSubTags(subtags);
      result.push(obj);
    }
  });
  return result;
}

export const bytes2Size = (bytes, decimals = 2) => {
  if (bytes === 0) return '0 Bytes';

  const k = 1024;
  const dm = decimals < 0 ? 0 : decimals;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

  const i = Math.floor(Math.log(bytes) / Math.log(k));

  return `${Math.ceil((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
};

export const isTraiDLTEnable = (isFullMode, smsRegister) => {
  const isTraiDltFeatureForOrg = hasTraiDltFeature();
  const isTraiEnableforLib = (!isFullMode && smsRegister === "DLT") || isFullMode;
  const isTraiDltFeature = isTraiDltFeatureForOrg && isTraiEnableforLib;
  return isTraiDltFeature;
};

//Input: " Spaces In Input "---> Output: "spaces_in_input"
export const intlKeyGenerator = (value = "") => String(value).replace(/[^a-zA-Z0-9_ ]/g, "").replace(/ /g, "_").toLowerCase();

export const handleInjectedData = (data, scope) => {
  let tagType;
  for (const tagKey in data) {
    if (data.hasOwnProperty(tagKey)) {
      const tag = data[tagKey];
      if (tag?.name === REGISTRATION_CUSTOM_FIELD) {
        tagType = CUSTOM_TAG;
      } else if (tag?.name === CUSTOMER_EXTENDED_FIELD) {
        tagType = EXTENDED_TAG;
      }

      if (tag?.name) {
        const name = tag.name;
        const key = intlKeyGenerator(name);
        const id = tagType
          ? `${scope}.${key}_name.${tagType}`
          : `${scope}.${key}`;

        tag.name = apiMessageFormatHandler(id, name);
      }

      if (tag?.subtags) {
        for (const subtagKey in tag.subtags) {
          if (tag.subtags.hasOwnProperty(subtagKey)) {
            const subtag = tag.subtags[subtagKey];
            if (subtag?.name) {
              const name = subtag?.name;
              const key = intlKeyGenerator(name);
              const id = tagType
                ? `${scope}.${key}_name.${tagType}`
                : `${scope}.${key}`;

              subtag.name = apiMessageFormatHandler(id, name);
            }
            if (subtag?.desc) {
              const desc = subtag?.desc;
              const key = intlKeyGenerator(desc);
              const id = tagType
                ? `${scope}.${key}_desc.${tagType}`
                : `${scope}.${key}`;

              subtag.desc = apiMessageFormatHandler(id, desc);
            }
          }
        }
      }
      tagType = "";
    }
  }
  return data;
};

export const handlePreviewInNewTab = (previewUrl) => {
  window.open(previewUrl, '_blank');
};

export const getDecodedFileName = (templateName = '') => {
  try {
    return decodeURIComponent(templateName);
  } catch (error) {
    // If decoding fails due to URIError, using the original encoded filename
    if (error instanceof URIError) {
      return templateName;
    } else {
      // For other errors, registering the exception in Bugsnag
      Bugsnag.leaveBreadcrumb("Error decoding the string:", error);
      Bugsnag.notify(error, (event) => {
        event.severity = "error";
      });
    }
  }
  return templateName;
};

export const createQueryString = (getQuery) => {
  if (!getQuery || typeof getQuery !== "object") {
      return ""; // Return an empty string if getQuery is invalid
  }

  const queryParams = Object?.entries(getQuery)
      ?.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
      ?.join("&");

  return queryParams ? `?${queryParams}` : ""; // Prepend "?" if there are query parameters
}