package cn.iocoder.yudao.module.asset.service.contract;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.asset.controller.admin.contract.vo.*;
import cn.iocoder.yudao.module.asset.convert.contract.ContractConvert;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.*;
import cn.iocoder.yudao.module.asset.dal.mysql.contract.*;
import cn.iocoder.yudao.module.asset.enums.contract.ContractApprovalStatusEnum;
import cn.iocoder.yudao.module.asset.enums.contract.ContractStatusEnum;
import cn.iocoder.yudao.module.asset.service.contract.listener.ContractBpmListener;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCancelReqDTO;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.asset.enums.ErrorCodeConstants.*;

/**
 * 车辆租赁合同 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class ContractServiceImpl implements ContractService {

    @Resource
    private ContractMapper contractMapper;

    @Resource
    private ContractVehicleMapper contractVehicleMapper;

    @Resource
    private ContractVehicleServiceMapper contractVehicleServiceMapper;

    @Resource
    private ContractAuthorizedMapper contractAuthorizedMapper;

    @Resource
    private ContractAttachmentMapper contractAttachmentMapper;

    @Resource
    private ContractChangeHistoryMapper contractChangeHistoryMapper;

    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createContract(ContractSaveReqVO createReqVO) {
        // 校验合同日期
        validateContractDate(createReqVO.getStartDate(), createReqVO.getEndDate());

        // 转换并插入合同主表
        ContractDO contract = ContractConvert.INSTANCE.convert(createReqVO);

        // 生成合同编码
        if (contract.getContractCode() == null || contract.getContractCode().isEmpty()) {
            contract.setContractCode(generateContractCode());
        }

        // 设置初始状态
        contract.setApprovalStatus(ContractApprovalStatusEnum.DRAFT.getStatus());
        contract.setContractStatus(0); // 草稿

        contractMapper.insert(contract);

        // 保存车辆订单
        saveContractVehicles(contract.getId(), createReqVO.getVehicles());

        // 保存被授权人
        saveContractAuthorized(contract.getId(), createReqVO.getAuthorizedPersons());

        // 记录变更历史
        saveChangeHistory(contract.getId(), "创建合同", "创建合同：" + contract.getContractCode());

        return contract.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateContract(ContractSaveReqVO updateReqVO) {
        // 校验存在
        ContractDO existContract = validateContractExists(updateReqVO.getId());

        // 校验状态：只有草稿、审批拒绝、已撤回状态才能修改
        if (!canEdit(existContract.getApprovalStatus())) {
            throw exception(CONTRACT_STATUS_NOT_ALLOW_UPDATE);
        }

        // 校验合同日期
        validateContractDate(updateReqVO.getStartDate(), updateReqVO.getEndDate());

        // 转换并更新合同主表
        ContractDO updateObj = ContractConvert.INSTANCE.convert(updateReqVO);
        contractMapper.updateById(updateObj);

        // 更新车辆订单：先删除旧的，再插入新的
        List<ContractVehicleDO> oldVehicles = contractVehicleMapper.selectListByContractId(updateReqVO.getId());
        List<Long> oldVehicleIds = oldVehicles.stream().map(ContractVehicleDO::getId).collect(Collectors.toList());
        if (!oldVehicleIds.isEmpty()) {
            contractVehicleServiceMapper.deleteByContractVehicleIds(oldVehicleIds);
        }
        contractVehicleMapper.deleteByContractId(updateReqVO.getId());
        saveContractVehicles(updateReqVO.getId(), updateReqVO.getVehicles());

        // 更新被授权人：先删除旧的，再插入新的
        contractAuthorizedMapper.deleteByContractId(updateReqVO.getId());
        saveContractAuthorized(updateReqVO.getId(), updateReqVO.getAuthorizedPersons());

        // 记录变更历史
        saveChangeHistory(updateReqVO.getId(), "更新合同", "更新合同信息");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteContract(Long id) {
        // 校验存在
        ContractDO contract = validateContractExists(id);

        // 校验状态：只有草稿、审批拒绝、已撤回状态才能删除
        if (!canEdit(contract.getApprovalStatus())) {
            throw exception(CONTRACT_STATUS_NOT_ALLOW_DELETE);
        }

        // 删除合同主表
        contractMapper.deleteById(id);

        // 删除关联数据
        List<ContractVehicleDO> vehicles = contractVehicleMapper.selectListByContractId(id);
        List<Long> vehicleIds = vehicles.stream().map(ContractVehicleDO::getId).collect(Collectors.toList());
        if (!vehicleIds.isEmpty()) {
            contractVehicleServiceMapper.deleteByContractVehicleIds(vehicleIds);
        }
        contractVehicleMapper.deleteByContractId(id);
        contractAuthorizedMapper.deleteByContractId(id);
        contractAttachmentMapper.deleteByContractId(id);

        // 记录变更历史
        saveChangeHistory(id, "删除合同", "删除合同：" + contract.getContractCode());
    }

    @Override
    public ContractDO getContract(Long id) {
        return contractMapper.selectById(id);
    }

    @Override
    public PageResult<ContractDO> getContractPage(ContractPageReqVO pageReqVO) {
        return contractMapper.selectPage(pageReqVO);
    }

    // ==================== 私有方法 ====================

    /**
     * 校验合同是否存在
     */
    private ContractDO validateContractExists(Long id) {
        ContractDO contract = contractMapper.selectById(id);
        if (contract == null) {
            throw exception(CONTRACT_NOT_EXISTS);
        }
        return contract;
    }

    /**
     * 校验合同日期
     */
    private void validateContractDate(java.time.LocalDate startDate, java.time.LocalDate endDate) {
        if (startDate != null && endDate != null && endDate.isBefore(startDate)) {
            throw exception(CONTRACT_END_DATE_BEFORE_START_DATE);
        }
    }

    /**
     * 判断是否可以编辑
     */
    private boolean canEdit(Integer approvalStatus) {
        return ContractApprovalStatusEnum.DRAFT.getStatus().equals(approvalStatus)
                || ContractApprovalStatusEnum.REJECTED.getStatus().equals(approvalStatus)
                || ContractApprovalStatusEnum.WITHDRAWN.getStatus().equals(approvalStatus);
    }

    /**
     * 生成合同编码
     */
    private String generateContractCode() {
        // 格式：HT-YYYY-NNNN
        String year = String.valueOf(java.time.LocalDate.now().getYear());

        // 查询当年最大序号
        String prefix = "HT-" + year + "-";
        String maxCode = contractMapper.selectMaxContractCodeByPrefix(prefix);

        int nextSeq = 1;
        if (maxCode != null && maxCode.startsWith(prefix)) {
            try {
                String seqStr = maxCode.substring(prefix.length());
                nextSeq = Integer.parseInt(seqStr) + 1;
            } catch (Exception e) {
                log.warn("解析合同编码失败: {}", maxCode, e);
            }
        }

        return prefix + String.format("%04d", nextSeq);
    }

    /**
     * 保存车辆订单
     */
    private void saveContractVehicles(Long contractId, List<ContractSaveReqVO.ContractVehicleSaveVO> vehicles) {
        if (vehicles == null || vehicles.isEmpty()) {
            return;
        }

        // 获取合同信息用于校验
        ContractDO contract = contractMapper.selectById(contractId);

        for (ContractSaveReqVO.ContractVehicleSaveVO vehicleVO : vehicles) {
            // 校验车辆时间冲突
            if (vehicleVO.getVehicleId() != null && contract != null) {
                validateVehicleTimeConflict(vehicleVO.getVehicleId(), contract.getStartDate(), contract.getEndDate(), contractId);
            }

            // 保存车辆订单
            ContractVehicleDO vehicle = ContractConvert.INSTANCE.convertVehicle(vehicleVO);
            vehicle.setContractId(contractId);
            contractVehicleMapper.insert(vehicle);

            // 保存服务项目
            if (vehicleVO.getServices() != null && !vehicleVO.getServices().isEmpty()) {
                List<ContractVehicleServiceDO> services = ContractConvert.INSTANCE.convertServiceList(vehicleVO.getServices());
                for (ContractVehicleServiceDO service : services) {
                    service.setContractVehicleId(vehicle.getId());
                    contractVehicleServiceMapper.insert(service);
                }
            }
        }
    }

    /**
     * 校验车辆时间冲突
     */
    private void validateVehicleTimeConflict(Long vehicleId, java.time.LocalDate startDate, java.time.LocalDate endDate, Long excludeContractId) {
        int count = contractMapper.countActiveContractsByVehicleAndDateRange(vehicleId, startDate, endDate);
        if (count > 0) {
            throw exception(CONTRACT_VEHICLE_TIME_CONFLICT);
        }
    }

    /**
     * 保存被授权人
     */
    private void saveContractAuthorized(Long contractId, List<ContractAuthorizedVO> authorizedPersons) {
        if (authorizedPersons == null || authorizedPersons.isEmpty()) {
            return;
        }

        List<ContractAuthorizedDO> authorizedList = ContractConvert.INSTANCE.convertAuthorizedList(authorizedPersons);
        for (ContractAuthorizedDO authorized : authorizedList) {
            authorized.setContractId(contractId);
            contractAuthorizedMapper.insert(authorized);
        }
    }

    /**
     * 保存变更历史
     */
    private void saveChangeHistory(Long contractId, String changeType, String changeContent) {
        ContractChangeHistoryDO history = new ContractChangeHistoryDO();
        history.setContractId(contractId);
        history.setChangeType(changeType);
        history.setChangeContent(changeContent);

        // 获取当前登录用户
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        history.setOperator(userId != null ? String.valueOf(userId) : "system");
        history.setOperateTime(LocalDateTime.now());

        contractChangeHistoryMapper.insert(history);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String submitContractApproval(Long id) {
        // 校验存在
        ContractDO contract = validateContractExists(id);

        // 校验状态：只有草稿、审批拒绝、已撤回状态才能提交审批
        if (!canEdit(contract.getApprovalStatus())) {
            throw exception(CONTRACT_STATUS_NOT_ALLOW_SUBMIT);
        }

        // 创建流程变量
        Map<String, Object> variables = new HashMap<>();
        variables.put("contractCode", contract.getContractCode());
        variables.put("projectName", contract.getProjectName());
        variables.put("customerName", contract.getCustomerName());
        variables.put("startDate", contract.getStartDate());
        variables.put("endDate", contract.getEndDate());

        // 创建流程实例
        BpmProcessInstanceCreateReqDTO reqDTO = new BpmProcessInstanceCreateReqDTO();
        reqDTO.setProcessDefinitionKey(ContractBpmListener.PROCESS_KEY);
        reqDTO.setBusinessKey(String.valueOf(id));
        reqDTO.setVariables(variables);

        Long userId = SecurityFrameworkUtils.getLoginUserId();
        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, reqDTO).getData();

        // 更新合同状态
        ContractDO updateObj = new ContractDO();
        updateObj.setId(id);
        updateObj.setApprovalStatus(ContractApprovalStatusEnum.APPROVING.getStatus());
        updateObj.setBpmInstanceId(processInstanceId);
        contractMapper.updateById(updateObj);

        // 记录变更历史
        saveChangeHistory(id, "提交审批", "提交合同审批，流程实例ID：" + processInstanceId);

        return processInstanceId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateContractApprovalStatus(Long id, Integer status) {
        // 校验存在
        ContractDO contract = validateContractExists(id);

        // 更新审批状态
        ContractDO updateObj = new ContractDO();
        updateObj.setId(id);

        // 根据 BPM 状态映射到合同审批状态
        if (BpmProcessInstanceStatusEnum.RUNNING.getStatus().equals(status)) {
            updateObj.setApprovalStatus(ContractApprovalStatusEnum.APPROVING.getStatus());
        } else if (BpmProcessInstanceStatusEnum.APPROVE.getStatus().equals(status)) {
            updateObj.setApprovalStatus(ContractApprovalStatusEnum.APPROVED.getStatus());
            updateObj.setContractStatus(ContractStatusEnum.PENDING.getStatus()); // 待生效
            updateObj.setEffectiveTime(LocalDateTime.now());
        } else if (BpmProcessInstanceStatusEnum.REJECT.getStatus().equals(status)) {
            updateObj.setApprovalStatus(ContractApprovalStatusEnum.REJECTED.getStatus());
        } else if (BpmProcessInstanceStatusEnum.CANCEL.getStatus().equals(status)) {
            updateObj.setApprovalStatus(ContractApprovalStatusEnum.WITHDRAWN.getStatus());
        }

        contractMapper.updateById(updateObj);

        // 记录变更历史
        String changeContent = "审批状态变更：" + getApprovalStatusName(updateObj.getApprovalStatus());
        saveChangeHistory(id, "审批状态变更", changeContent);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void withdrawContractApproval(Long id) {
        // 校验存在
        ContractDO contract = validateContractExists(id);

        // 校验状态：只有审批中状态才能撤回
        if (!ContractApprovalStatusEnum.APPROVING.getStatus().equals(contract.getApprovalStatus())) {
            throw exception(CONTRACT_STATUS_NOT_ALLOW_WITHDRAW);
        }

        // 调用 BPM API 取消流程实例
        if (contract.getBpmInstanceId() != null) {
            BpmProcessInstanceCancelReqDTO cancelReqDTO = new BpmProcessInstanceCancelReqDTO();
            cancelReqDTO.setId(contract.getBpmInstanceId());
            cancelReqDTO.setReason("合同审批撤回");
            Long userId = SecurityFrameworkUtils.getLoginUserId();
            bpmProcessInstanceApi.cancelProcessInstance(userId, cancelReqDTO);
        }

        // 更新合同状态
        ContractDO updateObj = new ContractDO();
        updateObj.setId(id);
        updateObj.setApprovalStatus(ContractApprovalStatusEnum.WITHDRAWN.getStatus());
        contractMapper.updateById(updateObj);

        // 记录变更历史
        saveChangeHistory(id, "撤回审批", "撤回合同审批");
    }

    /**
     * 获取审批状态名称
     */
    private String getApprovalStatusName(Integer status) {
        ContractApprovalStatusEnum statusEnum = ContractApprovalStatusEnum.valueOf(status);
        return statusEnum != null ? statusEnum.getName() : "未知";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void terminateContract(Long id, String reason) {
        // 校验存在
        ContractDO contract = validateContractExists(id);

        // 校验状态：只有进行中的合同才能终止
        if (!ContractStatusEnum.IN_PROGRESS.getStatus().equals(contract.getContractStatus())) {
            throw exception(CONTRACT_STATUS_NOT_ALLOW_TERMINATE);
        }

        // 更新合同状态
        ContractDO updateObj = new ContractDO();
        updateObj.setId(id);
        updateObj.setContractStatus(ContractStatusEnum.TERMINATED.getStatus());
        updateObj.setTerminateTime(LocalDateTime.now());
        updateObj.setTerminateReason(reason);
        contractMapper.updateById(updateObj);

        // 记录变更历史
        saveChangeHistory(id, "终止合同", "终止合同，原因：" + reason);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long renewContract(Long id, ContractSaveReqVO newContractReqVO) {
        // 校验原合同存在
        ContractDO oldContract = validateContractExists(id);

        // 校验状态：只有进行中或已到期的合同才能续签
        if (!ContractStatusEnum.IN_PROGRESS.getStatus().equals(oldContract.getContractStatus())
                && !ContractStatusEnum.EXPIRED.getStatus().equals(oldContract.getContractStatus())) {
            throw exception(CONTRACT_STATUS_NOT_ALLOW_RENEW);
        }

        // 创建新合同
        Long newContractId = createContract(newContractReqVO);

        // 更新原合同状态
        ContractDO updateObj = new ContractDO();
        updateObj.setId(id);
        updateObj.setContractStatus(ContractStatusEnum.RENEWED.getStatus());
        updateObj.setRenewedContractId(newContractId);
        contractMapper.updateById(updateObj);

        // 更新新合同的原合同ID
        ContractDO newContract = new ContractDO();
        newContract.setId(newContractId);
        newContract.setOriginalContractId(id);
        contractMapper.updateById(newContract);

        // 记录变更历史
        saveChangeHistory(id, "续签合同", "续签合同，新合同ID：" + newContractId);
        saveChangeHistory(newContractId, "续签合同", "由合同ID " + id + " 续签而来");

        return newContractId;
    }

    @Override
    public List<ContractDO> getContractSimpleList() {
        return contractMapper.selectList();
    }

    @Override
    public ContractDetailRespVO getContractDetail(Long id) {
        ContractDO contract = validateContractExists(id);
        ContractDetailRespVO result = ContractConvert.INSTANCE.convertDetail(contract);

        // 查询车辆订单
        List<ContractVehicleDO> vehicles = contractVehicleMapper.selectListByContractId(id);
        List<ContractDetailRespVO.ContractVehicleDetailVO> vehicleDetails = ContractConvert.INSTANCE.convertVehicleDetailList(vehicles);

        // 查询每个车辆的服务项目
        for (int i = 0; i < vehicles.size(); i++) {
            List<ContractVehicleServiceDO> services = contractVehicleServiceMapper.selectListByContractVehicleId(vehicles.get(i).getId());
            vehicleDetails.get(i).setServices(ContractConvert.INSTANCE.convertServiceVOList(services));
        }
        result.setVehicles(vehicleDetails);

        // 查询被授权人
        List<ContractAuthorizedDO> authorized = contractAuthorizedMapper.selectListByContractId(id);
        result.setAuthorizedPersons(ContractConvert.INSTANCE.convertAuthorizedVOList(authorized));

        // 查询附件
        List<ContractAttachmentDO> attachments = contractAttachmentMapper.selectListByContractId(id);
        result.setAttachments(ContractConvert.INSTANCE.convertAttachmentVOList(attachments));

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long convertToThirdParty(Long id, ContractSaveReqVO newContractReqVO) {
        // 校验原合同存在且进行中
        ContractDO oldContract = validateContractExists(id);
        if (!ContractStatusEnum.IN_PROGRESS.getStatus().equals(oldContract.getContractStatus())) {
            throw exception(CONTRACT_STATUS_NOT_ALLOW_CONVERT);
        }

        // 设置三方合同标记
        newContractReqVO.setThirdPartyEnabled(true);

        // 创建新合同
        Long newContractId = createContract(newContractReqVO);

        // 更新新合同的原合同ID
        ContractDO newContract = new ContractDO();
        newContract.setId(newContractId);
        newContract.setOriginalContractId(id);
        contractMapper.updateById(newContract);

        // 终止原合同
        ContractDO updateObj = new ContractDO();
        updateObj.setId(id);
        updateObj.setContractStatus(ContractStatusEnum.TERMINATED.getStatus());
        updateObj.setTerminateTime(LocalDateTime.now());
        updateObj.setTerminateReason("变更为三方合同");
        contractMapper.updateById(updateObj);

        // 记录变更历史
        saveChangeHistory(id, "变更为三方", "变更为三方合同，新合同ID：" + newContractId);
        saveChangeHistory(newContractId, "变更为三方", "由合同ID " + id + " 变更而来");

        return newContractId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long convertToFormal(Long id, ContractSaveReqVO newContractReqVO) {
        // 校验原合同存在且进行中
        ContractDO oldContract = validateContractExists(id);
        if (!ContractStatusEnum.IN_PROGRESS.getStatus().equals(oldContract.getContractStatus())) {
            throw exception(CONTRACT_STATUS_NOT_ALLOW_CONVERT);
        }
        // 校验原合同为试用合同
        if (!Integer.valueOf(1).equals(oldContract.getContractType())) {
            throw exception(CONTRACT_TYPE_NOT_TRIAL);
        }

        // 设置为正式合同
        newContractReqVO.setContractType(2);

        // 创建新合同
        Long newContractId = createContract(newContractReqVO);

        // 更新新合同的原合同ID
        ContractDO newContract = new ContractDO();
        newContract.setId(newContractId);
        newContract.setOriginalContractId(id);
        contractMapper.updateById(newContract);

        // 终止原合同
        ContractDO updateObj = new ContractDO();
        updateObj.setId(id);
        updateObj.setContractStatus(ContractStatusEnum.TERMINATED.getStatus());
        updateObj.setTerminateTime(LocalDateTime.now());
        updateObj.setTerminateReason("转正式合同");
        contractMapper.updateById(updateObj);

        // 记录变更历史
        saveChangeHistory(id, "转正式", "转正式合同，新合同ID：" + newContractId);
        saveChangeHistory(newContractId, "转正式", "由试用合同ID " + id + " 转正式而来");

        return newContractId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addVehiclesToContract(Long id, List<ContractSaveReqVO.ContractVehicleSaveVO> vehicles) {
        // 校验合同存在且进行中
        ContractDO contract = validateContractExists(id);
        if (!ContractStatusEnum.IN_PROGRESS.getStatus().equals(contract.getContractStatus())) {
            throw exception(CONTRACT_STATUS_NOT_ALLOW_ADD_VEHICLE);
        }

        // 追加车辆
        saveContractVehicles(id, vehicles);

        // 记录变更历史
        saveChangeHistory(id, "新增车辆", "追加 " + vehicles.size() + " 辆车辆");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void uploadSealedContract(Long id, String fileUrl, String fileName) {
        // 校验合同存在
        validateContractExists(id);

        // 创建附件记录
        ContractAttachmentDO attachment = new ContractAttachmentDO();
        attachment.setContractId(id);
        attachment.setAttachmentType(2); // 盖章合同
        attachment.setFileName(fileName);
        attachment.setFileUrl(fileUrl);
        attachment.setUploadTime(LocalDateTime.now());
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        attachment.setUploader(userId != null ? String.valueOf(userId) : "system");
        contractAttachmentMapper.insert(attachment);

        // 记录变更历史
        saveChangeHistory(id, "上传盖章合同", "上传盖章合同附件：" + fileName);
    }

}
