package cn.axzo.msg.center.message.service.impl.v3;

import static cn.axzo.msg.center.common.utils.PlaceholderResolver.getDefaultResolver;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import cn.axzo.msg.center.domain.entity.MessageTemplateButtonV3;
import cn.axzo.msg.center.domain.entity.MessageTemplateGroupV3;
import cn.axzo.msg.center.message.domain.dto.TemplateModelV3;
import cn.axzo.msg.center.service.domain.UrlConfig;
import cn.axzo.msg.center.service.enums.GroupType;
import cn.axzo.msg.center.service.enums.KVContentType;
import cn.axzo.msg.center.service.pending.card.domain.CardElementConfig;
import cn.axzo.msg.center.service.pending.response.v3.ParsedModel3Visitor;
import cn.axzo.msg.center.service.pending.response.v3.ParsedModelV3Walker;
import cn.axzo.msg.center.service.pending.response.v3.model.ComponentWorkerGroup;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedButtonV3;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedCardStyle;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedGroupDetail;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedGroupInfo;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedGroupV3;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedKV;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedModelV3;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedTemplateV3;
import cn.axzo.msg.center.service.pending.response.v3.model.PhoneInfo;
import cn.axzo.trade.datasecurity.core.util.DataSecurityHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author yanglin
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ModelV3Parser {

    private static final String CUSTOMER_PERSON_ID = "customerPersonId";

    public ParsedModelV3 parseModel(TemplateModelV3 templateModel,
                                    JSONObject bizParam,
                                    UrlParser urlParser) {
        ParsedModelV3 parsedModel = deepCopyModel(templateModel);
        ParsedModelV3Walker.walkDown(parsedModel, new ParsedModel3Visitor() {

            @Override
            public void visitTemplate(ParsedTemplateV3 template) {
               template.setTitle(resolveBizParam(template.getTitle()));
                template.setContent(resolveBizParam(template.getContent()));
            }

            @Override
            public void visitTemplateCardUrlConfig(UrlConfig urlConfig) {
                if (!urlConfig.hasUrl()) return;
                urlParser.parseUrlConfig(urlConfig);
            }

            @Override
            public void visitGroup(ParsedGroupV3 group) {
                if (group.getGroupType() != GroupType.COMPONENT_WORKER)
                    return;
                group.setName("工人信息");
                Long personId = bizParam.getLong(CUSTOMER_PERSON_ID);
                if (personId != null && personId > 0) {
                    parsedModel.addComponentWorkerGroup(new ComponentWorkerGroup(parsedModel, group, personId));
                } else {
                    log.warn("invalid worker identity id for component worker group. groupId={}, bizParams={}",
                            group.getId(), bizParam);
                    parsedModel.removeIdenticalGroup(group);
                }
            }

            @Override
            public void visitGroupKeyValue(ParsedGroupV3 group, ParsedKV kv) {
                kv.setValueVariable(kv.getValue());
                kv.setValue(resolveBizParam(kv.getValue()));

                if (kv.getContentType() == KVContentType.PERSON_ID
                        && StringUtils.isNotBlank(kv.getValue())
                        && NumberUtils.isDigits(kv.getValue())) {
                    parsedModel.addPersonKV(kv);
                } else if (kv.getContentType() == KVContentType.PHONE) {
                    kv.setPhoneInfo(parsePhone(kv));
                }
            }

            private PhoneInfo parsePhone(ParsedKV kv) {
                try {
                    PhoneInfo phoneInfo = new PhoneInfo();
                    String phone = kv.getValue();
                    if (phone.contains("*"))
                        phone = DataSecurityHelper.decrypt(phone);
                    phoneInfo.setMobilePhone(phone);
                    phoneInfo.setMobilePhoneCiphertext(DataSecurityHelper.encrypt(phone));
                    return phoneInfo;
                } catch (Exception e) {
                    log.warn("error parse phone info. kv={}", kv);
                    return null;
                }
            }

            @Override
            public void exitGroupInfo(ParsedGroupV3 group, ParsedGroupInfo groupInfo) {
                groupInfo.removeEmptyKValues();
            }

            @Override
            public void visitGroupDetailUrlConfig(ParsedGroupV3 group, UrlConfig urlConfig) {
                if (!urlConfig.hasUrl()) return;
                urlParser.parseUrlConfig(urlConfig);
                ParsedGroupDetail parsedDetail = group.getGroupInfo().getOrCreateDetail();
                parsedDetail.setName(group.getGroupInfo().getDetail().getName());
            }

            @Override
            public void visitButton(ParsedButtonV3 button) {
                button.setApiUrl(urlParser.parseUrl(button.getApiUrl()));
            }

            @Override
            public void visitButtonUrlConfig(ParsedButtonV3 button, UrlConfig urlConfig) {
                urlParser.parseUrlConfig(urlConfig);
            }

            @Override
            public void visitTemplateCardExtField(CardElementConfig cfg) {
                resolveCardElement(cfg);
            }

            @Override
            public void visitCardStyleElement(CardElementConfig cfg) {
                resolveCardElement(cfg);
            }

            private void resolveCardElement(CardElementConfig cfg) {
                cfg.setName(resolveBizParam(cfg.getName()));
                cfg.setValue(resolveBizParam(cfg.getValue()));
            }

            @Override
            public void exitModel(ParsedModelV3 model) {
                model.removeEmptyKVGroups();
            }

            private String resolveBizParam(String template) {
                return getDefaultResolver().resolveByMap(template, bizParam);
            }

        });
        // 提前排序避免并发问题
        parsedModel.sortButtons();
        return parsedModel;
    }

    /**
     * 拷贝对应避免对象引用问题
     */
    private ParsedModelV3 deepCopyModel(TemplateModelV3 templateModel) {
        ParsedModelV3 parsedModel = new ParsedModelV3();
        parsedModel.setTemplate(JSON.parseObject(JSON.toJSONString(templateModel.getTemplate()), ParsedTemplateV3.class));
        parsedModel.setGroups(deepCopyGroups(templateModel.getGroups()));
        parsedModel.setButtons(JSON.parseArray(JSON.toJSONString(templateModel.getButtons()), ParsedButtonV3.class));
        parsedModel.setCardStyle(JSON.parseObject(JSON.toJSONString(templateModel.getCardStyle()), ParsedCardStyle.class));
        if (CollectionUtils.isNotEmpty(templateModel.getButtons())) {
            for (int i = 0; i < templateModel.getButtons().size(); i++) {
                MessageTemplateButtonV3 button = templateModel.getButtons().get(i);
                parsedModel.getButtons().get(i).setPresetButtonType(button.getPresetBtnType());
            }
        }
        return parsedModel;
    }

    private List<ParsedGroupV3> deepCopyGroups(List<MessageTemplateGroupV3> groups) {
        ArrayList<ParsedGroupV3> parsedGroups = new ArrayList<>();
        for (MessageTemplateGroupV3 group : groups) {
            ParsedGroupV3 parsedGroup = new ParsedGroupV3();
            parsedGroup.setId(group.getId());
            parsedGroup.setName(group.getName());
            parsedGroup.setGroupType(group.getGroupType());
            String groupConfigJsonStr = JSON.toJSONString(group.getGroupConfig());
            parsedGroup.setGroupInfo(JSON.parseObject(groupConfigJsonStr, ParsedGroupInfo.class));
            parsedGroup.setWorkerInfo(null);
            parsedGroups.add(parsedGroup);
        }
        return parsedGroups;
    }

}