package com.hai.service.impl;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.hai.common.exception.ErrorCode;
import com.hai.common.exception.ErrorHelp;
import com.hai.common.exception.SysCode;
import com.hai.common.utils.HttpsUtils;
import com.hai.dao.*;
import com.hai.entity.*;
import com.hai.model.IndexCountModel;
import com.hai.model.LineCountModel;
import com.hai.service.CommonService;
import com.hai.service.HighOrderService;
import com.hai.service.HighUserService;
import com.hai.service.SecConfigService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;


@Service("iCommonService")
public class CommonServiceImpl implements CommonService {

	private long READ_STEP = 30*60*1000; //半小时刷新一次缓存的字典数据
	
	private ReentrantLock lock = new ReentrantLock();
	
	private ReentrantLock regionLock = new ReentrantLock();
	
	private long lastReadTime = 0;
	
	private long regionLastReadTime = 0;
	
	@Resource
	private SecRegionMapper regionMapper;
	
	@Resource
	private SecDictionaryMapper dicMapper;

	@Resource
	private SecConfigService secConfigService;

	@Resource
	private HighUserService highUserService;

    @Resource
    private HighOrderService highOrderService;

    @Resource
    private BsProductPlatformMapper bsProductPlatformMapper;

    @Resource
    private BsProductDiscountMapper bsProductDiscountMapper;

    @Resource
    private BsProductPayTypeMapper bsProductPayTypeMapper;

    private Map<String, Map<String, SecDictionary>> dicCache = new HashMap<String, Map<String, SecDictionary>>();

    private List<SecRegion> citiesCache = new ArrayList<>();

    private Map<Long, List<SecRegion>> regionsCache = new HashMap<>();

	private Map<Long,List<SecRegion>> streetCache = new HashMap<>();

	private Map<Long,List<SecRegion>> communityCache = new HashMap<>();
	
	private Map<Long, SecRegion> singleRegionCache = new HashMap<>();
	
	@Override
	public Map<String,Map<String, SecDictionary>> getDictionaries() {
		refreshDic();
		return dicCache;
	}

	/**
	 * @param codeType
	 * @param codeValue
	 * @throws
	 * @Title: getDictionary
	 * @Description: 精确查找数据字典值
	 * @author: 机器猫
	 * @param: @param codeType
	 * @param: @param codeValue
	 * @param: @return
	 * @param: @throws Exception
	 * @return: SysDictionary
	 */
	@Override
	public String getDictionaryCodeName(String codeType, String codeValue) {
		refreshDic();
		if(StringUtils.isBlank(codeType)){
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "");
		}
		Map<String, SecDictionary> m = dicCache.get(codeType);
		if (null == m) {
			return "";
		}
		SecDictionary sd = m.get(codeValue);
		if (null == sd) {
			return "";
		}
		return sd.getCodeName();
	}

	/**
	 * @param codeType
	 * @param codeValue
	 * @throws
	 * @Title: mappingSysCode
	 * @Description: 根据codetype，codevalue获取系统字典数据
	 * @author: 机器猫
	 * @param: @param codeType
	 * @param: @param codeValue
	 * @param: @return
	 * @param: @throws Exception
	 * @return: SysDictionary
	 */
	@Override
	public SecDictionary mappingSysCode(String codeType, String codeValue) {
		refreshDic();
		if(StringUtils.isBlank(codeType)){
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "");
		}
		Map<String, SecDictionary> m = dicCache.get(codeType);
		if (null == m) {
			return null;
		}
		SecDictionary sd = m.get(codeValue);
		return sd;
	}

	@Override
	public SecDictionary mappingSysName(String codeType, String codeName) {
		refreshDic();
		if(StringUtils.isBlank(codeType)){
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "");
		}
		Map<String, SecDictionary> m = dicCache.get(codeType);
		if (null == m) {
			return null;
		}
		for(Map.Entry<String, SecDictionary> entry : m.entrySet()){
			String mapKey = entry.getKey();
			SecDictionary mapValue = entry.getValue();
			if (mapValue.getCodeName().equals(codeName)) {
				return mapValue;
			}
		}
		return null;
	}

	/**
	 * 根据codeType获取该类的所有字典数据
	 *
	 * @param codeType
	 * @return
	 */
	@Override
	public List<SecDictionary> getDictionarys(String codeType) {
		refreshDic();
		if (StringUtils.isEmpty(codeType)) {
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "codeType is null");
		}
		Map<String, SecDictionary> maps = dicCache.get(codeType);
		if (null == maps) {
			return new ArrayList<>();
		}
		List<SecDictionary> rtn = new ArrayList<>();
		rtn.addAll(maps.values());
		return rtn;
	}

	@Override
	public List<SecDictionary> getDictionarysAndExt(String codeType, String ext1) {
		refreshDic();
		if (StringUtils.isEmpty(codeType)) {
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "codeType is null");
		}
		Map<String, SecDictionary> maps = dicCache.get(codeType);
		if (null == maps) {
			return new ArrayList<>();
		}
		List<SecDictionary> rtn = new ArrayList<>();
        for(SecDictionary dictionary : maps.values()){
            if (ext1.equals(dictionary.getExt1())) {
                rtn.add(dictionary);
            }
        }
		return rtn;
	}

	@Override
	public List<JSONObject> getIdAndName(String codeType) {
		refreshDic();
		if (StringUtils.isEmpty(codeType)) {
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "codeType is null");
		}
		Map<String, SecDictionary> maps = dicCache.get(codeType);
		if (null == maps) {
			return new ArrayList<>();
		}
		List<JSONObject> rtn = new ArrayList<>();
		for(SecDictionary dictionary : maps.values()){
			JSONObject jo = new JSONObject();
			jo.put("codeValue",dictionary.getCodeValue());
			jo.put("codeName",dictionary.getCodeName());
			rtn.add(jo);
		}
		return rtn;
	}

	private void getDicFormDB(){
		SecDictionaryExample example = new SecDictionaryExample();
		example.createCriteria().andStatusEqualTo(1);
		example.setOrderByClause(" sort_id asc");
		List<SecDictionary> dicList = dicMapper.selectByExample(example);
		for(SecDictionary dic : dicList){
			if(dicCache.get(dic.getCodeType()) != null){
				Map<String, SecDictionary> typeList = dicCache.get(dic.getCodeType());
				typeList.put(dic.getCodeValue(), dic);
			}else{
				Map<String, SecDictionary> temp = new HashMap<String, SecDictionary>();
				temp.put(dic.getCodeValue(), dic);
				dicCache.put(dic.getCodeType(), temp);
			}
		}
		lastReadTime = System.currentTimeMillis();
	}
	
	private void refreshDic() {
	    if (System.currentTimeMillis() - lastReadTime < READ_STEP) {
	        return;
        }
        if (lock.isLocked()) { //说明有线程已经在刷数据了
            if (null != dicCache && dicCache.size() > 0) { //如果有数据说明已经初始化过了，直接返回老数据
                return;
            }
            while (null == dicCache || dicCache.size()<1) { //说明初始化刷数据 等待10毫秒
                try {
                    Thread.sleep(10);
                    //此处是否需要添加一个超时机制？
                } catch (InterruptedException ie) {
                    //忽略
                }
            }
            return;
        }
        try {
        	lock.lock();
            if (System.currentTimeMillis() - lastReadTime < READ_STEP) {
                return;
            }
            getDicFormDB();
        } finally {
            if (lock.isLocked()) {
            	lock.unlock();
            }
        }
    }

	@Override
	public List<SecRegion> getCities() {
		refreshRegion();
		return citiesCache;
	}
	private void refreshRegion(){
		if (System.currentTimeMillis() - regionLastReadTime < READ_STEP) {
	        return;
        }
        if (regionLock.isLocked()) { //说明有线程已经在刷数据了
            if (null != dicCache && dicCache.size() > 0) { //如果有数据说明已经初始化过了，直接返回老数据
                return;
            }
            while (null == dicCache || dicCache.size()<1) { //说明初始化刷数据 等待10毫秒
                try {
                    Thread.sleep(10);
                    //此处是否需要添加一个超时机制？
                } catch (InterruptedException ie) {
                    //忽略
                }
            }
            return;
        }
        try {
        	regionLock.lock();
            if (System.currentTimeMillis() - regionLastReadTime < READ_STEP) {
                return;
            }
            getRegionFromDB();
        } finally {
            if (regionLock.isLocked()) {
            	regionLock.unlock();
            }
        }
	}
	private void getRegionFromDB(){
		SecRegionExample example = new SecRegionExample();
		example.createCriteria().andStatusEqualTo(1).andParentIdIsNull();
		citiesCache = regionMapper.selectByExample(example);
		for(SecRegion city : citiesCache){//市
			singleRegionCache.put(city.getRegionId(), city);
			List<SecRegion> regions = getRegions(city.getRegionId());
			regionsCache.put(city.getRegionId(),regions);
			for(SecRegion region : regions){//区
				singleRegionCache.put(region.getRegionId(), region);
				List<SecRegion> streets = getRegions(region.getRegionId());
				streetCache.put(region.getRegionId(), streets);
				for(SecRegion street : streets){
					singleRegionCache.put(street.getRegionId(), street);
					List<SecRegion> communities = getRegions(street.getRegionId());
					communityCache.put(street.getRegionId(),communities);
					for (SecRegion community : communities){
						singleRegionCache.put(community.getRegionId(),community);
					}
				}
			}
		}
		regionLastReadTime = System.currentTimeMillis();
	}

	private List<SecRegion> getRegions(Long parentId) {
		SecRegionExample example = new SecRegionExample();
		example.createCriteria().andStatusEqualTo(1).andParentIdEqualTo(parentId);
		return regionMapper.selectByExample(example);
	}
	@Override
	public List<SecRegion> getRegionsByParentId(Long parentId) {
		if(parentId == null){
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR);
		}
		refreshRegion();
		if(null != regionsCache.get(parentId)){
			return regionsCache.get(parentId);
		}else if(null != streetCache.get(parentId)){
			return streetCache.get(parentId);
		}else if(null != communityCache.get(parentId)){
			return communityCache.get(parentId);
		}else{
			return null;
		}
	}

	@Override
	public Map<String, SecRegion> getParentInfoByRegionId(Long regionId) {
		if(regionId == null){
			throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR);
		}
		refreshRegion();
		Map<String, SecRegion> map = new HashMap<String, SecRegion>();
		Stack<SecRegion> regionStack = getRegionStack(regionId,new Stack<>());
		if (regionStack.size() == 4){
			map.put("city",regionStack.pop());
			map.put("region",regionStack.pop());
			map.put("street",regionStack.pop());
			map.put("community",regionStack.pop());
		}else if (regionStack.size() == 3){
			map.put("city",regionStack.pop());
			map.put("region",regionStack.pop());
			map.put("street",regionStack.pop());
		}else if (regionStack.size() == 2){
			map.put("city",regionStack.pop());
			map.put("region",regionStack.pop());
		}else if (regionStack.size() == 1){
			map.put("city",regionStack.pop());
		}
		return map;
	}

	@Override
	public String getRegionName(Long regionId) {
		String regionName = "";
		Map<String, SecRegion> map = getParentInfoByRegionId(regionId);
		if (map.get("city") != null) {
			regionName += map.get("city").getRegionName();
		}
		if (map.get("region") != null) {
			regionName += map.get("region").getRegionName();
		}
		if (map.get("street") != null) {
			regionName += map.get("street").getRegionName();
		}
		if (map.get("community") != null) {
			regionName += map.get("community").getRegionName();
		}
		return regionName;
	}

	private Stack<SecRegion> getRegionStack(Long regionId, Stack<SecRegion> regionStack){
		SecRegion region = singleRegionCache.get(regionId);
		if (region != null){
			regionStack.push(region);
			getRegionStack(region.getParentId(),regionStack);
		}
		return regionStack;
	}

    @Override
    public SecRegion getRegionsById(Long regionId) {
        return regionMapper.selectByPrimaryKey(regionId);
    }

	@Override
	public List<SecRegion> getProvinceList() {
		SecRegionExample example = new SecRegionExample();
		example.createCriteria().andParentIdIsNull().andStatusEqualTo(1);
		return regionMapper.selectByExample(example);
	}

	@Override
	public SecRegion getRegionsByName(String regionName) {
		SecRegionExample example = new SecRegionExample();
		example.createCriteria().andRegionNameLike("%" + regionName + "%");
		List<SecRegion> list = regionMapper.selectByExample(example);
		if (list.size() > 0) {
			return list.get(0);
		}
		return null;
	}

    @Override
    public List<SecRegion> findByName(String name) {
        SecRegionExample example = new SecRegionExample();
        example.createCriteria().andStatusEqualTo(1).andRegionNameEqualTo(name);
        return regionMapper.selectByExample(example);
    }

    @Override
    public SecRegion getParentByRegion(Long regionId) {
        SecRegion secRegion = getRegionsById(regionId);
        if (secRegion != null && secRegion.getParentId() != null) {
            while (true) {
                secRegion = getRegionsById(secRegion.getParentId());
                if (secRegion.getParentId() == null) {
                    return secRegion;
                }
            }
        }
        return secRegion;
    }

	@Override
	public Boolean findValue(String codeType, String codeValue) {
		SecDictionaryExample example = new SecDictionaryExample();
		example.createCriteria().andCodeTypeEqualTo(codeType).andCodeValueEqualTo(codeValue);

		List<SecDictionary> list = dicMapper.selectByExample(example);

		return list.size() > 0;
	}

	@Override
	public List<SecDictionary> mappingSysNameOl(String codeType) {
		SecDictionaryExample example = new SecDictionaryExample();
		example.createCriteria().andCodeTypeEqualTo(codeType);
		return dicMapper.selectByExample(example);
	}

	@Override
	public void updateDictionary(SecDictionary secDictionary) {
		dicMapper.updateByPrimaryKeySelective(secDictionary);
	}

	@Override
	public BigDecimal getHLTBalance(String codeType) {

		SecConfig secConfig = secConfigService.findByCodeType(codeType);
		return new BigDecimal(secConfig.getCodeValue());
	}


	@Override
	public void addHLTBalance(String codeType , BigDecimal price) {
		SecConfig secConfig = secConfigService.findByCodeType(codeType);

		BigDecimal balance = new BigDecimal(secConfig.getCodeValue());

		secConfig.setCodeValue(String.valueOf(price.add(balance)));

		secConfigService.updateSecConfig(secConfig);
	}

	@Override
	public IndexCountModel getIndexCount() {

		Map<String , Object> map = new HashMap<>();
		Map<String , Object> mapToDay = new HashMap<>();

		mapToDay.put("today" , "1");

		IndexCountModel countModel = new IndexCountModel();

		// 获取注册总人数
		countModel.setRegistrantNum(highUserService.countUser());
		// 获取订单总金额
		countModel.setTransactionNum(highOrderService.orderPriceTotal(map).add(highOrderService.rechargePriceTotal(map)));
		// 获取当天订单总金额
		countModel.setTransactionToday(highOrderService.orderPriceTotal(mapToDay).add(highOrderService.rechargePriceTotal(mapToDay)));
		// 获取订单数量
		countModel.setTransactionOrder(highOrderService.orderPriceCount(map) + highOrderService.rechargePriceCount(map));
		//获取订单当天数量
		countModel.setTransactionOrderToday(highOrderService.orderPriceCount(mapToDay) + highOrderService.rechargePriceCount(mapToDay));
		return countModel;
	}

	@Override
	public LineCountModel getLineCount() {

		LineCountModel lineCount = new LineCountModel();

		lineCount.setOrderPriceSum(highOrderService.getOrderSumOrderByDate());
		lineCount.setOrderRechargePriceSum(highOrderService.getOrderSumRechargeByDate());
		lineCount.setOrderCount(highOrderService.getDateCountByOrder());
		lineCount.setOrderRechargeCount(highOrderService.getDateCountByRecharge());
		lineCount.setUserCount(highOrderService.getDateCountByUser());
		return lineCount;
	}

    @Override
    public JSONObject findByLatAndLng(String lng, String lat) throws Exception {

        Map<String,Object> map = new HashMap<>();//参数
        map.put("ak", "SfrwGH7INvjPq7BwCrYrioBQZm9XXxrR");//申请百度开放平台KEY(ak)
        map.put("output", "json");// 输出Json数据
        map.put("extensions_town", "true");// 行政区划返回乡镇级数据（town），仅国内召回乡镇数据
        map.put("coordtype", "wgs84ll"); // GPS 经纬度类型
        map.put("location", lat+","+lng+"");//百度经纬度

        String url = "http://api.map.baidu.com/reverse_geocoding/v3/";

        System.out.println("请求经纬度========" + map);

        return HttpsUtils.doGet(url , map);

    }

    @Override
    @Transactional(propagation= Propagation.REQUIRES_NEW)
    public void configPayType(JSONObject object)  {
        // 删除原支付方式
        deletePayType(object.getLong("sourceId"));
        // 支付方式
        BsProductPayType productPayType;
        // 配置支付方式
        JSONArray payTypeArray = object.getJSONArray("payType");
        for (Object payType : payTypeArray) {

            SecDictionary dictionary = mappingSysCode("PAY_TYPE", String.valueOf(payType));
            if (dictionary != null) {
                productPayType = new BsProductPayType();
                productPayType.setCreateTime(new Date());
                productPayType.setUpdateTime(new Date());
                productPayType.setPayTypeId(Integer.valueOf(String.valueOf(payType)));
                productPayType.setPayTypeLogo(dictionary.getExt1());
                productPayType.setPayTypeName(dictionary.getCodeName());
                productPayType.setProductType(object.getInteger("productType"));
                productPayType.setOperatorId(object.getLong("operatorId"));
                productPayType.setOperatorName(object.getString("operatorName"));
                productPayType.setSourceId(object.getLong("sourceId"));
                bsProductPayTypeMapper.insert(productPayType);
            }
        }
    }

    @Override
    @Transactional(propagation= Propagation.REQUIRES_NEW)
    public void configIntegralDiscount(JSONObject object) {
        // 清空原积分抵扣比例
        deleteIntegralDiscount(object.getLong("sourceId"));
        // 折扣比例
        BsProductDiscount productDiscount;
        // 配置积分折扣比例
        productDiscount = new BsProductDiscount();
        productDiscount.setDiscount(object.getBigDecimal("integralDiscount"));
        productDiscount.setProductType(object.getInteger("productType"));
        productDiscount.setCreateTime(new Date());
        productDiscount.setUpdateTime(new Date());
        productDiscount.setOperatorId(object.getLong("operatorId"));
        productDiscount.setOperatorName(object.getString("operatorName"));
        productDiscount.setSourceId(object.getLong("sourceId"));
        bsProductDiscountMapper.insert(productDiscount);

    }

    @Override
    @Transactional(propagation= Propagation.REQUIRES_NEW)
    public void configPlatform(JSONObject object) {

        deletePlatform(object.getLong("sourceId"));

        BsProductPlatform productPlatform;
        // 配置展示平台
        JSONArray productPlatformArray = object.getJSONArray("productPlatform");
        for (Object platform : productPlatformArray) {
            productPlatform = new BsProductPlatform();
            SecDictionary dictionary = mappingSysCode("SHOW_PLATFORM", String.valueOf(platform));
            productPlatform.setProductType(object.getInteger("productType"));
            productPlatform.setPlatformId(Integer.valueOf(String.valueOf(platform)));
            productPlatform.setPlatformName(dictionary.getCodeName());
            productPlatform.setCreateTime(new Date());
            productPlatform.setUpdateTime(new Date());
            productPlatform.setOperatorId(object.getLong("operatorId"));
            productPlatform.setOperatorName(object.getString("operatorName"));
            productPlatform.setSourceId(object.getLong("sourceId"));

            bsProductPlatformMapper.insert(productPlatform);
        }
    }

    // 清空支付方式配置
    private void deletePayType(Long sourceId) {

        if (sourceId == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "");
        }

        BsProductPayTypeExample example = new BsProductPayTypeExample();
        example.createCriteria().andSourceIdEqualTo(sourceId);

        List<BsProductPayType> list = bsProductPayTypeMapper.selectByExample(example);

        if (list.size() > 0) {
            for (BsProductPayType productPayType : list) {
                bsProductPayTypeMapper.deleteByPrimaryKey(productPayType.getId());
            }
        }

    }

    // 清空配置积分抵扣比例
    private void deleteIntegralDiscount(Long sourceId) {

        if (sourceId == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "");
        }

        BsProductDiscountExample example = new BsProductDiscountExample();
        example.createCriteria().andSourceIdEqualTo(sourceId);

        List<BsProductDiscount> list = bsProductDiscountMapper.selectByExample(example);

        if (list.size() > 0) {
            for (BsProductDiscount bsProductDiscount : list) {
                bsProductDiscountMapper.deleteByPrimaryKey(bsProductDiscount.getId());
            }
        }

    }

    // 清空配置展示平台
    private void deletePlatform(Long sourceId) {

        if (sourceId == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.REQ_PARAMS_ERROR, "");
        }

        BsProductPlatformExample example = new BsProductPlatformExample();
        example.createCriteria().andSourceIdEqualTo(sourceId);

        List<BsProductPlatform> list = bsProductPlatformMapper.selectByExample(example);

        if (list.size() > 0) {
            for (BsProductPlatform productPlatform : list) {
                bsProductPlatformMapper.deleteByPrimaryKey(productPlatform.getId());
            }
        }

    }
}
