package com.ruoyi.loanpricing.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.loanpricing.domain.dto.CorporateLoanPricingCreateDTO;
import com.ruoyi.loanpricing.domain.dto.PersonalLoanPricingCreateDTO;
import com.ruoyi.loanpricing.domain.entity.LoanPricingWorkflow;
import com.ruoyi.loanpricing.domain.entity.ModelCorpOutputFields;
import com.ruoyi.loanpricing.domain.entity.ModelRetailOutputFields;
import com.ruoyi.loanpricing.domain.vo.LoanPricingWorkflowListVO;
import com.ruoyi.loanpricing.domain.vo.LoanPricingWorkflowVO;
import com.ruoyi.loanpricing.mapper.LoanPricingWorkflowMapper;
import com.ruoyi.loanpricing.mapper.ModelCorpOutputFieldsMapper;
import com.ruoyi.loanpricing.mapper.ModelRetailOutputFieldsMapper;
import com.ruoyi.loanpricing.service.ILoanPricingWorkflowService;
import com.ruoyi.loanpricing.service.LoanPricingModelService;
import com.ruoyi.loanpricing.service.SensitiveFieldCryptoService;
import com.ruoyi.loanpricing.util.LoanPricingConverter;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 利率定价流程Service业务层处理
 *
 * @author ruoyi
 * @date 2025-01-19
 */
@Service
public class LoanPricingWorkflowServiceImpl implements ILoanPricingWorkflowService
{
    private static final String WORKFLOW_ADMIN_ROLE_NAME = "管理员";

    private static final String WORKFLOW_ADMIN_ROLE_KEY = "headAdmin";

    private static final String WORKFLOW_BRANCH_ADMIN_ROLE_NAME = "支行管理员";

    private static final String WORKFLOW_BRANCH_ADMIN_ROLE_KEY = "branchAdmin";

    @Resource
    private LoanPricingWorkflowMapper loanPricingWorkflowMapper;

    @Resource
    private LoanPricingModelService loanPricingModelService;

    @Resource
    private ModelRetailOutputFieldsMapper modelRetailOutputFieldsMapper;

    @Resource
    private ModelCorpOutputFieldsMapper modelCorpOutputFieldsMapper;

    @Resource
    private SensitiveFieldCryptoService sensitiveFieldCryptoService;

    /**
     * 发起利率定价流程
     *
     * @param loanPricingWorkflow 利率定价流程信息
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    public LoanPricingWorkflow createLoanPricing(LoanPricingWorkflow loanPricingWorkflow)
    {
        validateBusinessTypeAndHistoryRate(loanPricingWorkflow);
        validateCollateralTypeAndCouponRate(loanPricingWorkflow);

        // 自动生成业务方流水号(时间戳)
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String serialNum = sdf.format(new Date());
        loanPricingWorkflow.setSerialNum(serialNum);

        // 设置默认值
        if (!StringUtils.hasText(loanPricingWorkflow.getOrgCode()))
        {
            loanPricingWorkflow.setOrgCode("892000");
        }
        if (!StringUtils.hasText(loanPricingWorkflow.getRunType()))
        {
            loanPricingWorkflow.setRunType("1");
        }

        loanPricingWorkflow.setDeptId(SecurityUtils.getDeptId());
        loanPricingWorkflowMapper.insert(loanPricingWorkflow);
        loanPricingModelService.invokeModelAsync(loanPricingWorkflow.getId());

        return loanPricingWorkflow;
    }

    private void validateBusinessTypeAndHistoryRate(LoanPricingWorkflow workflow) {
        if (!StringUtils.hasText(workflow.getBusinessType())) {
            throw new ServiceException("业务种类不能为空");
        }
        if (!"新增".equals(workflow.getBusinessType())
                && !"存量新增".equals(workflow.getBusinessType())
                && !"存量转贷".equals(workflow.getBusinessType())) {
            throw new ServiceException("业务种类必须是：新增、存量新增、存量转贷之一");
        }
        if ("存量转贷".equals(workflow.getBusinessType())
                && !StringUtils.hasText(workflow.getLoanRateHistory())) {
            throw new ServiceException("请选择历史贷款合同");
        }
    }

    private void validateCollateralTypeAndCouponRate(LoanPricingWorkflow workflow) {
        if (!"抵押".equals(workflow.getGuarType()) && !"质押".equals(workflow.getGuarType())) {
            return;
        }
        if (!StringUtils.hasText(workflow.getCollType())) {
            throw new ServiceException("请选择抵质押类型");
        }
        if ("个人".equals(workflow.getCustType())) {
            validatePersonalCollateralType(workflow);
        }
        if ("企业".equals(workflow.getCustType())) {
            validateCorporateCollateralType(workflow);
        }
        if ("质押".equals(workflow.getGuarType())
                && "存单质押".equals(workflow.getCollType())
                && !StringUtils.hasText(workflow.getCouponRate())) {
            throw new ServiceException("存单票面利率不能为空");
        }
    }

    private void validatePersonalCollateralType(LoanPricingWorkflow workflow) {
        if ("抵押".equals(workflow.getGuarType())
                && !isOneOf(workflow.getCollType(), "一类", "二类", "三类", "四类")) {
            throw new ServiceException("个人抵押抵质押类型必须是：一类、二类、三类、四类之一");
        }
        if ("质押".equals(workflow.getGuarType())
                && !isOneOf(workflow.getCollType(), "存单质押", "其他质押")) {
            throw new ServiceException("个人质押抵质押类型必须是：存单质押、其他质押之一");
        }
    }

    private void validateCorporateCollateralType(LoanPricingWorkflow workflow) {
        if ("抵押".equals(workflow.getGuarType())
                && !isOneOf(workflow.getCollType(), "一类", "二类", "三类", "排污权抵押", "其他不动产抵押")) {
            throw new ServiceException("企业抵押抵质押类型必须是：一类、二类、三类、排污权抵押、其他不动产抵押之一");
        }
        if ("质押".equals(workflow.getGuarType())
                && !isOneOf(workflow.getCollType(), "存单质押", "股权质押", "其他质押")) {
            throw new ServiceException("企业质押抵质押类型必须是：存单质押、股权质押、其他质押之一");
        }
    }

    private boolean isOneOf(String value, String... allowedValues) {
        for (String allowedValue : allowedValues) {
            if (allowedValue.equals(value)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 发起个人客户利率定价流程
     *
     * @param dto 个人客户发起DTO
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoanPricingWorkflow createPersonalLoanPricing(PersonalLoanPricingCreateDTO dto) {
        LoanPricingWorkflow entity = LoanPricingConverter.toEntity(dto);
        return createLoanPricing(entity);
    }

    /**
     * 发起企业客户利率定价流程
     *
     * @param dto 企业客户发起DTO
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoanPricingWorkflow createCorporateLoanPricing(CorporateLoanPricingCreateDTO dto) {
        LoanPricingWorkflow entity = LoanPricingConverter.toEntity(dto);
        return createLoanPricing(entity);
    }

    @Override
    public LoanPricingWorkflow selectEditableLoanPricingBySerialNum(String serialNum)
    {
        LoanPricingWorkflow workflow = selectWorkflowBySerialNum(serialNum);
        assertCurrentUserIsCreator(workflow);
        return workflow;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoanPricingWorkflow updatePersonalLoanPricing(String serialNum, PersonalLoanPricingCreateDTO dto)
    {
        LoanPricingWorkflow editingWorkflow = LoanPricingConverter.toEntity(dto);
        return updateLoanPricing(serialNum, editingWorkflow, "个人");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoanPricingWorkflow updateCorporateLoanPricing(String serialNum, CorporateLoanPricingCreateDTO dto)
    {
        LoanPricingWorkflow editingWorkflow = LoanPricingConverter.toEntity(dto);
        return updateLoanPricing(serialNum, editingWorkflow, "企业");
    }

    private LoanPricingWorkflow updateLoanPricing(String serialNum, LoanPricingWorkflow editingWorkflow, String expectedCustType)
    {
        LoanPricingWorkflow existingWorkflow = selectWorkflowBySerialNum(serialNum);
        assertCurrentUserIsCreator(existingWorkflow);
        if (!expectedCustType.equals(existingWorkflow.getCustType()))
        {
            throw new ServiceException("客户类型不匹配，不能编辑该流程");
        }

        editingWorkflow.setId(existingWorkflow.getId());
        editingWorkflow.setSerialNum(existingWorkflow.getSerialNum());
        editingWorkflow.setCustType(existingWorkflow.getCustType());
        editingWorkflow.setModelOutputId(existingWorkflow.getModelOutputId());
        validateBusinessTypeAndHistoryRate(editingWorkflow);
        validateCollateralTypeAndCouponRate(editingWorkflow);

        LambdaUpdateWrapper<LoanPricingWorkflow> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(LoanPricingWorkflow::getId, existingWorkflow.getId())
                .set(LoanPricingWorkflow::getCustIsn, editingWorkflow.getCustIsn())
                .set(LoanPricingWorkflow::getCustName, editingWorkflow.getCustName())
                .set(LoanPricingWorkflow::getIdType, editingWorkflow.getIdType())
                .set(LoanPricingWorkflow::getIdNum, editingWorkflow.getIdNum())
                .set(LoanPricingWorkflow::getGuarType, editingWorkflow.getGuarType())
                .set(LoanPricingWorkflow::getApplyAmt, editingWorkflow.getApplyAmt())
                .set(LoanPricingWorkflow::getBusinessType, editingWorkflow.getBusinessType())
                .set(LoanPricingWorkflow::getLoanRateHistory, editingWorkflow.getLoanRateHistory())
                .set(LoanPricingWorkflow::getCouponRate, editingWorkflow.getCouponRate())
                .set(LoanPricingWorkflow::getLoanTerm, editingWorkflow.getLoanTerm())
                .set(LoanPricingWorkflow::getCollType, editingWorkflow.getCollType())
                .set(LoanPricingWorkflow::getCollThirdParty, editingWorkflow.getCollThirdParty())
                .set(LoanPricingWorkflow::getLoanLoop, editingWorkflow.getLoanLoop())
                .set(LoanPricingWorkflow::getResCover, editingWorkflow.getResCover())
                .set(LoanPricingWorkflow::getRepayMethod, editingWorkflow.getRepayMethod())
                .set(LoanPricingWorkflow::getIsGreenLoan, editingWorkflow.getIsGreenLoan())
                .set(LoanPricingWorkflow::getIsTradeBuildEnt, editingWorkflow.getIsTradeBuildEnt())
                .set(LoanPricingWorkflow::getUpdateBy, buildCurrentCreateBy(SecurityUtils.getLoginUser()))
                .set(LoanPricingWorkflow::getUpdateTime, new Date());
        loanPricingWorkflowMapper.update(null, updateWrapper);
        loanPricingModelService.reinvokeModelAndOverwrite(existingWorkflow.getId());
        return selectEditableLoanPricingBySerialNum(serialNum);
    }

    private LoanPricingWorkflow selectWorkflowBySerialNum(String serialNum)
    {
        LambdaQueryWrapper<LoanPricingWorkflow> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LoanPricingWorkflow::getSerialNum, serialNum);
        LoanPricingWorkflow workflow = loanPricingWorkflowMapper.selectOne(wrapper);
        if (workflow == null)
        {
            throw new ServiceException("记录不存在");
        }
        return workflow;
    }

    private void assertCurrentUserIsCreator(LoanPricingWorkflow workflow)
    {
        String currentCreateBy = buildCurrentCreateBy(SecurityUtils.getLoginUser());
        if (!currentCreateBy.equals(workflow.getCreateBy()))
        {
            throw new ServiceException("只有创建者可以编辑该流程");
        }
    }

    /**
     * 查询利率定价流程列表
     *
     * @param loanPricingWorkflow 利率定价流程信息
     * @return 利率定价流程
     */
    @Override
    public List<LoanPricingWorkflow> selectLoanPricingList(LoanPricingWorkflow loanPricingWorkflow)
    {
        LambdaQueryWrapper<LoanPricingWorkflow> wrapper = buildQueryWrapper(loanPricingWorkflow);
        // 按更新时间倒序
        wrapper.orderByDesc(LoanPricingWorkflow::getUpdateTime);
        return loanPricingWorkflowMapper.selectList(wrapper);
    }

    /**
     * 分页查询利率定价流程列表
     *
     * @param page 分页参数
     * @param loanPricingWorkflow 利率定价流程信息
     * @return 利率定价流程
     */
    @Override
    public IPage<LoanPricingWorkflowListVO> selectLoanPricingPage(Page<LoanPricingWorkflowListVO> page, LoanPricingWorkflow loanPricingWorkflow)
    {
        LoanPricingWorkflow scopedQuery = applyWorkflowListDataScope(loanPricingWorkflow);
        IPage<LoanPricingWorkflowListVO> pageResult = loanPricingWorkflowMapper.selectWorkflowPageWithRates(page, scopedQuery);
        return pageResult;
    }

    /**
     * 查询利率定价流程详情
     *
     * @param serialNum 业务方流水号
     * @return 利率定价流程
     */
    @Override
    public LoanPricingWorkflowVO selectLoanPricingBySerialNum(String serialNum)
    {
        LoanPricingWorkflowVO loanPricingWorkflowVO = new LoanPricingWorkflowVO();

        LambdaQueryWrapper<LoanPricingWorkflow> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LoanPricingWorkflow::getSerialNum, serialNum);
        LoanPricingWorkflow loanPricingWorkflow = loanPricingWorkflowMapper.selectOne(wrapper);
        loanPricingWorkflowVO.setLoanPricingWorkflow(loanPricingWorkflow);

        if (Objects.nonNull(loanPricingWorkflow.getModelOutputId())){
            if (loanPricingWorkflow.getCustType().equals("个人")){
                ModelRetailOutputFields modelRetailOutputFields = modelRetailOutputFieldsMapper.selectById(loanPricingWorkflow.getModelOutputId());
                if (Objects.nonNull(modelRetailOutputFields))
                {
                    loanPricingWorkflow.setLoanRate(modelRetailOutputFields.getFinalCalculateRate());
                }
                loanPricingWorkflowVO.setModelRetailOutputFields(modelRetailOutputFields);
            }
            if (loanPricingWorkflow.getCustType().equals("企业")){
                ModelCorpOutputFields modelCorpOutputFields = modelCorpOutputFieldsMapper.selectById(loanPricingWorkflow.getModelOutputId());
                if (Objects.nonNull(modelCorpOutputFields))
                {
                    loanPricingWorkflow.setLoanRate(modelCorpOutputFields.getFinalCalculateRate());
                }
                loanPricingWorkflowVO.setModelCorpOutputFields(modelCorpOutputFields);
            }
        }


        return loanPricingWorkflowVO;
    }

    /**
     * 构建查询条件
     *
     * @param loanPricingWorkflow 利率定价流程信息
     * @return LambdaQueryWrapper
     */
    private LambdaQueryWrapper<LoanPricingWorkflow> buildQueryWrapper(LoanPricingWorkflow loanPricingWorkflow)
    {
        LambdaQueryWrapper<LoanPricingWorkflow> wrapper = new LambdaQueryWrapper<>();

        // 按创建者筛选
        if (StringUtils.hasText(loanPricingWorkflow.getCreateBy()))
        {
            wrapper.like(LoanPricingWorkflow::getCreateBy, loanPricingWorkflow.getCreateBy());
        }

        // 按客户内码模糊查询
        if (StringUtils.hasText(loanPricingWorkflow.getCustIsn()))
        {
            wrapper.like(LoanPricingWorkflow::getCustIsn, loanPricingWorkflow.getCustIsn());
        }

        // 按创建人部门筛选
        if (loanPricingWorkflow.getDeptId() != null)
        {
            wrapper.eq(LoanPricingWorkflow::getDeptId, loanPricingWorkflow.getDeptId());
        }

        return wrapper;
    }

    private LoanPricingWorkflow applyWorkflowListDataScope(LoanPricingWorkflow query)
    {
        LoanPricingWorkflow scopedQuery = query == null ? new LoanPricingWorkflow() : query;
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (canViewAllWorkflows(loginUser))
        {
            return scopedQuery;
        }
        if (canViewBranchWorkflows(loginUser))
        {
            scopedQuery.setDataScopeDeptId(loginUser.getDeptId() == null ? -1L : loginUser.getDeptId());
        }
        else
        {
            scopedQuery.setDataScopeCreateBy(buildCurrentCreateBy(loginUser));
        }
        return scopedQuery;
    }

    private boolean canViewAllWorkflows(LoginUser loginUser)
    {
        SysUser user = loginUser.getUser();
        if (user.isAdmin())
        {
            return true;
        }
        List<SysRole> roles = user.getRoles();
        if (roles == null)
        {
            return false;
        }
        return roles.stream().anyMatch(role -> role != null
                && UserConstants.ROLE_NORMAL.equals(role.getStatus())
                && (WORKFLOW_ADMIN_ROLE_NAME.equals(role.getRoleName())
                || WORKFLOW_ADMIN_ROLE_KEY.equals(role.getRoleKey())));
    }

    private boolean canViewBranchWorkflows(LoginUser loginUser)
    {
        List<SysRole> roles = loginUser.getUser().getRoles();
        if (roles == null)
        {
            return false;
        }
        return roles.stream().anyMatch(role -> role != null
                && UserConstants.ROLE_NORMAL.equals(role.getStatus())
                && (WORKFLOW_BRANCH_ADMIN_ROLE_NAME.equals(role.getRoleName())
                || WORKFLOW_BRANCH_ADMIN_ROLE_KEY.equals(role.getRoleKey())));
    }

    private String buildCurrentCreateBy(LoginUser loginUser)
    {
        SysUser user = loginUser.getUser();
        return user.getNickName() + "-" + loginUser.getUsername();
    }

    /**
     * 设定执行利率
     *
     * @param serialNum   业务方流水号
     * @param executeRate 执行利率
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean setExecuteRate(String serialNum, String executeRate) {
        LambdaQueryWrapper<LoanPricingWorkflow> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LoanPricingWorkflow::getSerialNum, serialNum);
        LoanPricingWorkflow workflow = loanPricingWorkflowMapper.selectOne(wrapper);

        if (workflow == null) {
            return false;
        }

        workflow.setExecuteRate(executeRate);
        int result = loanPricingWorkflowMapper.updateById(workflow);
        return result > 0;
    }

    @Override
    public Map<String, Long> decryptWorkflowSensitiveFields()
    {
        long processed = 0L;
        long updated = 0L;
        long skipped = 0L;
        long failed = 0L;

        LambdaQueryWrapper<LoanPricingWorkflow> wrapper = new LambdaQueryWrapper<>();
        wrapper.and(q -> q.isNotNull(LoanPricingWorkflow::getCustName)
                .or()
                .isNotNull(LoanPricingWorkflow::getIdNum));
        List<LoanPricingWorkflow> workflows = loanPricingWorkflowMapper.selectList(wrapper);

        for (LoanPricingWorkflow workflow : workflows)
        {
            processed++;
            LoanPricingWorkflow update = new LoanPricingWorkflow();
            update.setId(workflow.getId());
            boolean changed = false;

            String decryptedCustName = tryDecrypt(workflow.getCustName());
            if (decryptedCustName != null && !Objects.equals(decryptedCustName, workflow.getCustName()))
            {
                update.setCustName(decryptedCustName);
                changed = true;
            }

            String decryptedIdNum = tryDecrypt(workflow.getIdNum());
            if (decryptedIdNum != null && !Objects.equals(decryptedIdNum, workflow.getIdNum()))
            {
                update.setIdNum(decryptedIdNum);
                changed = true;
            }

            if (!changed)
            {
                skipped++;
                continue;
            }

            try
            {
                loanPricingWorkflowMapper.updateSensitiveFieldsById(update.getId(), update.getCustName(), update.getIdNum());
                updated++;
            }
            catch (RuntimeException ex)
            {
                failed++;
            }
        }

        Map<String, Long> result = new LinkedHashMap<>();
        result.put("processed", processed);
        result.put("updated", updated);
        result.put("skipped", skipped);
        result.put("failed", failed);
        return result;
    }

    private String tryDecrypt(String value)
    {
        if (!StringUtils.hasText(value))
        {
            return null;
        }
        try
        {
            return sensitiveFieldCryptoService.decrypt(value);
        }
        catch (RuntimeException ex)
        {
            return null;
        }
    }
}
