/*
 * Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
 *
 * https://www.gz-yami.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */

package com.yami.shop.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.app.dto.ShopHeadInfoDto;
import com.yami.shop.bean.model.*;
import com.yami.shop.bean.param.ShopDetailParam;
import com.yami.shop.bean.enums.AuditStatus;
import com.yami.shop.bean.enums.OfflineHandleEventStatus;
import com.yami.shop.bean.enums.OfflineHandleEventType;
import com.yami.shop.bean.enums.ShopStatus;
import com.yami.shop.bean.event.OpenShopInitEvent;
import com.yami.shop.bean.param.OfflineHandleEventAuditParam;
import com.yami.shop.common.exception.GlobalException;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.dao.ProductMapper;
import com.yami.shop.dao.ShopDetailMapper;
import com.yami.shop.dao.ShopWalletMapper;
import com.yami.shop.dao.UserAddrMapper;
import com.yami.shop.service.OfflineHandleEventService;
import com.yami.shop.service.ShopAuditingService;
import com.yami.shop.service.ShopDetailService;
import com.yami.shop.service.ShopWalletService;
import lombok.AllArgsConstructor;
import ma.glasnost.orika.MapperFacade;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @author lgh on 2018/08/29.
 */
@Service
@AllArgsConstructor
public class ShopDetailServiceImpl extends ServiceImpl<ShopDetailMapper, ShopDetail> implements ShopDetailService {

    private final ShopDetailMapper shopDetailMapper;

    private final ShopAuditingService shopAuditingService;

    private final MapperFacade mapperFacade;

    private final ShopWalletMapper shopWalletMapper;

    private final OfflineHandleEventService offlineHandleEventService;

    private final ProductMapper productMapper;

    private final ApplicationContext applicationContext;

    private final PasswordEncoder passwordEncoder;
    private final UserAddrMapper userAddrMapper;

    @Resource
    private ShopWalletService shopWalletService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "shop_detail", key = "#shopDetail.shopId")
    public void updateShopDetail(ShopDetail shopDetail, ShopDetail dbShopDetail) {
        // 使用saveOrUpdate可以只更新部分字段
        saveOrUpdate(shopDetail);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "shop_detail", key = "#shopId")
    public void deleteShopDetailByShopId(Long shopId) {
        // 先删除数据库中的信息，再删除图片
        shopDetailMapper.deleteById(shopId);
    }

    @Override
    public ShopDetail getShopDetailByShopId(Long shopId) {
        return shopDetailMapper.selectShopDetailById(shopId);
    }

    @Override
    @CacheEvict(cacheNames = "shop_detail", key = "#shopId")
    public void removeShopDetailCacheByShopId(Long shopId) {
    }

    @Override
    public ShopDetail getShopByMobile(String mobile) {
        return shopDetailMapper.getShopByMobile(mobile);
    }

    @Override
    public void updatePasswordByUserName(String username, String newPassword) {
        shopDetailMapper.updatePasswordByUserName(username, newPassword);
    }

    @Override
    public ShopDetail getShopDetailByUserId(String userId) {
        return shopDetailMapper.getShopDetailByUserId(userId);
    }

    /**
     * 申请开店
     *
     * @param userId
     * @param shopDetailParam
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applyShop(String userId, ShopDetailParam shopDetailParam) {

        ShopDetail shopDetail = getShopDetailByUserId(userId);
        if (shopDetail != null && !Objects.equals(shopDetail.getShopStatus(), ShopStatus.NOTOPEN.value())) {
            throw new GlobalException("您已经申请开启店铺,不能重新申请");
        }
        ShopDetail dbShopnameShop = shopDetailMapper.selectOne(new LambdaQueryWrapper<ShopDetail>().eq(ShopDetail::getShopName, shopDetailParam.getShopName()).ne(ShopDetail::getUserId, userId));
        if (dbShopnameShop != null) {
            throw new GlobalException("店铺名称已存在");
        }
        ShopDetail newShopDetail = mapperFacade.map(shopDetailParam, ShopDetail.class);

        Date now = new Date();
        // 如果是第一次申请开店
        if (shopDetail == null) {
            newShopDetail.setUserId(userId);
            newShopDetail.setCreateTime(now);
            newShopDetail.setShopOwner(userId);
            newShopDetail.setShopStatus(ShopStatus.NOTOPEN.value());
            newShopDetail.setIsDistribution(1);
        } else {
            newShopDetail.setShopId(shopDetail.getShopId());
        }


        newShopDetail.setUpdateTime(now);
        // 保存或更新
        saveOrUpdate(newShopDetail);
        //清除缓存
        removeShopDetailCacheByShopId(newShopDetail.getShopId());

        // 插入一条审核记录
        ShopAuditing auditing = shopAuditingService.getShopAuditingByUserId(userId);
        if (auditing == null) {
            auditing = new ShopAuditing();
            auditing.setShopId(newShopDetail.getShopId());
            auditing.setUserId(userId);
            auditing.setCreateTime(new Date());
        }
        auditing.setStatus(AuditStatus.WAITAUDIT.value());
        auditing.setUpdateTime(new Date());
        shopAuditingService.saveOrUpdate(auditing);
    }

    /**
     * 审核商家信息
     *
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean audit(ShopAuditing shopAuditing) {
        shopAuditingService.updateById(shopAuditing);
        if (!Objects.equals(shopAuditing.getStatus(), AuditStatus.SUCCESSAUDIT.value())) {
            return true;
        }

        //修改店铺状态
        ShopDetail shopDetail = getShopDetailByShopId(shopAuditing.getShopId());
        if (shopDetail == null) {
            throw new GlobalException("店铺信息不存在");
        }
        shopDetail.setShopStatus(ShopStatus.OPEN.value());
        updateById(shopDetail);

        // 添加店铺钱包
        ShopWallet exist = shopWalletService.getOne(new LambdaQueryWrapper<ShopWallet>()
                .eq(ShopWallet::getShopId, shopAuditing.getShopId()));
        if (Objects.isNull(exist)) {
            ShopWallet shopWallet = new ShopWallet();
            shopWallet.setVersion(0L);
            shopWallet.setUnsettledAmount(0.0);
            shopWallet.setSettledAmount(0.0);
            shopWallet.setFreezeAmount(0.0);
            shopWallet.setTotalSettledAmount(0.0);
            shopWallet.setShopId(shopAuditing.getShopId());
            shopWallet.setUpdateTime(new Date());
            shopWalletMapper.insert(shopWallet);
        }

        // 新店初始化事件
        applicationContext.publishEvent(new OpenShopInitEvent(shopDetail));
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "shop_detail", key = "#shopDetail.shopId")
    public void offline(ShopDetail shopDetail, String offlineReason, Long sysUserId) {
        // 添加下线处理记录
        Date now = new Date();
        OfflineHandleEvent offlineHandleEvent = new OfflineHandleEvent();
        offlineHandleEvent.setHandleId(shopDetail.getShopId());
        offlineHandleEvent.setHandleType(OfflineHandleEventType.SHOP.getValue());
        offlineHandleEvent.setCreateTime(now);
        offlineHandleEvent.setOfflineReason(offlineReason);
        offlineHandleEvent.setHandlerId(sysUserId);
        offlineHandleEvent.setShopId(shopDetail.getShopId());
        offlineHandleEvent.setStatus(OfflineHandleEventStatus.OFFLINE_BY_PLATFORM.getValue());
        offlineHandleEvent.setUpdateTime(now);
        offlineHandleEventService.save(offlineHandleEvent);

        // 更新店铺状态为下线
        shopDetailMapper.updateStatus(shopDetail.getShopId(), ShopStatus.OFFLINE.value());

        // 将该店铺所有的商品下线状态
        productMapper.offlineProdByShopId(shopDetail.getShopId());

    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "shop_detail", key = "#offlineHandleEventAuditParam.handleId")
    public void auditOfflineShop(OfflineHandleEventAuditParam offlineHandleEventAuditParam, Long sysUserId) {
        // 审核通过
        if (Objects.equals(offlineHandleEventAuditParam.getStatus(), OfflineHandleEventStatus.AGREE_BY_PLATFORM.getValue())) {
            // 更新店铺为营业中状态
            shopDetailMapper.updateStatus(offlineHandleEventAuditParam.getHandleId(), ShopStatus.OPEN.value());
        }
        // 审核不通过
        else if (Objects.equals(offlineHandleEventAuditParam.getStatus(), OfflineHandleEventStatus.DISAGREE_BY_PLATFORM.getValue())) {
            shopDetailMapper.updateStatus(offlineHandleEventAuditParam.getHandleId(), ShopStatus.OFFLINE.value());
        }
        offlineHandleEventService.auditOfflineEvent(offlineHandleEventAuditParam, sysUserId);

    }

    @Override
    public List<ShopHeadInfoDto> listHotShopsHead() {
        return shopDetailMapper.listHotShopsHead();
    }

    @Override
    public Page<ShopHeadInfoDto> searchShops(PageParam<ShopHeadInfoDto> page, ShopHeadInfoDto shopHeadInfoDto) {
        return shopDetailMapper.searchShops(page, shopHeadInfoDto);
    }

    @Override
    public void insertDetail(ShopDetail shopDetail) {
        Date date = new Date();
        shopDetail.setShopStatus(1);
        shopDetail.setIsDistribution(1);
        shopDetail.setCreateTime(date);
        shopDetail.setUpdateTime(date);
        shopDetail.setPassword(passwordEncoder.encode(shopDetail.getPassword()));
        if (this.checkMobile(shopDetail.getMobile(), null) > 0) {
            throw new GlobalException("该账号已存在，请重新输入");
        }
        shopDetailMapper.insert(shopDetail);
        //创建店铺钱包
        ShopWallet shopWallet = new ShopWallet();
        shopWallet.setShopId(shopDetail.getShopId());
        shopWallet.setUnsettledAmount(0.0);
        shopWallet.setSettledAmount(0.0);
        shopWallet.setFreezeAmount(0.0);
        shopWallet.setTotalSettledAmount(0.0);
        shopWallet.setVersion(0L);
        shopWallet.setUpdateTime(new Date());
        shopWalletMapper.insert(shopWallet);
    }

    @Override
    public int checkMobile(String mobile, Long shopId) {
        return count(new LambdaQueryWrapper<ShopDetail>()
                .eq(ShopDetail::getMobile, mobile)
                .ne(Objects.nonNull(shopId), ShopDetail::getShopId, shopId)
        );
    }

    @Override
    @CacheEvict(cacheNames = "shop_detail", key = "#shopId")
    public void updatePasswordOrMobile(Long shopId, String password, String mobile) {
        ShopDetail shopDetail = new ShopDetail();
        shopDetail.setShopId(shopId);
        shopDetail.setPassword(passwordEncoder.encode(password));
        if (Objects.nonNull(mobile)) {
            if (this.checkMobile(shopDetail.getMobile(), shopId) > 0) {
                throw new GlobalException("该账号已存在，请重新输入");
            }
            shopDetail.setMobile(mobile);
        }
        shopDetailMapper.updateById(shopDetail);
    }

    @Override
    public ResponseEntity<ShopHeadInfoDto> neighborShop(String lat, String lon) {
        List<ShopDetail> shopDetails = shopDetailMapper.neighborShop(lat, lon, null);
        ShopHeadInfoDto dto = new ShopHeadInfoDto();
        dto.setShopId(shopDetails.get(0).getShopId());
        dto.setShopName(shopDetails.get(0).getShopName());
        return ResponseEntity.ok(dto);
    }

    @Override
    public ResponseEntity<ShopHeadInfoDto> neighborShop(String lat, String lon, Long channelId) {
        List<ShopDetail> shopDetails = shopDetailMapper.neighborShop(lat, lon, channelId);
        ShopHeadInfoDto dto = new ShopHeadInfoDto();
        if (ObjectUtils.isNotEmpty(shopDetails)) {
            dto.setShopId(shopDetails.get(0).getShopId());
            dto.setShopName(shopDetails.get(0).getShopName());
            dto.setLat(shopDetails.get(0).getShopLat());
            dto.setLon(shopDetails.get(0).getShopLng());
        }
        return ResponseEntity.ok(dto);
    }

    @Override
    public ResponseEntity<List<ShopHeadInfoDto>> neighborShopList(String lat, String lon, Long channelId) {
        List<ShopHeadInfoDto> collect = shopDetailMapper.neighborShop(lat, lon, channelId)
                .stream().map((s) -> ShopHeadInfoDto.builder()
                        .shopId(s.getShopId())
                        .shopName(s.getShopName())
                        .lat(s.getShopLat())
                        .lon(s.getShopLng())
                        .hasProduct(s.getHasProduct())
                        .build()).collect(Collectors.toList());
        return ResponseEntity.ok(collect);
    }

    @Override
    public ResponseEntity<List<ShopHeadInfoDto>> neighborShopByAddr(Long addr, Long channelId) {
        UserAddr userAddr = userAddrMapper.selectById(addr);
        List<ShopHeadInfoDto> collect = shopDetailMapper.neighborShop(userAddr.getLatitude().toString(), userAddr.getLongitude().toString(), channelId)
                .stream().map((s) -> ShopHeadInfoDto.builder()
                        .shopId(s.getShopId())
                        .shopName(s.getShopName())
                        .lat(s.getShopLat())
                        .lon(s.getShopLng())
                        .hasProduct(s.getHasProduct())
                        .build()).collect(Collectors.toList());
        return ResponseEntity.ok(collect);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "shop_detail", key = "#shopId")
    public void auditApply(Long eventId, Long shopId, String reapplyReason) {
        // 更新活动为待审核状态
        shopDetailMapper.updateStatus(shopId, ShopStatus.OFFLINE_AUDIT.value());

        // 更新事件状态
        offlineHandleEventService.updateToApply(eventId, reapplyReason);
    }

}
