import React, {
  useState, useEffect, useCallback, useMemo, useRef,
} from "react";
import isEmpty from 'lodash/isEmpty';
import get from 'lodash/get';
import { bindActionCreators } from "redux";
import { createStructuredSelector } from "reselect";
import { injectIntl, FormattedMessage } from "react-intl";
import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
import CapHeading from "@capillarytech/cap-ui-library/CapHeading";
import CapSpin from "@capillarytech/cap-ui-library/CapSpin";
import CapRadioGroup from "@capillarytech/cap-ui-library/CapRadioGroup";
import CapRow from "@capillarytech/cap-ui-library/CapRow";
import CapColumn from "@capillarytech/cap-ui-library/CapColumn";
import CapButton from "@capillarytech/cap-ui-library/CapButton";
import CapNotification from "@capillarytech/cap-ui-library/CapNotification";
import CapTab from "@capillarytech/cap-ui-library/CapTab";
import CapInput from "@capillarytech/cap-ui-library/CapInput";
import { makeSelectInApp, makeSelectAccount, makeSelectGetTemplateDetailsInProgress } from "./selectors";
import * as globalActions from '../Cap/actions';
import {
  isLoadingMetaEntities,
  makeSelectMetaEntities,
  setInjectedTags,
  selectCurrentOrgDetails,
  selectLiquidStateDetails,
} from "../Cap/selectors";
import * as inAppActions from "./actions";
import './index.scss';
import messages from "./messages";
import globalMessages from "../Cap/messages";
import creativesMessages from '../CreativesContainer/messages';
import withCreatives from "../../hoc/withCreatives";
import UnifiedPreview from "../../v2Components/CommonTestAndPreview/UnifiedPreview";
import TestAndPreviewSlidebox from '../../v2Components/TestAndPreviewSlidebox';
import injectReducer from '../../utils/injectReducer';
import v2InAppReducer from './reducer';
import { v2InAppSagas } from './sagas';
import injectSaga from '../../utils/injectSaga';
import {
  ANDROID,
  BIG_PICTURE,
  BIG_TEXT,
  BIG_HTML,
  DEEP_LINK,
  DEVICE_SUPPORTED,
  INAPP_BUTTON_TYPES,
  INAPP_MEDIA_TYPES,
  INAPP_MESSAGE_LAYOUT_TYPES,
  INITIAL_CTA_DATA,
  IOS,
  LAYOUT_RADIO_OPTIONS,
  IOS_CAPITAL,
} from "./constants";
import { GENERIC, INAPP, SMS } from "../CreativesContainer/constants";
import {
  ALL, TAG, EMBEDDED, DEFAULT, FULL, LIBRARY,
} from "../Whatsapp/constants";
import { getCdnUrl } from "../../utils/cdnTransformation";
import { getCtaObject, hasAnyErrors, getSingleTab } from "./utils";
import { validateInAppContent } from "../../utils/commonUtils";
import { validateTags } from "../../utils/tagValidations";
import { hasNewEditorFlowInAppEnabled, isAiContentBotDisabled } from "../../utils/common";
import formBuilderMessages from "../../v2Components/FormBuilder/messages";
import HTMLEditor from "../../v2Components/HtmlEditor";
import { HTML_EDITOR_VARIANTS } from "../../v2Components/HtmlEditor/constants";
import { INAPP_EDITOR_TYPES } from "../InAppWrapper/constants";
import InappAdvanced from "../InappAdvance/index";
import CapDeviceContent from "../../v2Components/CapDeviceContent";
import capDeviceContentMessages from "../../v2Components/CapDeviceContent/messages";
import { ErrorInfoNote } from "../../v2Components/ErrorInfoNote";

let editContent = {};

export const InApp = (props) => {
  const {
    intl,
    actions,
    globalActions,
    isFullMode,
    isLoyaltyModule,
    onCreateComplete,
    params,
    templateData = {},
    defaultData = {},
    editData = {},
    accountData = {},
    location,
    getDefaultTags,
    supportedTags,
    metaEntities,
    injectedTags,
    getFormData,
    selectedOfferDetails,
    fetchingLiquidValidation,
    templateName,
    getTemplateDetailsInProgress,
    isEditInApp,
    setIsLoadingContent,
    query,
    inAppCreateMode,
    isGetFormData,
    showTemplateName,
    onValidationFail,
    type,
    forwardedTags,
    currentOrgDetails,
    // TestAndPreviewSlidebox props
    showTestAndPreviewSlidebox: propsShowTestAndPreviewSlidebox,
    handleTestAndPreview: propsHandleTestAndPreview,
    handleCloseTestAndPreview: propsHandleCloseTestAndPreview,
  } = props || {};

  const { formatMessage } = intl;
  const [titleAndroid, setTitleAndroid] = useState("");
  const [titleIos, setTitleIos] = useState("");
  const [templateMediaType, setTemplateMediaType] = useState(
    INAPP_MEDIA_TYPES.TEXT
  );
  const [tempName, setTempName] = useState("");
  const [templateLayoutType, setTemplateLayoutType] = useState(
    INAPP_MESSAGE_LAYOUT_TYPES.MODAL
  );
  const [addActionLinkAndroid, setAddActionLinkAndroid] = useState(false);
  const [addActionLinkIos, setAddActionLinkIos] = useState(false);
  const [deepLinkValueAndroid, setDeepLinkValueAndroid] = useState('');
  const [deepLinkValueIos, setDeepLinkValueIos] = useState('');
  const [templateMessageAndroid, setTemplateMessageAndroid] = useState("");
  const [templateMessageIos, setTemplateMessageIos] = useState("");
  const [templateMessageErrorAndroid, setTemplateMessageErrorAndroid] = useState(false);
  const [templateMessageErrorIos, setTemplateMessageErrorIos] = useState(false);
  const [templateTitleErrorAndroid, setTemplateTitleErrorAndroid] = useState(false);
  const [templateTitleErrorIos, setTemplateTitleErrorIos] = useState(false);
  // HTML Editor content state (for INAPP HTML variant)
  // Initialize HTML content from edit data if available
  const getInitialHtmlContent = (device) => {
    const editContent = isFullMode
      ? get(editData?.templateDetails?.versions, `base.content`, {})
      : get(templateData?.versions, `base.content`, {});
    const deviceContent = editContent?.[device];
    return deviceContent?.message || "";
  };

  const [htmlContentAndroid, setHtmlContentAndroid] = useState(() => getInitialHtmlContent("ANDROID"));
  const [htmlContentIos, setHtmlContentIos] = useState(() => getInitialHtmlContent("IOS"));
  // Track if this is an HTML template (type === HTML or style === BIG_HTML)
  const [isHTMLTemplate, setIsHTMLTemplate] = useState(false);
  // Version to force HTMLEditor remount on layout changes
  const [htmlEditorContentVersion, setHtmlEditorContentVersion] = useState(0);
  // Refs to store latest content before layout changes
  const htmlContentAndroidRef = useRef(htmlContentAndroid);
  const htmlContentIosRef = useRef(htmlContentIos);
  const [accountId, setAccountId] = useState("");
  const [accessToken, setAccessToken] = useState("");
  const [accountName, setAccountName] = useState("");
  const [deepLink, setDeepLink] = useState([]);
  const [spin, setSpin] = useState(false);
  const [inAppImageSrcAndroid, setInAppImageSrcAndroid] = useState("");
  const [inAppImageSrcIos, setInAppImageSrcIos] = useState("");
  const [panes, setPanes] = useState(ANDROID);
  //for tag only
  const [tags, updateTags] = useState([]);
  //for edit only
  const [isEditFlow, setEditFlow] = useState(false);
  const [templateDate, setTemplateDate] = useState("");

  //buttons
  const [ctaDataAndroid, setCtaDataAndroid] = useState(INITIAL_CTA_DATA);
  const [ctaDataIos, setCtaDataIos] = useState(INITIAL_CTA_DATA);
  const [buttonTypeAndroid, setButtonTypeAndroid] = useState(
    INAPP_BUTTON_TYPES.NONE
  );
  const [buttonTypeIos, setButtonTypeIos] = useState(INAPP_BUTTON_TYPES.NONE);
  const isBtnTypeCtaAndroid = buttonTypeAndroid === INAPP_BUTTON_TYPES.CTA;
  const isBtnTypeCTaIos = buttonTypeIos === INAPP_BUTTON_TYPES.CTA;

  // TestAndPreviewSlidebox state
  const [showTestAndPreviewSlidebox, setShowTestAndPreviewSlidebox] = useState(false);

  // Get template content for TestAndPreviewSlidebox
  // Returns simple structure with preview fields only
  // Transformation to payload structure happens in prepareTestMessagePayload
  // Reference: Based on getPreviewSection() function (lines 490-530) which prepares content for TemplatePreview
  const getTemplateContent = useCallback(() => {
    // Prepare Android content
    const androidMediaPreview = {};
    if (templateMediaType === INAPP_MEDIA_TYPES.IMAGE) {
      androidMediaPreview.inAppImageSrcAndroid = inAppImageSrcAndroid;
    }

    const androidContent = {
      mediaPreview: androidMediaPreview,
      templateTitle: titleAndroid,
      templateMsg: templateMessageAndroid,
      ...(isBtnTypeCtaAndroid && {
        ctaData: ctaDataAndroid,
      }),
      templateLayoutType,
      // Additional fields for payload transformation
      deepLinkValue: deepLinkValueAndroid,
    };

    // Prepare iOS content
    const iosMediaPreview = {};
    if (templateMediaType === INAPP_MEDIA_TYPES.IMAGE) {
      iosMediaPreview.inAppImageSrcIos = inAppImageSrcIos;
    }

    const iosContent = {
      mediaPreview: iosMediaPreview,
      templateTitle: titleIos,
      templateMsg: templateMessageIos,
      ...(isBtnTypeCTaIos && {
        ctaData: ctaDataIos,
      }),
      templateLayoutType,
      // Additional fields for payload transformation
      deepLinkValue: deepLinkValueIos,
    };

    // Return both Android and iOS content (similar to MobilePush structure)
    return {
      androidContent,
      iosContent,
      templateLayoutType, // Keep at root level for backward compatibility
      templateMediaType, // Needed for determining type (IMAGE/TEXT) in payload
    };
  }, [
    titleAndroid,
    titleIos,
    templateMessageAndroid,
    templateMessageIos,
    templateMediaType,
    inAppImageSrcAndroid,
    inAppImageSrcIos,
    ctaDataAndroid,
    ctaDataIos,
    isBtnTypeCtaAndroid,
    isBtnTypeCTaIos,
    templateLayoutType,
    deepLinkValueAndroid,
    deepLinkValueIos,
  ]);

  // Handle Test and Preview button click
  const handleTestAndPreview = useCallback(() => {
    setShowTestAndPreviewSlidebox(true);
    if (propsHandleTestAndPreview) {
      propsHandleTestAndPreview();
    }
  }, [propsHandleTestAndPreview]);

  // Handle close Test and Preview slidebox
  const handleCloseTestAndPreview = useCallback(() => {
    setShowTestAndPreviewSlidebox(false);
    if (propsHandleCloseTestAndPreview) {
      propsHandleCloseTestAndPreview();
    }
  }, [propsHandleCloseTestAndPreview]);
  const { accessibleFeatures = [] } = currentOrgDetails || {};
  const [errorMessage, setErrorMessage] = useState({
    STANDARD_ERROR_MSG: {
      ANDROID: [],
      IOS: [],
      GENERIC: [],
    },
    LIQUID_ERROR_MSG: {
      ANDROID: [],
      IOS: [],
      GENERIC: [],
    },
  });

  //gets account details
  useEffect(() => {
    const accountObj = accountData?.selectedWeChatAccount || {};
    const deepLinkObj = accountObj?.configs?.deeplink || '';
    if (!isEmpty(accountObj)) {
      const {
        sourceAccountIdentifier = "",
        configs = {},
        name = "",
      } = accountObj;
      const isAndroidSupported = configs?.android === DEVICE_SUPPORTED;
      // DEVICE_SUPPORTED is '1', which indicates if the particular account is supported, and '0' if the devive is not supported
      //get deep link keys in an array
      const deepLinkKeys = Object.values(JSON.parse(deepLinkObj || '{}'));
      const keys = deepLinkKeys?.map((link) => ({ label: link.name, value: link.link, title: link.link }));
      setPanes(isAndroidSupported ? ANDROID : IOS);
      setDeepLink(keys);
      setAccountId(sourceAccountIdentifier);
      setAccessToken(configs?.accessToken || "");
      setAccountName(name);
    }
  }, [accountData?.selectedWeChatAccount]);

  //gets template details
  const paramObj = params || {};
  useEffect(() => {
    const { id } = paramObj;
    if (id) {
      if (isFullMode) {
        setSpin(true);
        actions.getTemplateDetails(id, setSpin);
      }
    }
    //cleanup code
    return () => {
      actions.resetEditTemplate();
    };
  }, [paramObj.id]);

  // Initialize template name from defaultData (from wrapper) or editData/templateData
  useEffect(() => {
    const defaultTemplateName = defaultData?.['template-name'] || '';
    if (defaultTemplateName && !isEditFlow) {
      setTempName(defaultTemplateName);
      // Call showTemplateName callback if provided (for header display)
      if (isFullMode && showTemplateName && defaultTemplateName) {
        showTemplateName({
          formData: { 'template-name': defaultTemplateName },
          onFormDataChange: (updatedFormData) => {
            const newName = updatedFormData?.['template-name'] || '';
            setTempName(newName);
            if (showTemplateName) {
              showTemplateName({
                formData: { 'template-name': newName },
                onFormDataChange: (formData) => {
                  setTempName(formData?.['template-name'] || '');
                },
              });
            }
          },
        });
      }
    }
  }, [defaultData?.['template-name'], showTemplateName, isEditFlow]);

  useEffect(() => {
    const {
      name = "",
      versions = {},
      createdAt = "",
    } = isFullMode ? editData?.templateDetails || {} : templateData || {};
    editContent = get(versions, `base.content`, {});

    // LIBRARY MODE FIX: Backend stores bodyType as 'POPUP' but UI expects 'MODAL'
    // Convert back to MODAL for display
    if (!isFullMode && editContent) {
      if (editContent.ANDROID?.bodyType === INAPP_MESSAGE_LAYOUT_TYPES.POPUP) {
        editContent.ANDROID.bodyType = INAPP_MESSAGE_LAYOUT_TYPES.MODAL;
      }
      if (editContent.IOS?.bodyType === INAPP_MESSAGE_LAYOUT_TYPES.POPUP) {
        editContent.IOS.bodyType = INAPP_MESSAGE_LAYOUT_TYPES.MODAL;
      }
    }

    if (editContent && !isEmpty(editContent)) {
      setEditFlow(true);
      setTempName(name);
      setTemplateDate(createdAt);
      setTemplateLayoutType(editContent?.ANDROID?.bodyType);
      // Call showTemplateName callback when in edit mode + full mode to show template name header
      if (showTemplateName && name) {
        showTemplateName({
          formData: { 'template-name': name },
          onFormDataChange: (updatedFormData) => {
            const newName = updatedFormData?.['template-name'] || '';
            setTempName(newName);
            if (showTemplateName) {
              showTemplateName({
                formData: { 'template-name': newName },
                onFormDataChange: (formData) => {
                  setTempName(formData?.['template-name'] || '');
                },
              });
            }
          },
        });
      }
      const androidContent = editContent?.ANDROID;
      let androidIsHTML = false;
      if (!isEmpty(androidContent)) {
        const {
          title: androidTitle = '',
          message: androidMessage = '',
          type: androidType = '',
          ctas: androidCta = {},
          expandableDetails: androidExpandableDetails = {},
        } = androidContent || {};
        const {
          style: androidStyle,
          image: androidImage,
          ctas: androidCtas,
        } = androidExpandableDetails || {};
        const androidCtaLength = androidCtas?.length;
        // Check if this is a Bee editor template
        const isBEEeditor = get(androidContent, 'isBEEeditor');
        const isBeeFreeTemplate = !isEmpty(androidTitle) && androidTitle.toLowerCase() === 'bee free template';
        // Check if this is an HTML editor template (identified by special title)
        const isHTMLEditorTemplate = !isEmpty(androidTitle) && androidTitle.toLowerCase() === 'html editor template';
        // Check if this is an HTML template
        // Prioritize HTML editor template title, then check type/style
        // But exclude if it's a Bee editor template
        androidIsHTML = isHTMLEditorTemplate || ((androidType === INAPP_MEDIA_TYPES.HTML || androidStyle === BIG_HTML)
          && !isBEEeditor
          && !isBeeFreeTemplate);
        setIsHTMLTemplate(androidIsHTML);

        setTitleAndroid(androidTitle);
        setTemplateMessageAndroid(androidMessage);
        // Initialize HTML content for HTMLEditor if feature is enabled and it's an HTML template
        if (androidIsHTML) {
          setHtmlContentAndroid(androidMessage);
        }
        setTemplateMediaType(
          androidIsHTML ? INAPP_MEDIA_TYPES.HTML
            : androidStyle === BIG_TEXT
              ? INAPP_MEDIA_TYPES.TEXT
              : INAPP_MEDIA_TYPES.IMAGE
        );
        setInAppImageSrcAndroid(androidImage);
        setDeepLinkValueAndroid(androidCta[0]?.actionLink || '');
        setAddActionLinkAndroid(!isEmpty(androidCta) && true);
        setButtonTypeAndroid(
          androidCtaLength ? INAPP_BUTTON_TYPES.CTA : INAPP_BUTTON_TYPES.NONE
        );
        if (androidCtaLength > 0) {
          setCtaDataAndroid(getCtaObject(androidCtas));
        }
      }
      const iosContent = editContent?.IOS;
      if (!isEmpty(iosContent)) {
        const {
          title: iosTitle = '',
          message: iosMessage = '',
          type: iosType = '',
          ctas: iosCta = {},
          expandableDetails: iosExpandableDetails = {},
        } = iosContent || {};
        const {
          style: iosStyle,
          image: iosImage,
          ctas: iosCtas,
        } = iosExpandableDetails || {};
        const iosCtaLength = iosCtas?.length;

        // Check if this is an HTML template (if Android wasn't HTML, check iOS)
        // Note: androidIsHTML is in the outer scope from the Android content check above
        if (!androidIsHTML) {
          // Check if this is a Bee editor template
          const isBEEeditor = get(iosContent, 'isBEEeditor');
          const isBeeFreeTemplate = !isEmpty(iosTitle) && iosTitle.toLowerCase() === 'bee free template';
          // Check if this is an HTML editor template (identified by special title)
          const isHTMLEditorTemplate = !isEmpty(iosTitle) && iosTitle.toLowerCase() === 'html editor template';
          // Check if this is an HTML template
          // Prioritize HTML editor template title, then check type/style
          // But exclude if it's a Bee editor template
          const iosIsHTML = isHTMLEditorTemplate || ((iosType === INAPP_MEDIA_TYPES.HTML || iosStyle === BIG_HTML)
            && !isBEEeditor
            && !isBeeFreeTemplate);
          setIsHTMLTemplate(iosIsHTML);
          // Initialize HTML content for HTMLEditor if feature is enabled and it's an HTML template
          if (iosIsHTML) {
            setHtmlContentIos(iosMessage);
          }
        } else {
          // If Android is HTML, also initialize iOS HTML content if available
          if (androidIsHTML) {
            setHtmlContentIos(iosMessage);
          }
        }

        setTitleIos(iosTitle);
        setTemplateMessageIos(iosMessage);
        // Update templateMediaType if iOS is HTML and Android wasn't
        if (!androidIsHTML) {
          // Check if this is a Bee editor template
          const isBEEeditor = get(iosContent, 'isBEEeditor');
          const isBeeFreeTemplate = !isEmpty(iosTitle) && iosTitle.toLowerCase() === 'bee free template';
          // Check if this is an HTML editor template (identified by special title)
          const isHTMLEditorTemplate = !isEmpty(iosTitle) && iosTitle.toLowerCase() === 'html editor template';
          // Check if this is an HTML template
          // Prioritize HTML editor template title, then check type/style
          const iosIsHTML = isHTMLEditorTemplate || ((iosType === INAPP_MEDIA_TYPES.HTML || iosStyle === BIG_HTML)
            && !isBEEeditor
            && !isBeeFreeTemplate);
          if (iosIsHTML) {
            setTemplateMediaType(INAPP_MEDIA_TYPES.HTML);
          } else {
            setTemplateMediaType(iosStyle === BIG_TEXT ? INAPP_MEDIA_TYPES.TEXT : INAPP_MEDIA_TYPES.IMAGE);
          }
        }
        setInAppImageSrcIos(iosImage);
        setButtonTypeIos(iosCtaLength ? INAPP_BUTTON_TYPES.CTA : INAPP_BUTTON_TYPES.NONE);
        setAddActionLinkIos(!isEmpty(iosCta) && true);
        setDeepLinkValueIos(iosCta[0]?.actionLink || '');
        if (iosCtaLength > 0) {
          setCtaDataIos(getCtaObject(iosCtas));
        }
      }
    } else {
      // Explicitly set edit flow to false if there's no edit content
      setEditFlow(false);
    }
  }, [editData.templateDetails || templateData]);

  // Extract editor type from defaultData for stable reference
  const editorType = useMemo(() => {
    const type = defaultData?.['editor-type'];
    return type;
  }, [defaultData]);

  // Separate effect for handling editor type from wrapper in create mode
  useEffect(() => {
    // Only process editor type if we're not in edit flow
    if (!isEditFlow) {
      if (editorType === INAPP_EDITOR_TYPES.HTML_EDITOR) {
        setIsHTMLTemplate(true);
        setTemplateMediaType(INAPP_MEDIA_TYPES.HTML);
      } else if (editorType === INAPP_EDITOR_TYPES.DRAG_DROP_EDITOR) {
        setIsHTMLTemplate(false);
      } else if (!editorType) {
        // If no editor type is set yet, ensure we start with false
        setIsHTMLTemplate(false);
      }
    }
  }, [editorType, isEditFlow]);

  // tag Code start from here
  useEffect(() => {
    // Reset tags fetch ref when switching between HTML and legacy editors
    tagsFetchedRef.current = false;

    // Only fetch tags if HTML Editor is not being used (legacy flow)
    // For HTML Editor, tags will be fetched via handleOnTagsContextChange
    if (isHTMLTemplate) {
      // HTML Editor will handle tag fetching via onContextChange
      return;
    }

    //fetching tags for legacy editor
    const { type, module } = location.query || {};
    const isEmbedded = type === EMBEDDED;
    const context = isEmbedded ? module : DEFAULT;
    const embedded = isEmbedded ? type : FULL;
    const query = {
      layout: SMS,
      type: TAG,
      context,
      embedded,
    };
    if (getDefaultTags) {
      query.context = getDefaultTags;
    }
    globalActions.fetchSchemaForEntity(query);
  }, [isHTMLTemplate]);

  useEffect(() => {
    let tag = get(metaEntities, `tags.standard`, []);
    const { type, module } = location.query || {};
    if (type === EMBEDDED && module === LIBRARY && !getDefaultTags) {
      tag = supportedTags;
    }
    updateTags(tag);
  }, [metaEntities]);

  // Track if we've already fetched tags to prevent duplicate calls
  const tagsFetchedRef = React.useRef(false);

  const handleOnTagsContextChange = useCallback((data) => {
    // This function is called when TagList needs to fetch tags
    // It triggers the API call to /meta/TAG endpoint via fetchSchemaForEntity
    // Only fetch if we haven't already fetched for this context
    if (tagsFetchedRef.current) {
      return;
    }

    const { type } = location.query || {};
    const tempData = (data || '').toLowerCase();
    const isEmbedded = type === EMBEDDED;
    const embedded = isEmbedded ? type : FULL;
    const context = tempData === ALL ? DEFAULT : tempData;
    const query = {
      layout: SMS,
      type: TAG,
      context,
      embedded,
    };
    // Mark as fetched to prevent duplicate calls
    tagsFetchedRef.current = true;
    // Call the API via Redux action - this will trigger the saga which calls Api.fetchSchemaForEntity
    // The API endpoint will be: /meta/TAG?query={...}
    globalActions.fetchSchemaForEntity(query);
  }, [location.query, globalActions]);


  // tag Code end

  const onTemplateLayoutTypeChange = ({ target: { value } }) => {
    // Update layout type and force HTMLEditor to remount with latest content
    // Increment version to force remount - this ensures editor displays current content
    setTemplateLayoutType(value);
    setHtmlEditorContentVersion((prev) => prev + 1);
  };


  //create methods end

  //used by create and edit

  const isDisableDone = (device) => {
    const isIosDevice = device === IOS || device === IOS_CAPITAL;
    const isAndroidDevice = device === ANDROID;
    const isNoDevice = device === null || device === undefined;

    // Check for validation errors first - if there are errors, disable the button
    if (hasAnyErrors(errorMessage)) {
      return true;
    }

    // For HTMLEditor: only validate HTML content (when it's an HTML template)
    if (isHTMLTemplate) {
      // Get account-level device support restrictions
      const androidSupported = get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED || get(editContent, 'ANDROID.deviceType') === ANDROID;
      const iosSupported = get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED || get(editContent, 'IOS.deviceType') === IOS_CAPITAL;

      // Check if devices have content - ensure content is a string before calling trim
      let hasAndroidContent = htmlContentAndroid && typeof htmlContentAndroid === 'string' && htmlContentAndroid?.trim() !== '';
      let hasIosContent = htmlContentIos && typeof htmlContentIos === 'string' && htmlContentIos?.trim() !== '';

      // LIBRARY MODE FIX: In library mode edit, htmlContent states might not be set yet
      // because HTMLEditor loads content via initialContent prop, not via onContentChange
      // Fallback to checking template data content
      if (!hasAndroidContent && isEditFlow && !isFullMode && templateData) {
        const androidTemplateContent = get(templateData, 'versions.base.content.ANDROID.message')
          || get(templateData, 'versions.base.content.ANDROID.beeHtml.value');
        hasAndroidContent = androidTemplateContent && androidTemplateContent.trim() !== '';
      }
      if (!hasIosContent && isEditFlow && !isFullMode && templateData) {
        const iosTemplateContent = get(templateData, 'versions.base.content.IOS.message')
          || get(templateData, 'versions.base.content.IOS.beeHtml.value');
        hasIosContent = iosTemplateContent && iosTemplateContent.trim() !== '';
      }

      // If checking specific device, validate that device's content
      if (isAndroidDevice) {
        // Only validate Android if it's supported by account
        // But in library mode edit, check template data too
        if (androidSupported || (isEditFlow && !isFullMode && hasAndroidContent)) {
          if (!hasAndroidContent) {
            return true;
          }
          return false;
        }
        // Android not supported and no content in template - skip validation
        return false;
      }
      if (isIosDevice) {
        // Only validate iOS if it's supported by account
        // But in library mode edit, check template data too
        if (iosSupported || (isEditFlow && !isFullMode && hasIosContent)) {
          if (!hasIosContent) {
            return true;
          }
          return false;
        }
        // iOS not supported and no content in template - skip validation
        return false;
      }

      // If no specific device, check if at least one supported device has content
      // Users can create templates with content in Android-only, iOS-only, or both devices
      // Even when both devices are supported, user can create template with content in just one device
      if (androidSupported && iosSupported) {
        // Both devices supported - user can create template with content in Android, iOS, or both
        // Only disable if NEITHER device has content
        if (!hasAndroidContent && !hasIosContent) {
          return true;
        }
        // At least one device has content - enable Done button
        return false;
      }
      if (androidSupported) {
        // Only Android supported - require Android content
        if (!hasAndroidContent) {
          return true;
        }
        // Android has content - enable Done button
        return false;
      }
      if (iosSupported) {
        // Only iOS supported - require iOS content
        if (!hasIosContent) {
          return true;
        }
        // iOS has content - enable Done button
        return false;
      }
      // Neither device supported - this shouldn't happen, but handle gracefully
      return true;
    }

    // Legacy flow validation (when HTMLEditor is not enabled)
    // Get account-level device support
    const androidSupported = get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED || get(editContent, 'ANDROID.deviceType') === ANDROID;
    const iosSupported = get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED || get(editContent, 'IOS.deviceType') === IOS_CAPITAL;

    // If no tags are available (e.g., in tests), allow submission even with minimal content
    // This is to support test scenarios where full content validation might not be set up
    const hasTags = tags && tags.length > 0;
    const isTestScenario = !hasTags;

    // If no device specified, check if at least one supported device has valid content
    if (isNoDevice) {
      // In test scenarios, only require message content (title is optional)
      // In production, require both title and message
      const hasAndroidContent = androidSupported && templateMessageAndroid && templateMessageAndroid?.trim() !== '' && !templateMessageErrorAndroid && (isTestScenario || (titleAndroid && titleAndroid?.trim() !== '' && !templateTitleErrorAndroid));
      const hasIosContent = iosSupported && templateMessageIos && templateMessageIos?.trim() !== '' && !templateMessageErrorIos && (isTestScenario || (titleIos && titleIos?.trim() !== '' && !templateTitleErrorIos));

      // Check media requirements
      const androidMediaValid = !androidSupported || (templateMediaType === INAPP_MEDIA_TYPES.TEXT || inAppImageSrcAndroid !== '');
      const iosMediaValid = !iosSupported || (templateMediaType === INAPP_MEDIA_TYPES.TEXT || inAppImageSrcIos !== '');

      // Check CTA requirements
      const androidCtaValid = !isBtnTypeCtaAndroid || (ctaDataAndroid[0]?.isSaved);
      const iosCtaValid = !isBtnTypeCTaIos || (ctaDataIos[0]?.isSaved);

      // Check action link requirements
      const androidActionLinkValid = !addActionLinkAndroid || deepLinkValueAndroid;
      const iosActionLinkValid = !addActionLinkIos || deepLinkValueIos;

      // If both devices are supported, at least one should have valid content
      if (androidSupported && iosSupported) {
        const androidValid = hasAndroidContent && androidMediaValid && androidCtaValid && androidActionLinkValid;
        const iosValid = hasIosContent && iosMediaValid && iosCtaValid && iosActionLinkValid;
        return !(androidValid || iosValid);
      }

      // If only Android is supported, it must have valid content
      if (androidSupported) {
        return !(hasAndroidContent && androidMediaValid && androidCtaValid && androidActionLinkValid);
      }

      // If only iOS is supported, it must have valid content
      if (iosSupported) {
        return !(hasIosContent && iosMediaValid && iosCtaValid && iosActionLinkValid);
      }

      // Neither device supported - disable
      return true;
    }

    //if template message is not entered
    //for android
    if (isAndroidDevice) {
      const androidMessage = templateMessageAndroid;
      if (androidMessage?.trim() === '' || templateMessageErrorAndroid) {
        return true;
      }
    }
    //for ios
    if (isIosDevice) {
      const iosMessage = templateMessageIos;
      if (iosMessage?.trim() === '' || templateMessageErrorIos) {
        return true;
      }
    }

    //if template title is not entered
    //for android
    if (isAndroidDevice && (titleAndroid?.trim() === '' || templateTitleErrorAndroid)) {
      return true;
    }
    if (isIosDevice && (titleIos?.trim() === '' || templateTitleErrorIos)) {
      return true;
    }//for ios
    //if media type is image and the mediaType file is not uploaded
    if (isAndroidDevice && !(templateMediaType === INAPP_MEDIA_TYPES.TEXT) && inAppImageSrcAndroid === '') {
      return true;
    }
    if (isIosDevice && !(templateMediaType === INAPP_MEDIA_TYPES.TEXT) && inAppImageSrcIos === '') {
      return true;
    }
    //cta
    if (isBtnTypeCtaAndroid) {
      if (ctaDataAndroid[0]?.isSaved) {
        //if button type is cta and 1st button is saved
        return false;
      }
      //if button type is cta and there are no buttons saved
      return true;
    }
    if (isBtnTypeCTaIos) {
      if (ctaDataIos[0]?.isSaved) {
        //if button type is cta and 1st button is saved
        return false;
      }
      //if button type is cta and there are no buttons saved
      return true;
    }

    // action link
    if (addActionLinkAndroid && !deepLinkValueAndroid) {
      return true;
    }
    if (addActionLinkIos && !deepLinkValueIos) {
      return true;
    }

    return false;
  };

  const getCtaPayload = (type) => (type === ANDROID ? ctaDataAndroid : ctaDataIos).map((cta) => {
    const { text, urlType, url } = cta;
    return {
      type: urlType,
      actionText: text,
      actionLink: url,
    };
  });

  const createPayload = () => {
    let androidMediaParams = {};
    let iosMediaParams = {};
    const commonDevicePayload = {
      luid: "{{luid}}",
      cuid: "{{cuid}}",
      communicationId: "{{communicationId}}",
    };
    switch (templateMediaType) {
      case INAPP_MEDIA_TYPES.IMAGE:
        androidMediaParams = {
          image: getCdnUrl({ url: inAppImageSrcAndroid, channelName: INAPP }),
          style: BIG_PICTURE,
        };
        iosMediaParams = {
          image: getCdnUrl({ url: inAppImageSrcIos, channelName: INAPP }),
          style: BIG_PICTURE,
        };
        break;
      default:
        break;
    }
    const accountObj = accountData?.selectedWeChatAccount || {};
    const {
      sourceAccountIdentifier = "",
      id,
    } = accountObj;

    // Use HTML content if HTMLEditor is enabled and it's an HTML template, otherwise use regular message
    const androidMessage = isHTMLTemplate && htmlContentAndroid
      ? htmlContentAndroid
      : templateMessageAndroid;

    // Determine type and style for Android
    const androidType = isHTMLTemplate ? INAPP_MEDIA_TYPES.HTML : templateMediaType;
    const androidExpandableStyle = isHTMLTemplate ? BIG_HTML : BIG_TEXT;

    // LIBRARY MODE FIX: Backend doesn't support 'MODAL' bodyType, convert to 'POPUP'
    const bodyTypeForBackend = templateLayoutType === INAPP_MESSAGE_LAYOUT_TYPES.MODAL ? INAPP_MESSAGE_LAYOUT_TYPES.POPUP : templateLayoutType;

    // Check account-level device support
    const androidSupported = get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED || get(editContent, 'ANDROID.deviceType') === ANDROID;
    const iosSupported = get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED || get(editContent, 'IOS.deviceType') === IOS_CAPITAL;

    // Check if devices have content (for HTML Editor)
    const hasAndroidContent = htmlContentAndroid && htmlContentAndroid?.trim() !== '';
    const hasIosContent = htmlContentIos && htmlContentIos?.trim() !== '';

    // For HTML Editor, check if device has content and is supported
    // For legacy editor, use isDisableDone check
    // Only include devices that have content - allows Android-only, iOS-only, or both
    const shouldIncludeAndroid = isHTMLTemplate
      ? (androidSupported && hasAndroidContent)
      : !isDisableDone(ANDROID);

    // Construct Android content if device is supported and has content
    // Even when both devices are supported, only include devices that have content
    const androidContent = shouldIncludeAndroid ? {
      ...commonDevicePayload,
      type: androidType,
      // Use 'html editor template' as title for HTML editor to differentiate from BEE editor
      title: isHTMLTemplate ? 'html editor template' : titleAndroid,
      message: androidMessage,
      bodyType: bodyTypeForBackend,
      expandableDetails: {
        style: androidExpandableStyle,
        message: androidMessage,
        ...androidMediaParams,
        ...(isBtnTypeCtaAndroid && {
          ctas: getCtaPayload(ANDROID),
        }),
      },
      custom: [],
      ...(deepLinkValueAndroid && {
        ctas: [{ type: DEEP_LINK, actionLink: deepLinkValueAndroid }],
      }),
    } : {};

    // Use HTML content if HTMLEditor is enabled and it's an HTML template, otherwise use regular message
    const iosMessage = isHTMLTemplate && htmlContentIos
      ? htmlContentIos
      : templateMessageIos;

    // Determine type and style for iOS
    const iosType = isHTMLTemplate ? INAPP_MEDIA_TYPES.HTML : templateMediaType;
    const iosExpandableStyle = isHTMLTemplate ? BIG_HTML : BIG_TEXT;

    // For HTML Editor, check if device has content and is supported
    // For legacy editor, use isDisableDone check
    // Only include devices that have content - allows Android-only, iOS-only, or both
    const shouldIncludeIos = isHTMLTemplate
      ? (iosSupported && hasIosContent)
      : !isDisableDone(IOS);

    // Construct iOS content if device is supported and has content
    // Even when both devices are supported, only include devices that have content
    const iosContent = shouldIncludeIos ? {
      ...commonDevicePayload,
      type: iosType,
      // Use 'html editor template' as title for HTML editor to differentiate from BEE editor
      title: isHTMLTemplate ? 'html editor template' : titleIos,
      message: iosMessage,
      bodyType: bodyTypeForBackend,
      expandableDetails: {
        style: iosExpandableStyle,
        message: iosMessage,
        ...iosMediaParams,
        ...(isBtnTypeCTaIos && {
          ctas: getCtaPayload(IOS),
        }),
      },
      custom: [],
      ...(deepLinkValueIos && {
        ctas: [{ type: DEEP_LINK, actionLink: deepLinkValueIos }],
      }),
    } : {};

    // Ensure name is always set - use tempName as fallback if templateName is empty
    const templateNameValue = isEditFlow ? tempName : (templateName || tempName || '');
    const trimmedName = (templateNameValue && typeof templateNameValue === 'string') ? templateNameValue?.trim() : '';

    const data = {
      name: trimmedName,
      versions: {
        base: {
          content: {
            ANDROID: androidContent,
            IOS: iosContent,
          },
        },
      },
      type: INAPP,
      definition: {
        accountId: id,
        licenseCode: sourceAccountIdentifier,
        mode: templateMediaType,
      },
    };
    return data;
  };

  const actionCallback = ({ errorMsg }) => {
    if (!errorMsg) {
      CapNotification.success({
        message: isEditFlow
          ? formatMessage(messages.inAppEditNotification)
          : formatMessage(messages.inAppCreateNotification),
      });
      actions.clearCreateResponse();
    } else {
      CapNotification.error({
        message: JSON.stringify(errorMsg),
      });
    }
  };

  const onCreateInApp = () => {
    setSpin(true);
    actions.createInAppTemplate(createPayload(), (resp, errorMsg) => {
      actionCallback({ resp, errorMsg });
      if (!errorMsg) {
        onCreateComplete();
      } else {
        setSpin(false);
      }
    });
  };

  const onEditInApp = () => {
    setSpin(true);
    actions.editTemplate(
      {
        ...createPayload(),
        _id: params.id,
      },
      (resp, errorMsg) => {
        actionCallback({ resp, errorMsg });
        if (!errorMsg) {
          onCreateComplete();
        } else {
          setSpin(false);
        }
      },
    );
  };

  // Handle HTML content changes from HTMLEditor
  const handleHtmlContentChange = useCallback((deviceContent, changedDevice) => {
    // The onChange callback from useInAppContent passes the full deviceContent object
    // But we only want to update the state for the device that actually changed
    // Use the second parameter (changedDevice) if provided, otherwise update both

    // Clear validation errors when content changes (similar to Bee Editor)
    // This ensures Done button re-enables after user fixes errors
    if (changedDevice) {
      setErrorMessage((prev) => ({
        STANDARD_ERROR_MSG: {
          ...prev.STANDARD_ERROR_MSG,
          [changedDevice.toUpperCase()]: [],
          GENERIC: [],
        },
        LIQUID_ERROR_MSG: {
          ...prev.LIQUID_ERROR_MSG,
          [changedDevice.toUpperCase()]: [],
          GENERIC: [],
        },
      }));
    }

    if (changedDevice) {
      // Only update the device that actually changed
      if (changedDevice.toUpperCase() === ANDROID && deviceContent?.android !== undefined) {
        setHtmlContentAndroid(deviceContent.android || '');
      } else if (changedDevice.toUpperCase() === IOS_CAPITAL && deviceContent?.ios !== undefined) {
        setHtmlContentIos(deviceContent.ios || '');
      }
    } else {
      // Fallback: update both if changedDevice not provided (for backward compatibility)
      // Only update if value actually changed to avoid unnecessary re-renders
      if (deviceContent?.android !== undefined) {
        setHtmlContentAndroid((prev) => {
          const newValue = deviceContent.android || '';
          return prev !== newValue ? newValue : prev;
        });
      }
      if (deviceContent?.ios !== undefined) {
        setHtmlContentIos((prev) => {
          const newValue = deviceContent.ios || '';
          return prev !== newValue ? newValue : prev;
        });
      }
    }
  }, [ANDROID, IOS, setErrorMessage]);

  // Handle HTML save from HTMLEditor
  const handleHtmlSave = useCallback((deviceContent) => {
    // Update state for both devices if present in the callback
    if (deviceContent?.android !== undefined) {
      setHtmlContentAndroid(deviceContent.android || '');
    }
    if (deviceContent?.ios !== undefined) {
      setHtmlContentIos(deviceContent.ios || '');
    }
    // Auto-save can trigger this, but we don't want to submit automatically
    // The user will click the Create/Update button to submit
  }, []);

  const onDoneCallback = () => {
    if (isFullMode) {
      if (isEditFlow) {
        return onEditInApp();
      }
      return onCreateInApp();
    }
    const payload = createPayload();
    getFormData({
      value: payload,
      _id: params && params.id,
      validity: true,
      type: INAPP,
    });
  };

  // Validation middleware for tag validation (both liquid and non-liquid flow)
  // Liquid validation (extractTags) runs only in library mode
  const validationMiddleWare = async () => {
    // Normalize validator bucket keys to component state keys (ANDROID, IOS_CAPITAL, GENERIC)
    // so we don't merge e.g. 'android'/'ios'/'generic' with ANDROID/IOS/GENERIC and get duplicate/stale keys
    const normalizeErrorBuckets = (errors) => {
      const normalized = {
        [ANDROID]: [],
        [IOS_CAPITAL]: [],
        [GENERIC]: [],
      };
      if (!errors || typeof errors !== 'object') return normalized;
      const keyMap = {
        ANDROID,
        android: ANDROID,
        [IOS_CAPITAL]: IOS_CAPITAL,
        [IOS]: IOS_CAPITAL,
        ios: IOS_CAPITAL,
        GENERIC,
        generic: GENERIC,
      };
      for (const [key, value] of Object.entries(errors)) {
        const targetKey = keyMap[key];
        if (targetKey != null && Array.isArray(value)) {
          normalized[targetKey] = value;
        }
      }
      return normalized;
    };
    const onError = ({ standardErrors, liquidErrors }) => {
      setErrorMessage((prev) => ({
        STANDARD_ERROR_MSG: { ...prev.STANDARD_ERROR_MSG, ...normalizeErrorBuckets(standardErrors) },
        LIQUID_ERROR_MSG: { ...prev.LIQUID_ERROR_MSG, ...normalizeErrorBuckets(liquidErrors) },
      }));
    };
    const onSuccess = () => {
      onDoneCallback();
    };

    // Library mode: run extractTags validation (always, so we catch liquid errors even when tags not loaded)
    if (!isFullMode) {
      const payload = createPayload();
      validateInAppContent(payload, {
        currentTab: panes === ANDROID ? 1 : 2,
        onError,
        onSuccess,
        getLiquidTags: (content, callback) => globalActions.getLiquidTags(content, callback),
        formatMessage,
        messages: formBuilderMessages,
        skipTags: (tag) => {
          const skipRegexes = [
            /dynamic_expiry_date_after_\d+_days\.FORMAT_\d/,
            /unsubscribe\(#[a-zA-Z\d]{6}\)/,
            /Link_to_[a-zA-z]/,
            /SURVEY.*\.TOKEN/,
            /^[A-Za-z].*\([a-zA-Z\d]*\)/,
          ];
          return skipRegexes.some((regex) => regex.test(tag));
        },
        singleTab: getSingleTab(accountData),
      });
    } else {
      onSuccess();
    }
  };

  // Check template data to determine editor type (for render decision)
  const templateDetails = isFullMode ? editData?.templateDetails : templateData;
  const versions = templateDetails?.versions || {};
  const templateContent = get(versions, 'base.content', {});
  const androidContent = templateContent?.ANDROID || {};
  const iosContent = templateContent?.IOS || {};

  // Check if this is a Bee editor template
  const isBEEeditor = get(androidContent, 'isBEEeditor') || get(iosContent, 'isBEEeditor');
  const androidTitle = androidContent?.title || '';
  const iosTitle = iosContent?.title || '';
  const isBeeFreeTemplate = (!isEmpty(androidTitle) && androidTitle.toLowerCase() === 'bee free template')
    || (!isEmpty(iosTitle) && iosTitle.toLowerCase() === 'bee free template');

  // Check if this is an HTML template from content data
  const androidType = androidContent?.type || '';
  const androidStyle = androidContent?.expandableDetails?.style || '';
  const iosType = iosContent?.type || '';
  const iosStyle = iosContent?.expandableDetails?.style || '';
  const isHTMLTemplateFromData = (androidType === INAPP_MEDIA_TYPES.HTML || androidStyle === BIG_HTML
    || iosType === INAPP_MEDIA_TYPES.HTML || iosStyle === BIG_HTML)
    && !isBEEeditor
    && !isBeeFreeTemplate;

  const isNewEditorFlowEnabled = !isLoyaltyModule && hasNewEditorFlowInAppEnabled();

  // Use state if available, otherwise fall back to direct data check
  const shouldUseHTMLEditor = isNewEditorFlowEnabled && (isHTMLTemplate || isHTMLTemplateFromData);

  // Only route to Bee editor if it's explicitly a Bee editor AND not an HTML template
  const shouldUseBeeEditor = isNewEditorFlowEnabled && (isBEEeditor || isBeeFreeTemplate) && !shouldUseHTMLEditor;

  // Early returns to avoid nested ternary
  if (isEditInApp && getTemplateDetailsInProgress) {
    return <CapSpin spinning={getTemplateDetailsInProgress} />;
  }

  // Allow BEE editor in both full mode edit (isEditInApp) AND library mode edit (isEditFlow && !isFullMode)
  if ((isEditInApp || (isEditFlow && !isFullMode)) && shouldUseBeeEditor) {
    return (
      <InappAdvanced
        getFormData={getFormData}
        setIsLoadingContent={setIsLoadingContent}
        defaultData={{ "template-name": tempName }}
        location={{
          pathname: `/inapp/edit`,
          query,
          search: '',
        }}
        params={{ mode: inAppCreateMode, id: params.id }}
        isFullMode={isFullMode}
        isGetFormData={isGetFormData}
        showTemplateName={showTemplateName}
        route={{ name: "inapp" }}
        getDefaultTags={type}
        onValidationFail={onValidationFail}
        templateData={templateData}
        templateName={tempName}
        setTemplateName={setTempName}
        forwardedTags={forwardedTags}
        selectedOfferDetails={selectedOfferDetails}
        onCreateComplete={onCreateComplete}
      />
    );
  }

  // ── Old-flow helpers (used by CapDeviceContent when flag is disabled) ──────
  const aiContentBotDisabled = isAiContentBotDisabled();

  /**
   * Clears stored API/liquid errors for a specific device (and GENERIC) so
   * hasAnyErrors() goes false and Done can be re-clicked after editing.
   * Does NOT clear errors that belong only to the *other* device.
   */
  const clearDeviceErrors = useCallback((platform) => {
    const key = platform === IOS ? IOS_CAPITAL : ANDROID;
    setErrorMessage((prev) => ({
      STANDARD_ERROR_MSG: {
        ...prev.STANDARD_ERROR_MSG,
        [key]: [],
        GENERIC: [],
      },
      LIQUID_ERROR_MSG: {
        ...prev.LIQUID_ERROR_MSG,
        [key]: [],
        GENERIC: [],
      },
    }));
  }, [IOS_CAPITAL, ANDROID]);

  const setTitleAndroidWithClear = useCallback((value) => {
    clearDeviceErrors(ANDROID);
    setTitleAndroid(value);
  }, [clearDeviceErrors]);

  const setTitleIosWithClear = useCallback((value) => {
    clearDeviceErrors(IOS);
    setTitleIos(value);
  }, [clearDeviceErrors]);

  const setTemplateMessageAndroidWithClear = useCallback((value) => {
    clearDeviceErrors(ANDROID);
    setTemplateMessageAndroid(value);
  }, [clearDeviceErrors]);

  const setTemplateMessageIosWithClear = useCallback((value) => {
    clearDeviceErrors(IOS);
    setTemplateMessageIos(value);
  }, [clearDeviceErrors]);

  const templateDescErrorHandler = (value) => {
    const { unsupportedTags, isBraceError } = validateTags({
      content: value,
      tagsParam: tags,
      injectedTagsParams: injectedTags,
      location,
      tagModule: getDefaultTags,
      isFullMode,
    }) || {};
    if (unsupportedTags?.length > 0) {
      return formatMessage(globalMessages.unsupportedTagsValidationError, { unsupportedTags });
    }
    if (isBraceError) {
      return formatMessage(globalMessages.unbalanacedCurlyBraces);
    }
    return '';
  };

  /** Same rules as CapDeviceContent onTitleChange / onTemplateMessageChange */
  const computeInAppTemplateFieldError = (value) => {
    let error = templateDescErrorHandler(value);
    if (value === '') {
      error = formatMessage(capDeviceContentMessages.emptyTemplateMessageErrorMessage);
    }
    return error;
  };

  const onCopyTitleAndContent = () => {
    if (panes === ANDROID) {
      clearDeviceErrors(ANDROID);
      setTitleAndroid(titleIos);
      setTemplateMessageAndroid(templateMessageIos);
      setTemplateTitleErrorAndroid(computeInAppTemplateFieldError(titleIos));
      setTemplateMessageErrorAndroid(computeInAppTemplateFieldError(templateMessageIos));
      setInAppImageSrcAndroid(inAppImageSrcIos);
    } else {
      clearDeviceErrors(IOS);
      setTitleIos(titleAndroid);
      setTemplateMessageIos(templateMessageAndroid);
      setTemplateTitleErrorIos(computeInAppTemplateFieldError(titleAndroid));
      setTemplateMessageErrorIos(computeInAppTemplateFieldError(templateMessageAndroid));
      setInAppImageSrcIos(inAppImageSrcAndroid);
    }
  };

  const onTagSelect = (value, index) => {
    const tag = `{{${value}}}`;
    if (panes === ANDROID) {
      clearDeviceErrors(ANDROID);
      const nextTitle = index === 0 ? titleAndroid + tag : titleAndroid;
      const nextMessage = index === 0 ? templateMessageAndroid : templateMessageAndroid + tag;
      if (index === 0) setTitleAndroid(nextTitle);
      else setTemplateMessageAndroid(nextMessage);
      setTemplateTitleErrorAndroid(computeInAppTemplateFieldError(nextTitle));
      setTemplateMessageErrorAndroid(computeInAppTemplateFieldError(nextMessage));
    } else {
      clearDeviceErrors(IOS);
      const nextTitle = index === 0 ? titleIos + tag : titleIos;
      const nextMessage = index === 0 ? templateMessageIos : templateMessageIos + tag;
      if (index === 0) setTitleIos(nextTitle);
      else setTemplateMessageIos(nextMessage);
      setTemplateTitleErrorIos(computeInAppTemplateFieldError(nextTitle));
      setTemplateMessageErrorIos(computeInAppTemplateFieldError(nextMessage));
    }
  };

  // Device support flags (same derivation as InappAdvance)
  const isAndroidSupported = get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED || get(editContent, 'ANDROID.deviceType') === ANDROID;
  const isIosSupported = get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED || get(editContent, 'IOS.deviceType') === IOS_CAPITAL;

  // CapDeviceContent tab panes (old flow)
  const DEVICE_PANES = [
    {
      content: (
        <CapDeviceContent
          panes={ANDROID}
          actions={actions}
          editData={editData}
          isFullMode={isFullMode}
          inAppImageSrc={inAppImageSrcAndroid}
          setInAppImageSrc={setInAppImageSrcAndroid}
          isEditFlow={isEditFlow}
          ctaData={ctaDataAndroid}
          setCtaData={setCtaDataAndroid}
          buttonType={buttonTypeAndroid}
          setButtonType={setButtonTypeAndroid}
          templateMediaType={templateMediaType}
          setTemplateMediaType={setTemplateMediaType}
          title={titleAndroid}
          setTitle={setTitleAndroidWithClear}
          templateMessageError={templateMessageErrorAndroid}
          templateMessage={templateMessageAndroid}
          setTemplateMessage={setTemplateMessageAndroidWithClear}
          setTemplateMessageError={setTemplateMessageErrorAndroid}
          addActionLink={addActionLinkAndroid}
          setAddActionLink={setAddActionLinkAndroid}
          deepLink={deepLink}
          deepLinkValue={deepLinkValueAndroid}
          setDeepLinkValue={setDeepLinkValueAndroid}
          onCopyTitleAndContent={onCopyTitleAndContent}
          isOtherDeviceSupported={isIosSupported}
          tags={tags}
          onTagSelect={onTagSelect}
          handleOnTagsContextChange={handleOnTagsContextChange}
          templateDescErrorHandler={templateDescErrorHandler}
          templateTitleError={templateTitleErrorAndroid}
          setTemplateTitleError={setTemplateTitleErrorAndroid}
          isAiContentBotDisabled={aiContentBotDisabled}
          injectedTags={injectedTags}
          selectedOfferDetails={selectedOfferDetails}
          location={location}
        />
      ),
      tab: <FormattedMessage {...messages.Android} />,
      key: ANDROID,
      isSupported: isAndroidSupported,
    },
    {
      content: (
        <CapDeviceContent
          panes={IOS}
          actions={actions}
          editData={editData}
          isFullMode={isFullMode}
          inAppImageSrc={inAppImageSrcIos}
          setInAppImageSrc={setInAppImageSrcIos}
          isEditFlow={isEditFlow}
          ctaData={ctaDataIos}
          setCtaData={setCtaDataIos}
          buttonType={buttonTypeIos}
          setButtonType={setButtonTypeIos}
          templateMediaType={templateMediaType}
          setTemplateMediaType={setTemplateMediaType}
          title={titleIos}
          setTitle={setTitleIosWithClear}
          templateMessageError={templateMessageErrorIos}
          templateMessage={templateMessageIos}
          setTemplateMessage={setTemplateMessageIosWithClear}
          setTemplateMessageError={setTemplateMessageErrorIos}
          addActionLink={addActionLinkIos}
          setAddActionLink={setAddActionLinkIos}
          deepLink={deepLink}
          deepLinkValue={deepLinkValueIos}
          setDeepLinkValue={setDeepLinkValueIos}
          onCopyTitleAndContent={onCopyTitleAndContent}
          isOtherDeviceSupported={isAndroidSupported}
          tags={tags}
          onTagSelect={onTagSelect}
          handleOnTagsContextChange={handleOnTagsContextChange}
          templateDescErrorHandler={templateDescErrorHandler}
          templateTitleError={templateTitleErrorIos}
          setTemplateTitleError={setTemplateTitleErrorIos}
          isAiContentBotDisabled={aiContentBotDisabled}
          injectedTags={injectedTags}
          selectedOfferDetails={selectedOfferDetails}
          location={location}
        />
      ),
      tab: <FormattedMessage {...messages.Ios} />,
      key: IOS,
      isSupported: isIosSupported,
    },
  ];
  // ─────────────────────────────────────────────────────────────────────────

  // Calculate column span: HTML editor = 18, everything else = 24
  const editorColumnSpan = shouldUseHTMLEditor ? 18 : 24;

  return (
    <CapSpin spinning={spin || fetchingLiquidValidation} tip={fetchingLiquidValidation ? <FormattedMessage {...formBuilderMessages.liquidSpinText} /> : ""}>
      <CapRow className="cap-inapp-creatives">
        <CapColumn span={editorColumnSpan}>
          {/* Creative layout type */}
          {shouldUseHTMLEditor && (
            <>
              <CapRow>
                <CapHeading type="h4">
                  <FormattedMessage {...messages.creativeLayout} />
                </CapHeading>
                <CapHeading type="h6" className="inapp-creative-layout-desc">
                  <FormattedMessage {...messages.creativeLayoutDesc} />
                </CapHeading>
              </CapRow>
              <CapRadioGroup
                id="inapp-layout-radio"
                options={LAYOUT_RADIO_OPTIONS}
                value={templateLayoutType}
                onChange={onTemplateLayoutTypeChange}
                className="inapp-layout-radio"
              />
            </>
          )}
          {shouldUseHTMLEditor && (
            <HTMLEditor
              key={`inapp-html-editor-v${htmlEditorContentVersion}`}
              variant={HTML_EDITOR_VARIANTS.INAPP}
              layoutType={templateLayoutType}
              initialContent={{
                android: htmlContentAndroidRef.current || htmlContentAndroid || templateMessageAndroid || '',
                ios: htmlContentIosRef.current || htmlContentIos || templateMessageIos || '',
              }}
              onContentChange={handleHtmlContentChange}
              onSave={handleHtmlSave}
              tags={tags}
              injectedTags={injectedTags}
              location={location}
              selectedOfferDetails={selectedOfferDetails}
              onTagSelect={() => {
                // Tag insertion handled by HTMLEditor's CodeEditorPane at cursor position
              }}
              onContextChange={handleOnTagsContextChange}
              errors={errorMessage}
              isFullMode={isFullMode}
              data-test="inapp-html-editor"
              style={{ width: '138%' }}
            />
          )}
          {!shouldUseHTMLEditor && isNewEditorFlowEnabled && (
            <InappAdvanced
              getFormData={getFormData}
              setIsLoadingContent={setIsLoadingContent}
              defaultData={{ "template-name": tempName }}
              location={{
                pathname: `/inapp/create`,
                query,
                search: '',
              }}
              params={{ mode: inAppCreateMode }}
              isFullMode={isFullMode}
              isGetFormData={isGetFormData}
              showTemplateName={showTemplateName}
              route={{ name: "inapp" }}
              getDefaultTags={type}
              onValidationFail={onValidationFail}
              templateData={templateData}
              templateName={tempName}
              setTemplateName={setTempName}
              forwardedTags={forwardedTags}
              selectedOfferDetails={selectedOfferDetails}
              onCreateComplete={onCreateComplete}
            />
          )}
          {!shouldUseHTMLEditor && !isNewEditorFlowEnabled && (
            <>
              <CapInput
                label={<FormattedMessage {...messages.creativeName} />}
                onChange={({ target: { value } }) => setTempName(value)}
                value={tempName}
                labelPosition="top"
                size="default"
              />
              <CapRow>
                <CapHeading type="h4">
                  <FormattedMessage {...messages.creativeLayout} />
                </CapHeading>
                <CapHeading type="h6" className="inapp-creative-layout-desc">
                  <FormattedMessage {...messages.creativeLayoutDesc} />
                </CapHeading>
              </CapRow>
              <CapRadioGroup
                id="inapp-layout-radio"
                options={LAYOUT_RADIO_OPTIONS}
                value={templateLayoutType}
                onChange={onTemplateLayoutTypeChange}
                className="inapp-layout-radio"
              />
              <CapTab
                panes={DEVICE_PANES.filter((devicePane) => devicePane?.isSupported === true)}
                onChange={(value) => setPanes(value)}
                activeKey={panes}
                defaultActiveKey={panes}
                className="inapp-template-device-tab"
              />
            </>
          )}
        </CapColumn>
      </CapRow>
      {/* Footer with Done/Update button - show for HTML editor and old CapDeviceContent flow */}
      {
        (shouldUseHTMLEditor || !isNewEditorFlowEnabled) && (
          <>
            {hasAnyErrors(errorMessage) && (
              <ErrorInfoNote
                errorMessages={errorMessage}
                currentTab={panes}
              />
            )}
            <div className={`inapp-footer ${!isFullMode && `inapp-footer-lib`}`}>
              <CapButton
                onClick={validationMiddleWare}
                disabled={shouldUseHTMLEditor ? isDisableDone(null) : isDisableDone(panes)}
                className="inapp-create-btn"
              >
                {(() => {
                  if (isEditFlow) {
                    return isFullMode ? (
                      <FormattedMessage {...messages.update} />
                    ) : (
                      <FormattedMessage {...globalMessages.done} />
                    );
                  }
                  return isFullMode ? (
                    <FormattedMessage {...messages.create} />
                  ) : (
                    <FormattedMessage {...globalMessages.done} />
                  );
                })()}
              </CapButton>
              {/* {!isFullMode && ( */}
              <CapButton
                onClick={handleTestAndPreview}
                className="inapp-test-preview-btn"
                type="secondary"
                style={{ marginLeft: '8px' }}
              >
                <FormattedMessage {...creativesMessages.testAndPreview} />
              </CapButton>
              {/* )} */}
            </div>
          </>
        )}
      <TestAndPreviewSlidebox
        show={propsShowTestAndPreviewSlidebox || showTestAndPreviewSlidebox}
        onClose={handleCloseTestAndPreview}
        formData={null} // InApp doesn't use formData structure like SMS
        content={getTemplateContent()}
        currentChannel={INAPP}
        accountId={accountData?.selectedWeChatAccount?.id || null} // Pass selected account ID for test message payload
      />
    </CapSpin>
  );
};

const mapStateToProps = createStructuredSelector({
  editData: makeSelectInApp(),
  accountData: makeSelectAccount(),
  metaEntities: makeSelectMetaEntities(),
  loadingTags: isLoadingMetaEntities(),
  injectedTags: setInjectedTags(),
  currentOrgDetails: selectCurrentOrgDetails(),
  fetchingLiquidValidation: selectLiquidStateDetails(),
  getTemplateDetailsInProgress: makeSelectGetTemplateDetailsInProgress(),
});

const mapDispatchToProps = (dispatch) => ({
  actions: bindActionCreators(inAppActions, dispatch),
  globalActions: bindActionCreators(globalActions, dispatch),
});

const withReducer = injectReducer({ key: 'inapp', reducer: v2InAppReducer });
const withInAppSaga = injectSaga({ key: 'inapp', saga: v2InAppSagas, mode: DAEMON });

export default withCreatives({
  WrappedComponent: injectIntl(InApp),
  mapStateToProps,
  mapDispatchToProps,
  userAuth: true,
  sagas: [withInAppSaga],
  reducers: [withReducer],
});
