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

import com.neusoft.smart.pos.card.constants.CardCapitalChangeType;
import com.neusoft.smart.pos.card.constants.CardErrorCode;
import com.neusoft.smart.pos.card.dto.UpdateCardInfoRequest;
import com.neusoft.smart.pos.card.dto.UpdateCardRequest;
import com.neusoft.smart.pos.card.persistent.domain.Card;
import com.neusoft.smart.pos.card.persistent.domain.CardCapitalChange;
import com.neusoft.smart.pos.card.persistent.domain.CardExample;
import com.neusoft.smart.pos.card.persistent.domain.CardShopRelation;
import com.neusoft.smart.pos.card.persistent.mapper.CardMapper;
import com.neusoft.smart.pos.card.persistent.repository.CardCapitalChangeRepository;
import com.neusoft.smart.pos.card.persistent.repository.CardRepository;
import com.neusoft.smart.pos.card.persistent.repository.ICardRelationRepository;
import com.neusoft.smart.pos.constants.EntityTableConstant;
import com.neusoft.smart.pos.dto.member.request.MemberCardChangeRequest;
import com.neusoft.smart.pos.dto.member.request.UpdateCardPointRequest;
import com.neusoft.smart.pos.framework.exception.BusinessCommonException;
import com.neusoft.smart.pos.framework.utils.IdGenerateUtils;
import com.neusoft.smart.pos.framework.utils.SpringUtils;
import com.neusoft.smart.pos.member.constants.MemberErrorCode;
import com.neusoft.smart.pos.member.persistent.domain.MemberCardRelation;
import com.neusoft.smart.pos.member.persistent.domain.MemberWithCard;
import com.neusoft.smart.pos.member.persistent.domain.MemberWithCardExample;
import com.neusoft.smart.pos.member.persistent.mapper.MemberCardRelationMapper;
import com.neusoft.smart.pos.member.persistent.mapper.MemberWithCardMapper;
import com.neusoft.smart.pos.redis.bean.DeviceInfo;
import com.neusoft.smart.pos.utils.PosDeviceRedisUtils;
import com.neusoft.smart.pos.utils.RequestHeaderUtil;
import lombok.val;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;

@Service
public class CardService {
	private final static Logger logger = LoggerFactory.getLogger(CardService.class);

	@Resource
	private PasswordEncoder passwordEncoder;

	@Resource
	private MemberWithCardMapper memberWithCardMapper;
	
	@Resource
	private MemberCardRelationMapper memberCardRelationMapper;

    @Resource
    private CardRepository cardRepository;
	
	@Resource
	private CardMapper cardMapper;

    @Resource
    private CardCapitalChangeRepository cardCapitalChangeRepository;

	@Autowired
	private RemoteOrgService remoteOrgService;


	public void validateByCardNum(String cardNum) {
		MemberWithCardExample memberWithCardExample = new MemberWithCardExample();
		memberWithCardExample.createCriteria().andCardNumEqualTo(cardNum);
		MemberWithCard memberWithCard = memberWithCardMapper.selectByExample(memberWithCardExample).stream().filter(card -> card.getCardActive().equals(1)).findAny().orElse(null);
		if (null != memberWithCard) {
			throw new BusinessCommonException(MemberErrorCode.MEMBER_CARD_NUM_DUPLICATE);
		}
	}

	public void deactivateCard(Integer cardId) {
		CardExample cardExample = new CardExample();
		cardExample.createCriteria().andCardIdEqualTo(cardId);
		Card cardFound = cardMapper.selectByExample(cardExample).stream().filter(cardf -> cardf.getIsActive().equals(1)).findAny().orElse(null);
		if (cardFound == null) {
			throw new BusinessCommonException(CardErrorCode.CARD_NOT_EXIST);
		}

		cardFound.setIsActive(0);
		cardMapper.updateByPrimaryKey(cardFound);
	}

    /**
     * 更新卡识别号（即换卡）
     * @param memberCardChangeRequest 更新卡识别号请求
     * @return 卡号
     */
	@Transactional
	public String changeCard(MemberCardChangeRequest memberCardChangeRequest) {
		MemberWithCardExample memberWithCardExample = new MemberWithCardExample();
		memberWithCardExample.createCriteria().andMemberIdEqualTo(memberCardChangeRequest.getMemberId());
		MemberWithCard memberWithCard = memberWithCardMapper.selectByExample(memberWithCardExample).stream().filter(card -> card.getCardActive().equals(1)).findAny().orElse(null);

		if (StringUtils.isEmpty(memberCardChangeRequest.getCardNum())) {
			memberCardChangeRequest.setCardNum(generateCardNum());
		}

		if (null == memberWithCard) { //没有可用的会员卡,新插入一张会员卡
			Card card = new Card();
			card.setCardNum(memberCardChangeRequest.getCardNum());
            card.setCardOtherId(memberCardChangeRequest.getCardHidCode());
			cardMapper.insertSelective(card);
			this.createRelation(memberCardChangeRequest.getMemberId(), card.getCardId());
		} else { //更新原有卡号
			Card card = cardMapper.selectByPrimaryKey(memberWithCard.getCardId());
			Card newCard = new Card();
			BeanUtils.copyProperties(card, newCard, "cardId", "cardNum", "version", "createdTime", "createdBy", "updatedTime",
					"updatedBy");
			newCard.setCreatedBy(RequestHeaderUtil.getUserId()+"");
			newCard.setCreatedTime(new Date());
			newCard.setCardNum(memberCardChangeRequest.getCardNum());
            newCard.setCardOtherId(memberCardChangeRequest.getCardHidCode());
			cardMapper.insertSelective(newCard);
			
			this.createRelation(memberCardChangeRequest.getMemberId(), newCard.getCardId());
			
			//更新下原始会员卡数据
//			card.setCardBalance(BigDecimal.valueOf(0)); // TODO 卡内余额和积分是否需要更新，如果更新，需要创建卡内资金变更记录
//			card.setCardPoint(0);
			card.setIsActive(0);
			cardMapper.updateByPrimaryKey(card);

			// 更新店铺和卡的对应关系表，卡兑换策略表
			val cardRelationRepository = SpringUtils.getBean(card.getBeanName(), ICardRelationRepository.class);
			cardRelationRepository.updateCardShopRelation(card.getCardId(), newCard.getCardId());
			cardRelationRepository.updateExchangeStrategy(card.getCardId(), newCard.getCardId());

		}
		return memberCardChangeRequest.getCardNum();
	}
	
	/**
	 * 开卡
	 * @param card 开卡信息
	 */
	public void createCard(Card card) {
		if (StringUtils.isEmpty(card.getCardNum())) {
			card.setCardNum(generateCardNum());
		}

		card.setCardPassword(passwordEncoder.encode(card.getCardPassword()));
		cardMapper.insertSelective(card);
	}
	
	private String generateCardNum() {
		return IdGenerateUtils.generateMemberCardNum();
	}

	/**
	 * 卡唯一性验证: 同一个组织中，cardNum唯一
	 * @param card 卡信息
	 * @param relations 卡与店铺关系列表
	 */
	public void verifyCardNumUniqueness(Card card, List<CardShopRelation> relations) {
		for (CardShopRelation relation : relations) {
			Map<String, Object> params = new HashMap<>();
			params.put("card_id", card.getCardId());
			params.put("card_num", card.getCardNum());
			params.put("store_id", relation.getStoreId());
			List<Map> rs =  cardMapper.queryCardWithSameNum(params);
			if(rs != null && rs.size() > 0) {
				throw new BusinessCommonException(CardErrorCode.MEMBER_CARD_NUM_DUPLICATE);
			}
		}
	}

	/**
	 * 卡唯一性验证: 同一个组织中，cardAnotherId唯一
	 * @param card 卡信息
	 * @param relations 卡与店铺关系列表
	 */
	public void verifyCardAnotherIdUniqueness(Card card, List<CardShopRelation> relations) {
		for (CardShopRelation relation : relations) {
			if (!StringUtils.isEmpty(card.getCardAnotherId())) {
				Map<String, Object> params = new HashMap<>();
				params.put("card_id", card.getCardId());
				params.put("card_another_id", card.getCardAnotherId());
				params.put("store_id", relation.getStoreId());
				List<Map> rs =  cardMapper.queryCardWithSameAnotherId(params);
				if(rs != null && rs.size() > 0) {
					throw new BusinessCommonException(CardErrorCode.CARD_ANOTHER_ID_DUPLICATE);
				}
			}
		}
	}

    /**
     * 更新卡内积分： 原积分 + request中的积分
     * @param request 更新卡内积分请求
     */
	public void updateCardPoint(UpdateCardPointRequest request) {
		CardExample example = new CardExample();
		example.createCriteria().andCardNumEqualTo(request.getCardNum()).andIsActiveEqualTo(1);
		
		List<Card> result = cardMapper.selectByExample(example);
		if (result.size() == 0) {
			throw new BusinessCommonException(CardErrorCode.CARD_NOT_EXIST);
		}
		
		Card card = new Card();
		BeanUtils.copyProperties(result.get(0), card, "cardPoint");
		validateCard(card, request.getSn());

        card.setCardPoint(card.getCardPoint() + request.getCardPoint());
		cardMapper.updateByPrimaryKeySelective(card);
	}

    /**
     * 更新卡内余额和积分：更新为request中的数值
	 * 加入卡内原余额和积分，是为了保证操作原子性，避免充值和消费同时进行时导致数据错误
     * @param request 卡余额积分更新请求
     */
	public int updateCard(UpdateCardRequest request) {
		CardExample example = new CardExample();
		example.createCriteria().andCardNumEqualTo(request.getCardNum())
				.andIsActiveEqualTo(1)
				.andCardBalanceEqualTo(request.getCardBeforeBalance())
				.andCardPointEqualTo(request.getCardBeforePoint());

		Card card = new Card();
		card.setCardBalance(request.getCardAfterBalance());
		card.setCardPoint(request.getCardAfterPoint());
		if(request.getRechargeAmount() != null) {
			card.setLastRechargeAmount(request.getRechargeAmount());
		}
		return cardMapper.updateByExampleSelective(card, example);
    }
	
	/**
	 * 创建会员和卡的对应关系
	 * @param memberId 会员ID
	 * @param cardId 卡ID
	 */
	public void createRelation(Integer memberId, Integer cardId) {
		MemberCardRelation memberCardRelation = new MemberCardRelation();
		memberCardRelation.setMemberId(memberId);
		memberCardRelation.setCardId(cardId);
		memberCardRelationMapper.insertSelective(memberCardRelation);
	}

	/**
	 * 根据cardId查询卡
	 * @param cardId 卡ID
	 * @return 查询到的卡结构
	 */
	public Card getCardByPrimaryId(int cardId) {
	    return cardRepository.get(cardId);
    }

	/**
	 * 根据卡号或会员ID或卡其他识别ID获得卡
	 * @param cardNum 卡号
	 * @param cardAnotherId 卡识别ID2
	 * @param cardOtherId 卡其他ID
	 * @param sn 接口调用端设备ID
	 * @param orgId 接口调用端orgId
	 * @param shopId 卡接口调用端店铺ID
	 * @return 查询到的卡结构
	 */
	public Card getSingleCard(String cardNum, String cardAnotherId, String cardOtherId, String sn, Integer orgId, Integer shopId) {
	    if(StringUtils.isEmpty(cardNum) && StringUtils.isEmpty(cardAnotherId)) {
            throw new BusinessCommonException(CardErrorCode.CARD_QUERY_PARAM_EMPTY);
        }
        CardExample cardExample = new CardExample();
        CardExample.Criteria criteria = cardExample.createCriteria();

        if(!StringUtils.isEmpty(cardNum)) {
            criteria.andCardNumEqualTo(cardNum);
        }
        if(!StringUtils.isEmpty(cardAnotherId)) {
            criteria.andCardAnotherIdEqualTo(cardAnotherId);
        }
        if(!StringUtils.isEmpty(cardOtherId)) {
            criteria.andCardOtherIdEqualTo(cardOtherId);
        }
        criteria.andIsActiveEqualTo(1);

        List<Card> list = cardMapper.selectByExample(cardExample);

        if(StringUtils.isEmpty(sn) && StringUtils.isEmpty(orgId) && StringUtils.isEmpty(shopId)) { // 不用验证，返回查询到的第一张卡
			return list.size() > 0 ? list.get(0) : null;
		}
        if(StringUtils.isEmpty(sn)) { // 用orgId和shopId进行验证
			for (Card card:list) {
				val cardRelationRepository = SpringUtils.getBean(card.getBeanName(), ICardRelationRepository.class);
				List<CardShopRelation> relationList = cardRelationRepository.getCardShopRelation(card.getCardId());
				for (CardShopRelation relation : relationList) {
					val isChildResponse = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_SHOP.getCode(), shopId, relation.getStoreId());
					if (null != isChildResponse && null != isChildResponse.getResult() && isChildResponse.getResult()) {
						return card;
					}

					val isChildResponse2 = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_ORG.getCode(), orgId, relation.getStoreId());
					if (null != isChildResponse2 && null != isChildResponse2.getResult() && isChildResponse2.getResult()) {
						return card;
					}
				}
			}
        } else { // 用sn进行验证
			for (Card card:list) {
				DeviceInfo deviceInfo = PosDeviceRedisUtils.getDeviceInfoBySerialNo(sn);
				if (null == deviceInfo) {
					throw new BusinessCommonException(CardErrorCode.DEVICE_INFO_NOT_FOUND);
				}
				val cardRelationRepository = SpringUtils.getBean(card.getBeanName(), ICardRelationRepository.class);
				List<CardShopRelation> relationList = cardRelationRepository.getCardShopRelation(card.getCardId());
				for (CardShopRelation relation : relationList) {
					val isChildResponse = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_SHOP.getCode(), deviceInfo.getStoreId(), relation.getStoreId());
					if (null != isChildResponse && null != isChildResponse.getResult() && isChildResponse.getResult()) {
						return card;
					}

					val isChildResponse2 = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_ORG.getCode(), deviceInfo.getOrganizationId(), relation.getStoreId());
					if (null != isChildResponse2 && null != isChildResponse2.getResult() && isChildResponse2.getResult()) {
						return card;
					}
				}
			}
        }
		return null;
    }

	/**
	 * 根据卡号或会员ID或卡其他识别ID获得卡
	 * @param cardNum 卡号
	 * @param cardAnotherId 卡识别ID2
	 * @param cardOtherId 卡其他ID
	 * @param sn 接口调用端设备ID
	 * @param orgId 接口调用端orgId
	 * @param shopId 卡接口调用端店铺ID
	 * @return 查询到的卡l列表
	 */
	public List<Card> getCard(String cardNum, String cardAnotherId, String cardOtherId, String sn, Integer orgId, Integer shopId) {
		if(StringUtils.isEmpty(cardNum) && StringUtils.isEmpty(cardAnotherId) && StringUtils.isEmpty(cardOtherId)) {
			throw new BusinessCommonException(CardErrorCode.CARD_QUERY_PARAM_EMPTY);
		}
		CardExample cardExample = new CardExample();
		CardExample.Criteria criteria = cardExample.createCriteria();

		if(!StringUtils.isEmpty(cardNum)) {
			criteria.andCardNumEqualTo(cardNum);
		}
		if(!StringUtils.isEmpty(cardAnotherId)) {
			criteria.andCardAnotherIdEqualTo(cardAnotherId);
		}
		if(!StringUtils.isEmpty(cardOtherId)) {
			criteria.andCardOtherIdEqualTo(cardOtherId);
		}
		criteria.andIsActiveEqualTo(1);

		List<Card> list = cardMapper.selectByExample(cardExample);

		if(StringUtils.isEmpty(sn) && StringUtils.isEmpty(orgId) && StringUtils.isEmpty(shopId)) { // 不用验证，返回查询到的所有卡
			return list;
		}
		List<Card> resultList = new ArrayList<>();
		if(StringUtils.isEmpty(sn)) { // 用orgId和shopId进行验证
			for (Card card:list) {
				val cardRelationRepository = SpringUtils.getBean(card.getBeanName(), ICardRelationRepository.class);
				List<CardShopRelation> relationList = cardRelationRepository.getCardShopRelation(card.getCardId());
				for (CardShopRelation relation : relationList) {
					val isChildResponse = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_SHOP.getCode(), shopId, relation.getStoreId());
					if (null != isChildResponse && null != isChildResponse.getResult() && isChildResponse.getResult()) {
						resultList.add(card);
						continue;
					}

					val isChildResponse2 = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_ORG.getCode(), orgId, relation.getStoreId());
					if (null != isChildResponse2 && null != isChildResponse2.getResult() && isChildResponse2.getResult()) {
						resultList.add(card);
						continue;
					}
				}
			}
		} else { // 用sn进行验证
			for (Card card:list) {
				DeviceInfo deviceInfo = PosDeviceRedisUtils.getDeviceInfoBySerialNo(sn);
				if (null == deviceInfo) {
					throw new BusinessCommonException(CardErrorCode.DEVICE_INFO_NOT_FOUND);
				}
				val cardRelationRepository = SpringUtils.getBean(card.getBeanName(), ICardRelationRepository.class);
				List<CardShopRelation> relationList = cardRelationRepository.getCardShopRelation(card.getCardId());
				for (CardShopRelation relation : relationList) {
					val isChildResponse = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_SHOP.getCode(), deviceInfo.getStoreId(), relation.getStoreId());
					if (null != isChildResponse && null != isChildResponse.getResult() && isChildResponse.getResult()) {
						resultList.add(card);
						continue;
					}

					val isChildResponse2 = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_ORG.getCode(), deviceInfo.getOrganizationId(), relation.getStoreId());
					if (null != isChildResponse2 && null != isChildResponse2.getResult() && isChildResponse2.getResult()) {
						resultList.add(card);
						continue;
					}
				}
			}
		}
		return resultList;
	}

	/**
	 * 根据卡号查询卡
	 * @param cardNum 卡号
	 * @return 查询到的卡结构
	 */
    public Card getCardByCardNum(String cardNum) {
        Card card = cardRepository.getActiveCardByCardNum(cardNum);
        if (null == card) {
            throw new BusinessCommonException(CardErrorCode.CARD_NOT_EXIST);
        } else if (!card.getIsActive().equals(1)) {
            throw new BusinessCommonException(CardErrorCode.CARD_CAN_NOT_USE);
        }
        return card;
    }

    public Long getCardBalance(String cardNum, String cardAnotherId, String cardOtherId) {
        Card card = this.getSingleCard(cardNum, cardAnotherId, cardOtherId, null, null, null);
		if(card == null) {
			throw new BusinessCommonException(CardErrorCode.CARD_NOT_EXIST);
		}
        return card.getCardBalance();
    }

    /**
     * 清空卡余额
	 */
    @Transactional
    public void emptyBalance(String cardNum, String cardAnotherId, String cardOtherId, String sn) {
        Card card = this.getSingleCard(cardNum, cardAnotherId, cardOtherId, sn, null, null);
		if(card == null) {
			throw new BusinessCommonException(CardErrorCode.CARD_NOT_EXIST);
		}
        Long originalBalance = card.getCardBalance();
        card.setCardBalance(0L);
        card.setCardPoint(0);
        cardMapper.updateByPrimaryKeySelective(card);

        //存下变更表
        CardCapitalChange cardCapitalChange = new CardCapitalChange();
        cardCapitalChange.setCardId(card.getCardId());
        cardCapitalChange.setCardNum(card.getCardNum());
        cardCapitalChange.setChangeOrderId(UUID.randomUUID().toString());//随个uuid占位
        cardCapitalChange.setAmountChange(originalBalance);//修改后减去修改前,就是差额,负的就是扣减
        cardCapitalChange.setAmountBefore(originalBalance);
        cardCapitalChange.setAmountAfter(0L);
        cardCapitalChange.setContent("清卡");
        cardCapitalChange.setChangeTime(new Date());
        cardCapitalChange.setChangeType(CardCapitalChangeType.EMPTY.getCode());
        cardCapitalChangeRepository.createChange(cardCapitalChange);
    }

	// 所有的卡相关操作需要检验在该店铺下是否可用
	public void validateCard(Card card, String sn) {
		DeviceInfo deviceInfo = PosDeviceRedisUtils.getDeviceInfoBySerialNo(sn);
		if (null == deviceInfo) {
			throw new BusinessCommonException(CardErrorCode.DEVICE_INFO_NOT_FOUND);
		}

		val cardRelationRepository = SpringUtils.getBean(card.getBeanName(), ICardRelationRepository.class);
		List<CardShopRelation> relationList = cardRelationRepository.getCardShopRelation(card.getCardId());
		for (CardShopRelation relation:relationList) {
			val isChildResponse = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_SHOP.getCode(), deviceInfo.getStoreId(), relation.getStoreId());
			if(null != isChildResponse && null != isChildResponse.getResult() && isChildResponse.getResult()) {
				return;
			}

			val isChildResponse2 = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_ORG.getCode(), deviceInfo.getOrganizationId(), relation.getStoreId());
			if(null != isChildResponse2 && null != isChildResponse2.getResult() && isChildResponse2.getResult()) {
				return;
			}
		}
		throw new BusinessCommonException(CardErrorCode.CARD_CAN_NOT_BE_USED_IN_THIS_SHOP);
	}

	public void validateCard(Card card, int orgId, int storeId) {
		val cardRelationRepository = SpringUtils.getBean(card.getBeanName(), ICardRelationRepository.class);
		List<CardShopRelation> relationList = cardRelationRepository.getCardShopRelation(card.getCardId());
		for (CardShopRelation relation:relationList) {
			val isChildResponse = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_SHOP.getCode(), storeId, relation.getStoreId());
			if(null != isChildResponse && null != isChildResponse.getResult() && isChildResponse.getResult()) {
				return;
			}

			val isChildResponse2 = remoteOrgService.getIsChild(EntityTableConstant.ENTITY_TABLE_ORG.getCode(), orgId, relation.getStoreId());
			if(null != isChildResponse2 && null != isChildResponse2.getResult() && isChildResponse2.getResult()) {
				return;
			}
		}
		throw new BusinessCommonException(CardErrorCode.CARD_CAN_NOT_BE_USED_IN_THIS_SHOP);
	}

	public Integer updateCard(Card originCard, UpdateCardInfoRequest request) {
    	/* 卡唯一性验证: 同一个组织中，cardNum和cardAnotherId两个参数唯一*/
		val cardRelationRepository = SpringUtils.getBean(originCard.getBeanName(), ICardRelationRepository.class);
		List<CardShopRelation> relations = cardRelationRepository.getCardShopRelation(request.getCardId());
		Card newCard = originCard;
		BeanUtils.copyProperties(newCard, originCard);
		if(!StringUtils.isEmpty(request.getCardNum())) {
			newCard.setCardNum(request.getCardNum());
			verifyCardNumUniqueness(newCard, relations);
		}
		if(!StringUtils.isEmpty(request.getCardAnotherId())) {
			newCard.setCardAnotherId(request.getCardAnotherId());
			verifyCardAnotherIdUniqueness(newCard, relations);
		}
		if(!StringUtils.isEmpty(request.getCardOtherId())) {
			newCard.setCardOtherId(request.getCardOtherId());
		}
		return cardMapper.updateByPrimaryKeySelective(newCard);
	}
	
}
