package com.neusoft.smart.pos.member.service;

import com.alibaba.fastjson.JSONObject;
import com.neusoft.smart.pos.dto.member.request.CheckMemberLevelRequest;
import com.neusoft.smart.pos.dto.member.response.DetailedMemberResponse;
import com.neusoft.smart.pos.dto.promotion.bean.CalculateBean;
import com.neusoft.smart.pos.framework.exception.BusinessCommonException;
import com.neusoft.smart.pos.member.constants.BonusCalculatorType;
import com.neusoft.smart.pos.member.constants.MemberErrorCode;
import com.neusoft.smart.pos.member.constants.MemberLevelConditionType;
import com.neusoft.smart.pos.member.core.BonusCalculator;
import com.neusoft.smart.pos.member.core.MemberLevelCondition;
import com.neusoft.smart.pos.member.dto.BonusCalculatorTypeResponse;
import com.neusoft.smart.pos.member.dto.MemberLevelConditionTypeResponse;
import com.neusoft.smart.pos.member.dto.MemberLevelPolicyResponse;
import com.neusoft.smart.pos.member.dto.request.CreateMemberLevelPolicyRequest;
import com.neusoft.smart.pos.member.dto.request.DeleteMemberLevelPolicyRequest;
import com.neusoft.smart.pos.member.dto.request.GetMemberLevelPolicyRequest;
import com.neusoft.smart.pos.member.dto.request.UpdateMemberLevelPolicyRequest;
import com.neusoft.smart.pos.member.persistent.domain.MemberLevelPolicy;
import com.neusoft.smart.pos.member.persistent.domain.MemberLevelPolicyExample;
import com.neusoft.smart.pos.member.persistent.mapper.MemberLevelPolicyMapper;
import com.neusoft.smart.pos.utils.RequestHeaderUtil;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class MemberLevelPolicyService {
    @Resource
    MemberLevelPolicyMapper memberLevelPolicyMapper;

    @Autowired
    MemberService memberService;
    @Autowired
    private MemberBusinessService memberBusinessService;

    private static List<MemberLevelConditionTypeResponse> _conditionTypesTag;
    private static List<BonusCalculatorTypeResponse> _calculatorTypesTag;

    private static List<MemberLevelConditionTypeResponse> getConditionTypesTag() {
        if (_conditionTypesTag == null || _conditionTypesTag.size() == 0) {
            _conditionTypesTag = new ArrayList<>();
            for (MemberLevelCondition.ConditionCreator creator : MemberLevelCondition.getPolicyClassesMap().values()
            ) {
                _conditionTypesTag.add(creator.getConditionTypeTag());
            }
        }
        return _conditionTypesTag;
    }

    private static List<BonusCalculatorTypeResponse> getCalculatorTypesTag() {
        if (_calculatorTypesTag == null || _calculatorTypesTag.size() == 0) {
            _calculatorTypesTag = new ArrayList<>();
            for (BonusCalculator.ConditionCreator creator : BonusCalculator.getPolicyClassesMap().values()
            ) {
                _calculatorTypesTag.add(creator.getBonusCalculatorTag());
            }
        }
        return _calculatorTypesTag;
    }

    public List<BonusCalculatorTypeResponse> getBonusCalculatorTypes() {
        return getCalculatorTypesTag();
    }

    public List<MemberLevelConditionTypeResponse> getMemberLevelPolicyConditionTypes() {
        return getConditionTypesTag();
    }

    public List<MemberLevelPolicyResponse> selectMemberLevelPolicyList(GetMemberLevelPolicyRequest request) {
        MemberLevelPolicyExample example = new MemberLevelPolicyExample();
        MemberLevelPolicyExample.Criteria criteria = example.createCriteria();

        criteria.andIsActiveEqualTo(1);

        OrgAndStore orgAndStore = checkOrgAndStoreByRole(null, 0, request.getOrg(), request.getStore());

        criteria.andOrgIdEqualTo(orgAndStore.getOrg());
        criteria.andStoreIdEqualTo(orgAndStore.getStore());

        checkAndBuildDefaultLevel(orgAndStore.getOrg(), orgAndStore.getStore());

        example.setOrderByClause("store_id, level_number asc");

        ArrayList<MemberLevelPolicyResponse> result = new ArrayList<>();
        for (MemberLevelPolicy memberLevel : memberLevelPolicyMapper.selectByExample(example)) {
            result.add(fillMemberLevelResponse(new MemberLevelPolicyResponse(), memberLevel));
        }

        return result;
    }

    private MemberLevelPolicyResponse fillMemberLevelResponse(MemberLevelPolicyResponse resp, MemberLevelPolicy memberLevel) {
        resp.setId(memberLevel.getId());
        resp.setLevelName(memberLevel.getLevelName());
        resp.setLevelNumber(memberLevel.getLevelNumber());
        resp.setCondition(memberLevel.getLevelCondition());
        resp.setBonusTable(memberLevel.getBonusCalculator());

        return resp;
    }

    private void checkAndBuildDefaultLevel(Integer org, Integer store) {
        MemberLevelPolicyExample example = new MemberLevelPolicyExample();
        MemberLevelPolicyExample.Criteria criteria = example.createCriteria();

        criteria.andOrgIdEqualTo(org);
        criteria.andStoreIdEqualTo(store);
        criteria.andLevelNumberEqualTo(1);
        criteria.andIsActiveEqualTo(1);

        List<MemberLevelPolicy> result = memberLevelPolicyMapper.selectByExample(example);
        if (result.size() > 0) {
            return;
        }

        MemberLevelPolicy memberLevelPolicy = new MemberLevelPolicy();
        memberLevelPolicy.setLevelName("初级");
        memberLevelPolicy.setLevelNumber(1);

        MemberLevelCondition condition = MemberLevelCondition.createDefaultMemberLevelCondition(MemberLevelConditionType.RECHARGE_BASED);
        BonusCalculator calc = BonusCalculator.createDefaultBonusCalculator(BonusCalculatorType.ORDER_AMOUNT_DISCOUNT);
        JSONObject calculators = new JSONObject();
        calculators.put("0", calc.toJSONObject());
        memberLevelPolicy.setLevelCondition(condition.toJsonString());
        memberLevelPolicy.setBonusCalculator(calculators.toJSONString());

        memberLevelPolicy.setOrgId(org);
        memberLevelPolicy.setStoreId(store);

        memberLevelPolicyMapper.insertSelective(memberLevelPolicy);

        return;
    }

    public MemberLevelPolicyResponse selectMemberLevelPolicyById(Integer id) {
        return fillMemberLevelResponse(new MemberLevelPolicyResponse(), memberLevelPolicyMapper.selectByPrimaryKey(id));
    }


    public MemberLevelPolicyResponse insertOrUpdateMemberLevelPolicy(CreateMemberLevelPolicyRequest request) {
        MemberLevelPolicy memberLevelPolicy = new MemberLevelPolicy();
        BonusCalculator.validateJsonTable(request.getBonusArray());

        OrgAndStore orgAndStore = checkOrgAndStoreByRole(null, 0, request.getOrg(), request.getStore());

        memberLevelPolicy.setOrgId(orgAndStore.getOrg());
        memberLevelPolicy.setStoreId(orgAndStore.getStore());
        memberLevelPolicy.setLevelName(request.getLevelName());
        memberLevelPolicy.setLevelNumber(request.getLevelNumber());
        memberLevelPolicy.setLevelCondition(request.getCondition());
        memberLevelPolicy.setBonusCalculator(request.getBonusArray());

        MemberLevelPolicyExample example = new MemberLevelPolicyExample();
        example.createCriteria().andLevelNumberEqualTo(memberLevelPolicy.getLevelNumber())
                .andOrgIdEqualTo(memberLevelPolicy.getOrgId())
                .andStoreIdEqualTo(memberLevelPolicy.getStoreId());

        if (memberLevelPolicyMapper.countByExample(example) > 0) {
            // update
            memberLevelPolicyMapper.updateByExampleSelective(memberLevelPolicy, example);
        } else {
            // create
            memberLevelPolicyMapper.insertSelective(memberLevelPolicy);
        }

        return fillMemberLevelResponse(new MemberLevelPolicyResponse(), memberLevelPolicy);

    }

    public MemberLevelPolicyResponse updateMemberLevelPolicy(Integer id, UpdateMemberLevelPolicyRequest request) {
        MemberLevelPolicy memberLevelPolicy = new MemberLevelPolicy();
        memberLevelPolicy.setId(id);

        BonusCalculator.validateJsonTable(request.getBonusArray());

        if (!StringUtils.isEmpty(request.getLevelName())) memberLevelPolicy.setLevelName(request.getLevelName());
        memberLevelPolicy.setBonusCalculator(request.getBonusArray());
        if (request.getCondition() != null) memberLevelPolicy.setLevelCondition(request.getCondition());

        memberLevelPolicyMapper.updateByPrimaryKeySelective(memberLevelPolicy);

        return fillMemberLevelResponse(new MemberLevelPolicyResponse(), memberLevelPolicyMapper.selectByPrimaryKey(id));
    }

    public void deleteMemberPolicyLevel(DeleteMemberLevelPolicyRequest request) {
        MemberLevelPolicyExample example = new MemberLevelPolicyExample();
        MemberLevelPolicyExample.Criteria criteria = example.createCriteria();

        criteria.andLevelNumberEqualTo(request.getLevel());

        OrgAndStore orgAndStore = checkOrgAndStoreByRole(null, 0, request.getOrg(), request.getStore());

        criteria.andOrgIdEqualTo(orgAndStore.getOrg());
        criteria.andStoreIdEqualTo(orgAndStore.getStore());

        memberLevelPolicyMapper.deleteByExample(example);
    }

    public CalculateBean calculate(DetailedMemberResponse detailedMember, CalculateBean bean) {
        if (detailedMember == null || detailedMember.getMemberLevelId() == null) {
            return bean;
        }
        MemberLevelPolicy policy = memberLevelPolicyMapper.selectByPrimaryKey(detailedMember.getMemberLevelId());

        Map<Integer, BonusCalculator> calculatorTable = BonusCalculator.createBonusCalculatorTable(policy.getBonusCalculator());

        for (Integer i=0;i<calculatorTable.size();i++
        ) {
            if(calculatorTable.containsKey(i))
            {
                bean = calculatorTable.get(i).calculate(bean);
            }
        }
        return bean;
    }

    public List<MemberLevelPolicy> getMemberLevelPoliciesByOrgStore(Integer org, Integer store) {
        if (store == null)
            store = 0;
        MemberLevelPolicyExample example = new MemberLevelPolicyExample();
        MemberLevelPolicyExample.Criteria criteria = example.createCriteria();

        criteria.andOrgIdEqualTo(org);
        criteria.andStoreIdEqualTo(store);
        criteria.andIsActiveEqualTo(1);

        List<MemberLevelPolicy> result = memberLevelPolicyMapper.selectByExample(example);
        return result;
    }

    public Boolean checkMemberMatchLevel(MemberLevelPolicy policy, CheckMemberLevelRequest request) {
        MemberLevelCondition condition = MemberLevelCondition.createMemberLevelCondition(policy.getLevelCondition());
        return condition.isMatch(request);
    }


    public MemberLevelPolicy getMemberLevelPolicyById(Integer memberLevelId) {
        return memberLevelPolicyMapper.selectByPrimaryKey(memberLevelId);
    }

    private OrgAndStore checkOrgAndStoreByRole(Integer org, Integer store, Integer requestOrg, Integer requestStore) {
        switch (RequestHeaderUtil.getRole()) {
            case "sa":
                if (requestOrg == null || requestStore == null) {
                    throw new BusinessCommonException(MemberErrorCode.NO_ORG_PROVIDED);
                }
                org = requestOrg;
                store = requestStore;
                break;
            case "admin":
                if (requestOrg == null) {
                    throw new BusinessCommonException(MemberErrorCode.NO_ORG_PROVIDED);
                }
                org = RequestHeaderUtil.getOrgId();
                if (org == null) org = requestOrg;
                store = requestStore;
                break;
            case "user":
                if (requestStore == null) {
                    throw new BusinessCommonException(MemberErrorCode.NO_STORE_PROVIDED);
                }
                org = RequestHeaderUtil.getOrgId();
                if (org == null) org = requestOrg;
                store = requestStore;
                break;
            default:
                throw new BusinessCommonException(MemberErrorCode.UNKNOWN_USER_ROLE);
        }
        return new OrgAndStore(org, store);
    }

    @Data
    private class OrgAndStore {
        Integer org;
        Integer store;

        public OrgAndStore(Integer org, Integer store) {
            this.org = org;
            this.store = store;
        }
    }

}
