package com.ruoyi.web.modules.entry.service.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.web.modules.account.domain.BizMember;
import com.ruoyi.web.modules.account.mapper.BizMemberMapper;
import com.ruoyi.web.modules.content.facade.IMerchantEntryAgreementFacade;
import com.ruoyi.web.modules.entry.constant.MerchantEntryApplyConstants;
import com.ruoyi.web.modules.entry.domain.BizMerchantEntryApply;
import com.ruoyi.web.modules.entry.dto.EntryApplyApproveDTO;
import com.ruoyi.web.modules.entry.dto.EntryApplyRejectDTO;
import com.ruoyi.web.modules.entry.dto.EntryApplyShopDTO;
import com.ruoyi.web.modules.entry.dto.EntryApplySubmitDTO;
import com.ruoyi.web.modules.entry.mapper.BizMerchantEntryApplyMapper;
import com.ruoyi.web.modules.entry.service.IMerchantEntryApplyService;
import com.ruoyi.web.modules.entry.support.EntryApplyFormMapper;
import com.ruoyi.web.modules.entry.support.MemberEntryMessageSupport;
import com.ruoyi.web.modules.entry.vo.EntryApplyDetailVO;
import com.ruoyi.web.modules.entry.vo.EntryApplyListVO;
import com.ruoyi.web.modules.entry.vo.EntryApplyMyVO;
import com.ruoyi.web.modules.merchant.constant.MerchantConstants;
import com.ruoyi.web.modules.merchant.domain.BizMerchant;
import com.ruoyi.web.modules.merchant.dto.MerchantCreateDTO;
import com.ruoyi.web.modules.merchant.mapper.BizMerchantMapper;
import com.ruoyi.web.modules.merchant.service.IMerchantAccountBindService;
import com.ruoyi.web.modules.merchant.support.BizCompleteEvaluator;
import com.ruoyi.web.modules.store.dto.ShopCreateDTO;
import com.ruoyi.web.modules.store.mapper.BizShopMapper;
import com.ruoyi.web.modules.store.service.IShopService;

@Service
public class MerchantEntryApplyServiceImpl implements IMerchantEntryApplyService
{
    @Autowired
    private BizMerchantEntryApplyMapper applyMapper;

    @Autowired
    private BizMerchantMapper merchantMapper;

    @Autowired
    private BizShopMapper shopMapper;

    @Autowired
    private BizMemberMapper memberMapper;

    @Autowired
    private IMerchantEntryAgreementFacade agreementFacade;

    @Autowired
    private IMerchantAccountBindService accountBindService;

    @Autowired
    private IShopService shopService;

    @Autowired
    private MemberEntryMessageSupport messageSupport;

    @Autowired
    private ISysConfigService configService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> submitApply(Long memberId, EntryApplySubmitDTO dto)
    {
        agreementFacade.assertAccepted(Boolean.TRUE.equals(dto.getAgreementAccepted()));
        if (applyMapper.countPendingByMemberId(memberId) > 0)
        {
            throw new ServiceException(MerchantEntryApplyConstants.MSG_PENDING_EXISTS);
        }
        BizMember member = requireMember(memberId);
        if (StringUtils.isEmpty(member.getNickName()))
        {
            throw new ServiceException(MerchantEntryApplyConstants.MSG_MEMBER_NICK_REQUIRED);
        }
        validateSubmitForm(dto);
        String merchantType = resolveMerchantType(dto);
        assertUniqueOnSubmit(dto, merchantType, null);

        Date now = new Date();
        BizMerchantEntryApply apply = new BizMerchantEntryApply();
        apply.setApplyNo(generateApplyNo());
        apply.setApplyStatus(MerchantEntryApplyConstants.STATUS_PENDING);
        apply.setMerchantType(merchantType);
        apply.setMemberId(memberId);
        apply.setMemberCode(StringUtils.isNotEmpty(member.getMemberCode()) ? member.getMemberCode() : member.getNickName());
        apply.setSubjectLabel(EntryApplyFormMapper.resolveSubjectLabel(merchantType, dto.getSubject()));
        apply.setContactName(dto.getBiz().getContactName());
        apply.setContactPhone(dto.getBiz().getContactPhone());
        apply.setIdCardNo(resolveIdCardNo(dto, merchantType));
        apply.setCreditCode(resolveCreditCode(dto, merchantType));
        apply.setMerchantName(dto.getBiz().getMerchantName());
        apply.setShopName(dto.getShop().getShopName());
        apply.setFormJson(EntryApplyFormMapper.toFormJson(dto));
        apply.setApplyTime(now);
        apply.setDelFlag(MerchantEntryApplyConstants.DEL_NORMAL);
        apply.setCreateBy(String.valueOf(memberId));
        applyMapper.insert(apply);

        Map<String, Object> data = new HashMap<>();
        data.put("applyId", apply.getApplyId());
        data.put("applyNo", apply.getApplyNo());
        data.put("applyStatus", apply.getApplyStatus());
        return data;
    }

    @Override
    public List<EntryApplyMyVO> listMyApplies(Long memberId)
    {
        List<EntryApplyMyVO> rows = new ArrayList<>();
        for (BizMerchantEntryApply apply : applyMapper.selectMyByMemberId(memberId,
                MerchantEntryApplyConstants.MY_APPLY_LIMIT))
        {
            EntryApplyMyVO vo = new EntryApplyMyVO();
            vo.setApplyId(apply.getApplyId());
            vo.setApplyNo(apply.getApplyNo());
            vo.setApplyStatus(apply.getApplyStatus());
            vo.setApplyTime(apply.getApplyTime());
            vo.setAuditTime(apply.getAuditTime());
            vo.setRejectReason(apply.getRejectReason());
            vo.setMerchantId(apply.getMerchantId());
            vo.setShopId(apply.getShopId());
            rows.add(vo);
        }
        return rows;
    }

    @Override
    public List<EntryApplyListVO> selectList(BizMerchantEntryApply query)
    {
        return applyMapper.selectList(query);
    }

    @Override
    public int countPlatformPending()
    {
        return applyMapper.countPlatformPending();
    }

    @Override
    public EntryApplyDetailVO selectDetail(Long applyId)
    {
        BizMerchantEntryApply apply = requirePendingOrAny(applyId);
        EntryApplyDetailVO vo = new EntryApplyDetailVO();
        vo.setApplyId(apply.getApplyId());
        vo.setApplyNo(apply.getApplyNo());
        vo.setApplyStatus(apply.getApplyStatus());
        vo.setMerchantType(apply.getMerchantType());
        vo.setMemberId(apply.getMemberId());
        vo.setMemberCode(apply.getMemberCode());
        vo.setSubjectLabel(apply.getSubjectLabel());
        vo.setContactName(apply.getContactName());
        vo.setContactPhone(apply.getContactPhone());
        vo.setRejectReason(apply.getRejectReason());
        vo.setMerchantId(apply.getMerchantId());
        vo.setShopId(apply.getShopId());
        vo.setApplyTime(apply.getApplyTime());
        vo.setAuditBy(apply.getAuditBy());
        vo.setAuditTime(apply.getAuditTime());
        EntryApplyFormMapper.ParsedForm parsed = EntryApplyFormMapper.parseFormJson(apply.getFormJson());
        vo.setSubject(parsed.getSubject());
        vo.setBiz(parsed.getBiz());
        vo.setShop(parsed.getShop());
        vo.setAgreementAccepted(parsed.getAgreementAccepted());
        boolean pending = MerchantEntryApplyConstants.STATUS_PENDING.equals(apply.getApplyStatus());
        vo.setCanApprove(pending);
        vo.setCanReject(pending);
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> approve(Long applyId, EntryApplyApproveDTO dto, String operator)
    {
        BizMerchantEntryApply apply = requirePendingApply(applyId);
        assertUniqueOnSubmit(buildSubmitFromApply(apply), apply.getMerchantType(), apply.getApplyId());

        BizMember member = requireMember(apply.getMemberId());
        if (StringUtils.isEmpty(member.getNickName()))
        {
            throw new ServiceException(MerchantEntryApplyConstants.MSG_MEMBER_NICK_REQUIRED);
        }

        BizMerchant merchant = EntryApplyFormMapper.toMerchantFromJson(apply.getFormJson(), apply.getMerchantType());
        Date now = new Date();
        merchant.setCertStatus(MerchantConstants.CERT_NORMAL);
        merchant.setCertTime(now);
        merchant.setBizComplete(BizCompleteEvaluator.evaluateCompleteFlag(merchant));
        merchant.setShopCount(0);
        merchant.setDelFlag(MerchantConstants.DEL_FLAG_NORMAL);
        merchant.setCreateBy(operator);
        merchantMapper.insert(merchant);

        MerchantCreateDTO bindDto = EntryApplyFormMapper.toMerchantCreateDTO(merchant, apply.getMemberId());
        accountBindService.bindAccountOnMerchantCreate(merchant.getMerchantId(), bindDto, operator);

        String memberName = StringUtils.isNotEmpty(member.getMemberCode()) ? member.getMemberCode() : member.getNickName();
        String adminName = memberName + member.getMemberId();
        String initPassword = resolveInitPassword();
        ShopCreateDTO shopDto = EntryApplyFormMapper.toShopCreateDTO(apply.getFormJson(), merchant.getMerchantId(),
                member.getNickName(), adminName, initPassword);
        Long shopId = shopService.createShop(shopDto, operator);

        BizMerchantEntryApply update = new BizMerchantEntryApply();
        update.setApplyId(apply.getApplyId());
        update.setApplyStatus(MerchantEntryApplyConstants.STATUS_APPROVED);
        update.setMerchantId(merchant.getMerchantId());
        update.setShopId(shopId);
        update.setAuditBy(operator);
        update.setAuditTime(now);
        update.setUpdateBy(operator);
        applyMapper.updateAuditResult(update);

        messageSupport.sendPass(apply.getMemberId(), apply.getApplyId(), apply.getApplyNo());

        Map<String, Object> data = new HashMap<>();
        data.put("merchantId", merchant.getMerchantId());
        data.put("shopId", shopId);
        return data;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reject(EntryApplyRejectDTO dto, String operator)
    {
        if (dto == null || dto.getApplyId() == null)
        {
            throw new ServiceException(MerchantEntryApplyConstants.MSG_APPLY_NOT_FOUND);
        }
        if (StringUtils.isEmpty(dto.getRejectReason()))
        {
            throw new ServiceException(MerchantEntryApplyConstants.MSG_REJECT_REASON_REQUIRED);
        }
        BizMerchantEntryApply apply = requirePendingApply(dto.getApplyId());
        Date now = new Date();
        BizMerchantEntryApply update = new BizMerchantEntryApply();
        update.setApplyId(apply.getApplyId());
        update.setApplyStatus(MerchantEntryApplyConstants.STATUS_REJECTED);
        update.setRejectReason(dto.getRejectReason().trim());
        update.setAuditBy(operator);
        update.setAuditTime(now);
        update.setUpdateBy(operator);
        applyMapper.updateAuditResult(update);
        messageSupport.sendReject(apply.getMemberId(), apply.getApplyId(), apply.getApplyNo(), dto.getRejectReason());
    }

    private BizMerchantEntryApply requirePendingApply(Long applyId)
    {
        BizMerchantEntryApply apply = applyMapper.selectById(applyId);
        if (apply == null)
        {
            throw new ServiceException(MerchantEntryApplyConstants.MSG_APPLY_NOT_FOUND);
        }
        if (!MerchantEntryApplyConstants.STATUS_PENDING.equals(apply.getApplyStatus()))
        {
            throw new ServiceException(MerchantEntryApplyConstants.MSG_NOT_PENDING);
        }
        return apply;
    }

    private BizMerchantEntryApply requirePendingOrAny(Long applyId)
    {
        BizMerchantEntryApply apply = applyMapper.selectById(applyId);
        if (apply == null)
        {
            throw new ServiceException(MerchantEntryApplyConstants.MSG_APPLY_NOT_FOUND);
        }
        return apply;
    }

    private BizMember requireMember(Long memberId)
    {
        BizMember member = memberMapper.selectById(memberId);
        if (member == null)
        {
            throw new ServiceException("会员不存在");
        }
        return member;
    }

    private void validateSubmitForm(EntryApplySubmitDTO dto)
    {
        if (dto == null || dto.getSubject() == null || dto.getBiz() == null || dto.getShop() == null)
        {
            throw new ServiceException("请完整填写入驻申请信息");
        }
        String merchantType = resolveMerchantType(dto);
        if (MerchantConstants.TYPE_PERSON.equals(merchantType))
        {
            if (StringUtils.isEmpty(dto.getSubject().getPersonName()))
            {
                throw new ServiceException("请输入姓名");
            }
        }
        else if (MerchantConstants.TYPE_ENTERPRISE.equals(merchantType))
        {
            if (StringUtils.isEmpty(dto.getSubject().getCreditCode()))
            {
                throw new ServiceException("请输入统一社会信用代码");
            }
        }
        else
        {
            throw new ServiceException("商户类型无效");
        }
        if (StringUtils.isEmpty(dto.getBiz().getMerchantName()))
        {
            throw new ServiceException("请输入商户名称");
        }
        if (StringUtils.isEmpty(dto.getBiz().getContactName()))
        {
            throw new ServiceException("请输入联系人姓名");
        }
        if (StringUtils.isEmpty(dto.getBiz().getContactPhone()))
        {
            throw new ServiceException("请输入联系人手机");
        }
        EntryApplyShopDTO shop = dto.getShop();
        if (StringUtils.isEmpty(shop.getShopName()))
        {
            throw new ServiceException("请输入店铺名称");
        }
    }

    private String resolveMerchantType(EntryApplySubmitDTO dto)
    {
        if (dto.getSubject() != null && StringUtils.isNotEmpty(dto.getSubject().getMerchantType()))
        {
            return dto.getSubject().getMerchantType();
        }
        if (dto.getBiz() != null && StringUtils.isNotEmpty(dto.getBiz().getMerchantType()))
        {
            return dto.getBiz().getMerchantType();
        }
        throw new ServiceException("商户类型无效");
    }

    private void assertUniqueOnSubmit(EntryApplySubmitDTO dto, String merchantType, Long excludeApplyId)
    {
        if (MerchantConstants.TYPE_ENTERPRISE.equals(merchantType))
        {
            String creditCode = dto.getSubject().getCreditCode();
            if (merchantMapper.countByCreditCode(creditCode, null) > 0)
            {
                throw new ServiceException("该统一社会信用代码已入驻");
            }
            if (applyMapper.countByCreditCodeActive(creditCode, excludeApplyId) > 0)
            {
                throw new ServiceException("该统一社会信用代码已有入驻申请");
            }
        }
        if (MerchantConstants.TYPE_PERSON.equals(merchantType))
        {
            String idCardNo = dto.getSubject().getIdCardNo();
            if (StringUtils.isNotEmpty(idCardNo))
            {
                if (merchantMapper.countByIdCardNo(idCardNo, null) > 0)
                {
                    throw new ServiceException("该证件号码已入驻");
                }
                if (applyMapper.countByIdCardNoActive(idCardNo, excludeApplyId) > 0)
                {
                    throw new ServiceException("该证件号码已有入驻申请");
                }
            }
        }
        String merchantName = dto.getBiz().getMerchantName();
        if (merchantMapper.countByMerchantName(merchantName, null) > 0)
        {
            throw new ServiceException("商户名称已存在");
        }
        if (applyMapper.countByMerchantNameActive(merchantName, excludeApplyId) > 0)
        {
            throw new ServiceException("商户名称已有入驻申请");
        }
        String shopName = dto.getShop().getShopName();
        if (shopMapper.countByShopName(shopName, null) > 0)
        {
            throw new ServiceException("店铺名称已存在");
        }
        if (applyMapper.countByShopNameActive(shopName, excludeApplyId) > 0)
        {
            throw new ServiceException("店铺名称已有入驻申请");
        }
    }

    private EntryApplySubmitDTO buildSubmitFromApply(BizMerchantEntryApply apply)
    {
        EntryApplyFormMapper.ParsedForm parsed = EntryApplyFormMapper.parseFormJson(apply.getFormJson());
        EntryApplySubmitDTO dto = new EntryApplySubmitDTO();
        dto.setSubject(parsed.getSubject());
        dto.setBiz(parsed.getBiz());
        dto.setShop(parsed.getShop());
        return dto;
    }

    private String resolveIdCardNo(EntryApplySubmitDTO dto, String merchantType)
    {
        if (MerchantConstants.TYPE_PERSON.equals(merchantType) && dto.getSubject() != null)
        {
            return dto.getSubject().getIdCardNo();
        }
        return null;
    }

    private String resolveCreditCode(EntryApplySubmitDTO dto, String merchantType)
    {
        if (MerchantConstants.TYPE_ENTERPRISE.equals(merchantType) && dto.getSubject() != null)
        {
            return dto.getSubject().getCreditCode();
        }
        return null;
    }

    private String generateApplyNo()
    {
        return MerchantEntryApplyConstants.APPLY_NO_PREFIX
                + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())
                + String.format("%03d", (int) (Math.random() * 1000));
    }

    private String resolveInitPassword()
    {
        String init = configService.selectConfigByKey("sys.user.initPassword");
        return StringUtils.isNotEmpty(init) ? init : "123456";
    }
}
