package com.czg.service.account.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.czg.account.dto.PageDTO;
import com.czg.account.dto.shopinfo.*;
import com.czg.account.entity.*;
import com.czg.account.enums.BranchDataSyncMethodEnum;
import com.czg.account.enums.ShopTypeEnum;
import com.czg.account.service.*;
import com.czg.config.RedisCst;
import com.czg.enums.StatusEnum;
import com.czg.enums.YesNoEnum;
import com.czg.exception.ApiNotPrintException;
import com.czg.exception.CzgException;
import com.czg.resp.CzgResult;
import com.czg.sa.MyStpLogic;
import com.czg.sa.StpKit;
import com.czg.service.RedisService;
import com.czg.service.account.mapper.ShopInfoMapper;
import com.czg.service.account.mapper.ShopUserMapper;
import com.czg.system.dto.SysParamsDTO;
import com.czg.system.service.SysParamsService;
import com.czg.utils.AssertUtil;
import com.czg.utils.GeoUtil;
import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author Administrator
 */
@Slf4j
@DubboService
@CacheConfig(cacheNames = "shopInfo")
public class ShopInfoServiceImpl extends ServiceImpl<ShopInfoMapper, ShopInfo> implements ShopInfoService {
    @Resource
    private SysUserService sysUserService;
    @Resource
    private MerchantRegisterService merchantRegisterService;
    @Resource
    private ShopTableService shopTableService;
    @Resource
    private ShopUserMapper shopUserMapper;
    @Resource
    private ShopExtendService shopExtendService;
    @Resource
    private RedisService redisService;
    @Resource
    private ShopPayTypeService shopPayTypeService;
    @Resource
    private FreeDineConfigService freeDineConfigService;
    @Resource
    private PointsBasicSettingService pointsBasicSettingService;
    @Resource
    private ShopConfigService shopConfigService;

    @DubboReference
    private SysParamsService sysParamsService;

    @Override
    @Cacheable(key = "#id")
    public ShopInfo getById(Serializable id) {
        ShopInfo shopInfo = super.getById(id);
        ShopConfig shopConfig = shopConfigService.getById(shopInfo.getId());
        BeanUtil.copyProperties(shopConfig, shopInfo);
        return shopInfo;
    }

    private ShopInfo getShopInfo(Long shopId) {
        ShopInfo shopInfo = getById(shopId);
        if (shopInfo == null) {
            throw new ApiNotPrintException("店铺不存在");
        }
        if (shopInfo.getExpireTime() != null && (DateUtil.date().toLocalDateTime().isAfter(shopInfo.getExpireTime()))) {
            throw new ApiNotPrintException("店铺已过期，请联系商家");
        }
        if (StatusEnum.DISABLE.value() == shopInfo.getOnSale() || shopInfo.getStatus() != StatusEnum.ENABLED.value()) {
            throw new ApiNotPrintException("店铺已停业，请联系商家");
        }

        shopInfo.setBookingSms(null);
        shopInfo.setOperationPwd(null);
        shopInfo.setBindAccount(null);
        return shopInfo;
    }

    @Override
    public Page<ShopInfo> get(PageDTO pageDTO, String shopName, Integer status, Integer isHeadShop) {
        QueryWrapper queryWrapper = new QueryWrapper();
        if (StrUtil.isNotBlank(shopName)) {
            queryWrapper.like(ShopInfo::getShopName, shopName);
        }
        if (status != null) {
            queryWrapper.eq(ShopInfo::getStatus, status);
        }
        if (isHeadShop != null) {
            queryWrapper.eq(ShopInfo::getIsHeadShop, isHeadShop);
        }
        CzgResult<SysParamsDTO> shopOrderPayBaseUrl = sysParamsService.getParamsByCode("shop_order_pay_base_url");
        CzgResult<SysParamsDTO> shopOrderPayBaseUrl2 = sysParamsService.getParamsByCode("ali_account_app_id");

        List<ShopInfo> shopAllList = getMapper().selectListByQuery(query().select(ShopInfo::getId, ShopInfo::getShopName));
        Map<Long, String> shopKv = shopAllList.stream().collect(Collectors.toMap(ShopInfo::getId, ShopInfo::getShopName));
        queryWrapper.orderBy(ShopInfo::getCreateTime, false);
        Page<ShopInfo> page = page(new Page<>(pageDTO.page(), pageDTO.size()), queryWrapper);
        page.getRecords().forEach(shopInfo -> {
            ShopConfig shopConfig = shopConfigService.getById(shopInfo.getId());
            BeanUtil.copyProperties(shopConfig, shopInfo);
            shopInfo.setHeadShopName(shopKv.get(shopInfo.getMainId()));
        });
        return page;
    }

    private void activateShop(ShopInfo shopInfo, String activateCode) {
        MerchantRegister merchantRegister = merchantRegisterService.queryChain().eq(MerchantRegister::getRegisterCode, activateCode).one();
        AssertUtil.isNull(merchantRegister, "激活码不存在");
        if (merchantRegister.getStatus() == 1) {
            throw new CzgException("激活码已使用");
        }

        // 续期
        if (shopInfo.getExpireTime() != null && shopInfo.getExpireTime().isAfter(LocalDateTime.now())) {
            shopInfo.setExpireTime(DateUtil.offsetMonth(DateUtil.date(shopInfo.getExpireTime()), merchantRegister.getPeriodMonth()).toLocalDateTime());
        } else {
            shopInfo.setExpireTime(DateUtil.offsetMonth(DateUtil.date(), merchantRegister.getPeriodMonth()).toLocalDateTime());
        }
        merchantRegister.setStatus(1);
        merchantRegister.setShopId(shopInfo.getId());
        merchantRegisterService.updateById(merchantRegister);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(ShopInfoAddDTO shopInfoAddDTO) {
        // 如果店铺类型是单店，是否主店要设为否
        if (ShopTypeEnum.ONLY.getValue().equals(shopInfoAddDTO.getShopType())) {
            shopInfoAddDTO.setIsHeadShop(YesNoEnum.NO.value());
            if (shopInfoAddDTO.getMainId() != null) {
                throw new CzgException("单店不允许设置主店ID");
            }
        } else {
            if (shopInfoAddDTO.getIsHeadShop() == null) {
                throw new CzgException("加盟店/连锁店请选择是否主店");
            }
            if (shopInfoAddDTO.getIsHeadShop() == YesNoEnum.NO.value() && shopInfoAddDTO.getMainId() == null) {
                throw new CzgException("请选择一个店铺做为主店");
            }
            if (shopInfoAddDTO.getIsHeadShop() == YesNoEnum.YES.value()) {
                shopInfoAddDTO.setMainId(null);
            }
        }
        shopInfoAddDTO.setPhone(shopInfoAddDTO.getPhone().trim());
        if (StrUtil.isBlank(shopInfoAddDTO.getPhone())) {
            throw new ApiNotPrintException("请输入手机号");
        }
        long count = sysUserService.queryChain().eq(SysUser::getAccount, shopInfoAddDTO.getAccountName()).count();
        if (count > 0) {
            throw new CzgException("账户已存在");
        }

        // 添加系统账号
        shopInfoAddDTO.setRoleId(shopInfoAddDTO.getRoleId() == null ? 2L : shopInfoAddDTO.getRoleId());
        SysUser sysUser = sysUserService.addUser(shopInfoAddDTO.getShopName(), shopInfoAddDTO.getAccountName(), shopInfoAddDTO.getAccountPwd(), shopInfoAddDTO.getPhone(), shopInfoAddDTO.getRoleId());

        // 保存店铺信息
        ShopInfo shopInfo = BeanUtil.copyProperties(shopInfoAddDTO, ShopInfo.class);
        shopInfo.setId(sysUser.getId());
        //设置激活码
        shopInfo.setStatus(1);
        shopInfo.setProfiles("release");
        save(shopInfo);
        if (StrUtil.isNotBlank(shopInfoAddDTO.getActivateCode())) {
            activateShop(shopInfo, shopInfoAddDTO.getActivateCode());
        }

        // 增加支付方式
        shopPayTypeService.addInfo(shopInfo.getId());
        // 初始化积分霸王餐设置
        freeDineConfigService.getConfig(shopInfo.getId());
        pointsBasicSettingService.initInfo(shopInfo.getId());
        updateById(shopInfo);
        syncSaveConfig(shopInfo, shopInfoAddDTO);
        return true;
    }

    /**
     * 同步创建配置信息
     *
     * @param shopInfo 店铺信息实体
     * @param dto      新增店铺信息DTO
     */
    private void syncSaveConfig(ShopInfo shopInfo, ShopInfoAddDTO dto) {
        ShopConfig shopConfig = new ShopConfig();
        shopConfig.setId(shopInfo.getId());
        shopConfig.setMainId(shopInfo.getMainId());
        shopConfig.setIsEnableProdSync(0);
        shopConfig.setIsEnableVipSync(0);
        shopConfig.setIsEnableConsSync(0);
        shopConfig.setIsAllowAccountLogin(1);
        shopConfig.setRemark(null);
        shopConfig.setIsCustomAmount(0);
        shopConfig.setIsReturnPwd(0);
        shopConfig.setIsMemberInPwd(0);
        shopConfig.setIsMemberReturnPwd(0);
        shopConfig.setIsTableFee(1);
        shopConfig.setIsMemberPrice(0);
        shopConfig.setIsAccountPay(0);
        shopConfig.setBranchDataSyncMethod(null);
        if (dto.getIsHeadShop() == YesNoEnum.YES.value()) {
            shopConfig.setBranchDataSyncMethod(BranchDataSyncMethodEnum.AUTO.getValue());
        }
        shopConfigService.save(shopConfig);
    }

    /**
     * 同步修改配置信息
     *
     * @param shopInfo 店铺信息实体
     * @param dto      新增店铺信息DTO
     */
    private void syncUpdateConfig(ShopInfo shopInfo, ShopInfoEditDTO dto) {
        ShopConfig shopConfig = shopConfigService.getById(shopInfo.getId());
        BeanUtil.copyProperties(dto, shopConfig);
        shopConfigService.updateById(shopConfig);
    }

    @Override
    @CacheEvict(key = "#shopInfoEditDTO.id")
    public Boolean edit(ShopInfoEditDTO shopInfoEditDTO) {
        ShopInfo shopInfo;
        if (!StpKit.USER.isAdmin()) {
            shopInfo = queryChain().eq(ShopInfo::getId, StpKit.USER.getShopId()).one();
        } else {
            shopInfo = getById(shopInfoEditDTO.getId());
        }
        if (shopInfo == null) {
            throw new CzgException("店铺不存在");
        }
        String shopType = shopInfo.getShopType();
        Long mainId = shopInfo.getMainId();
        Integer isHeadShop = shopInfo.getIsHeadShop();
        if (StrUtil.isEmpty(shopInfoEditDTO.getShopType()) || shopType.equals(shopInfoEditDTO.getShopType())) {
            // 原来是单店，现在不是单店，那么就允许修改，否则不允许修改
        } else if (ShopTypeEnum.ONLY.getValue().equals(shopType) && !ShopTypeEnum.ONLY.getValue().equals(shopInfoEditDTO.getShopType())) {
            if (shopInfoEditDTO.getIsHeadShop() == null) {
                throw new CzgException("加盟店/连锁店请选择是否主店");
            }
            if (shopInfoEditDTO.getIsHeadShop() == YesNoEnum.NO.value() && shopInfoEditDTO.getMainId() == null) {
                throw new CzgException("请选择一个店铺做为主店");
            }
            if (shopInfoEditDTO.getIsHeadShop() == YesNoEnum.YES.value()) {
                if (mainId != null) {
                    throw new CzgException("数据错误：当前店铺是非主店，不允许随意切换为主店");
                }
            }
            if (isHeadShop == YesNoEnum.YES.value() && shopInfoEditDTO.getIsHeadShop() == YesNoEnum.NO.value()) {
                throw new CzgException("数据错误：当前店铺是主店，不允许随意切换为非主店");
            }
        } else {
            throw new CzgException("禁止连锁店/加盟店修改为单店");
        }
        if (shopInfoEditDTO.getActivateCode() != null) {
            activateShop(shopInfo, shopInfoEditDTO.getActivateCode());
        }
        BeanUtil.copyProperties(shopInfoEditDTO, shopInfo);
        if (shopInfoEditDTO.getOperationPwd() != null) {
            String key = "%s%s:%s".formatted(RedisCst.SMS_CODE, shopInfo.getPhone(), "editShopInfoOpePwd");
            Object val = redisService.get(key);
            if (val instanceof String code && !shopInfoEditDTO.getCode().equals(code)) {
                throw new ApiNotPrintException("验证码错误");
            }

            shopInfo.setOperationPwd(SecureUtil.md5(shopInfoEditDTO.getOperationPwd()));
            redisService.del(key);
        }
        if (updateById(shopInfo)) {
            syncUpdateConfig(shopInfo, shopInfoEditDTO);
            SysUser sysUser = sysUserService.getById(shopInfo.getId());
            boolean flag = false;
            if (!sysUser.getNickName().equals(shopInfo.getShopName())) {
                sysUser.setNickName(shopInfo.getShopName());
                flag = true;
            }

            if (!sysUser.getPhone().equals(shopInfo.getPhone())) {
                sysUser.setPhone(shopInfo.getPhone());
                flag = true;
            }

            if (flag) {
                sysUserService.updateById(sysUser);
            }
            return true;
        }
        return false;
    }

    @Override
    public ShopDetailDTO detail(Long id) {
        ShopInfo shopInfo = queryChain().eq(ShopInfo::getId, id == null ? StpKit.USER.getShopId() : id).one();
        if (shopInfo == null) {
            throw new ApiNotPrintException("店铺信息不存在");
        }

        if (!StpKit.USER.isAdmin() && !Objects.equals(StpKit.USER.getShopId(), shopInfo.getId())) {
            throw new ApiNotPrintException("店铺信息不存在");
        }
        ShopConfig shopConfig = shopConfigService.getById(shopInfo.getId());
        if (shopConfig == null) {
            throw new CzgException("店铺配置不存在");
        }
        ShopDetailDTO shopDetailDTO = BeanUtil.copyProperties(shopInfo, ShopDetailDTO.class);
        SysUser sysUser = sysUserService.getById(shopInfo.getId());
        shopDetailDTO.setAccount(sysUser.getAccount());
        CzgResult<SysParamsDTO> shopOrderPayBaseUrl = sysParamsService.getParamsByCode("shop_order_pay_base_url");
        shopDetailDTO.setPaymentQrcode(shopOrderPayBaseUrl.getData().getParamValue() + "?shopId=" + id);
        BeanUtil.copyProperties(shopConfig, shopDetailDTO);
        return shopDetailDTO;
    }

    @Override
    public ShopInfoByCodeDTO getByCode(String tableCode, String lat, String lng, boolean checkState) {
        ShopTable shopTable = shopTableService.queryChain().eq(ShopTable::getTableCode, tableCode).one();
        if (shopTable == null) {
            throw new ApiNotPrintException("错误的台桌码");
        }

        if (checkState && !shopTable.canUseByStatus()) {
            throw new ApiNotPrintException("台桌状态不可用");
        }
        ShopInfo shopInfo = getShopInfo(shopTable.getShopId());
        double distance = 0;
        if (StrUtil.isAllNotBlank(lat, lng, shopInfo.getLat(), shopInfo.getLng())) {
            // 计算距离，单位：米
            distance = GeoUtil.getDistance(Long.parseLong(shopInfo.getLat()), Long.parseLong(shopInfo.getLng()), Long.parseLong(lat), Long.parseLong(lng));
        }

        ShopUser shopUser = shopUserMapper.selectOneByQuery(new QueryWrapper().eq(ShopUser::getUserId, StpKit.USER.getLoginIdAsLong()));
        List<ShopExtend> shopExtends = shopExtendService.listInfo(shopInfo.getId(), null);
        Map<String, ShopExtend> shopExtendMap = shopExtends.stream().collect(Collectors.toMap(ShopExtend::getAutoKey, i -> i));
        return new ShopInfoByCodeDTO(distance, shopInfo, shopExtendMap, shopTable, shopUser != null && shopUser.getIsVip() != null && shopUser.getIsVip() == 1);
    }

    @Override
    public ShopInfoDetailDTO getDetail(Long shopId) {
        ShopInfo shopInfo = getShopInfo(shopId);
        List<ShopExtend> shopExtends = shopExtendService.listInfo(shopId, null);
        Map<String, ShopExtend> shopExtendMap = shopExtends.stream().collect(Collectors.toMap(ShopExtend::getAutoKey, i -> i));
        return new ShopInfoDetailDTO(shopInfo, shopExtendMap);
    }

    @Override
    public Page<ShopInfoSubVO> getSubList(String lat, String lng, float distance) {
        PageHelper.startPage(PageUtil.buildPageHelp());
        return PageUtil.convert(new PageInfo<>(mapper.getSubList(lng, lat, distance)));
    }

    @Override
    public Page<ShopInfo> getShopByMainId(PageDTO pageDTO, String shopName, Integer status) {
        Long loginId = (Long) StpKit.USER.getLoginId();
        ShopInfo shopInfo = getById(loginId);
        QueryWrapper queryWrapper = new QueryWrapper();
        if (!"only".equals(shopInfo.getShopType())) {
            if (shopInfo.getIsHeadShop() != 1) {
                queryWrapper.eq(ShopInfo::getId, loginId);
            } else {
                queryWrapper.and(q -> {
                    q.eq(ShopInfo::getId, loginId).or(q1 -> {
                        q1.eq(ShopInfo::getMainId, loginId);
                    });
                });
            }
        } else {
            queryWrapper.eq(ShopInfo::getId, loginId);
        }
        return page(new Page<>(pageDTO.page(), pageDTO.size()), queryWrapper);
    }

    @Override
    public List<ShopBranchSelectDTO> findShopBranch(Long shopId) {
        Long mainIdByShopId = getMainIdByShopId(shopId);
        List<ShopBranchSelectDTO> list = new ArrayList<>();
        // 主店信息
        ShopInfo shopInfo = getById(mainIdByShopId);
        ShopBranchSelectDTO head = new ShopBranchSelectDTO();
        head.setShopId(shopInfo.getId());
        head.setShopName(shopInfo.getShopName());
        list.add(head);
        MyStpLogic.LoginType loginType = (MyStpLogic.LoginType) StpKit.USER.getSession().get("loginType");
        if (loginType.compareTo(MyStpLogic.LoginType.STAFF) == 0) {
            return list;
        }
        List<ShopInfo> branchList = mapper.selectListByQuery(query().select(ShopInfo::getId, ShopInfo::getShopName).eq(ShopInfo::getMainId, mainIdByShopId).orderBy(ShopInfo::getId, true));
        for (ShopInfo info : branchList) {
            ShopBranchSelectDTO branch = new ShopBranchSelectDTO();
            branch.setShopId(info.getId());
            branch.setShopName(info.getShopName());
            list.add(branch);
        }
        return list;
    }

    @Override
    public Long getMainIdByShopId(Long shopId) {
        ShopInfo shopInfo = this.getById(shopId);
        if (shopInfo == null) {
            throw new CzgException("店铺信息不存在");
        }
        return shopInfo.getMainId() == null ? shopId : shopInfo.getMainId();

    }

    @Override
    public List<ShopInfo> getByMainIdOrList(Long mainShopId, List<Long> shopIdList, String shopName) {
        QueryWrapper queryWrapper = new QueryWrapper().like(ShopInfo::getShopName, shopName);
        if (shopIdList == null) {
            queryWrapper.and(q -> {
                q.eq(ShopInfo::getMainId, mainShopId).or(q1 -> {
                    q1.eq(ShopInfo::getId, mainShopId);
                });
            });
        }else  {
            shopIdList.add(mainShopId);
            queryWrapper.in(ShopInfo::getId, shopIdList);
        }

        return list(queryWrapper);
    }

    @CacheEvict(key = "#id")
    @Override
    public BigDecimal updateAmount(Long id, BigDecimal amount) {
        ShopInfo shopInfo = getShopInfo(id);
        if (shopInfo.getAmount() == null) {
            shopInfo.setAmount(BigDecimal.ZERO);
        }
        boolean flag = mapper.updateAmount(id, amount);
        if (!flag) {
            throw new CzgException("更新失败");
        }
        return shopInfo.getAmount().add(amount);
    }


}
