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

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.*;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryTaskActivateReqVO;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryTaskContractGroupRespVO;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryTaskVehicleVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractDO;
import cn.iocoder.yudao.module.asset.dal.dataobject.delivery.DeliveryTaskDO;
import cn.iocoder.yudao.module.asset.dal.dataobject.delivery.DeliveryTaskVehicleDO;
import cn.iocoder.yudao.module.asset.dal.mysql.contract.ContractMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.delivery.DeliveryTaskMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.delivery.DeliveryTaskVehicleMapper;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import jakarta.annotation.Resource;
import java.util.*;
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
@Validated
public class DeliveryTaskServiceImpl implements DeliveryTaskService {

    @Resource
    private DeliveryTaskMapper deliveryTaskMapper;

    @Resource
    private DeliveryTaskVehicleMapper deliveryTaskVehicleMapper;

    @Resource
    private ContractMapper contractMapper;

    @Resource
    private DeptApi deptApi;

    @Resource
    private AdminUserApi adminUserApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createDeliveryTask(DeliveryTaskSaveReqVO createReqVO) {
        // 校验合同是否存在
        if (contractMapper.selectById(createReqVO.getContractId()) == null) {
            throw exception(CONTRACT_NOT_EXISTS);
        }

        // Generate task code: contractCode + JC + sequence
        String taskCode = generateTaskCode(createReqVO.getContractId(), createReqVO.getContractCode());

        DeliveryTaskDO task = BeanUtils.toBean(createReqVO, DeliveryTaskDO.class);
        task.setTaskCode(taskCode);
        task.setTaskStatus(0); // active
        task.setDeliveryStatus(0); // not delivered
        task.setVehicleCount(createReqVO.getVehicles() != null ? createReqVO.getVehicles().size() : 0);
        deliveryTaskMapper.insert(task);

        // Insert vehicles
        if (createReqVO.getVehicles() != null) {
            for (DeliveryTaskVehicleVO vehicleVO : createReqVO.getVehicles()) {
                DeliveryTaskVehicleDO vehicle = BeanUtils.toBean(vehicleVO, DeliveryTaskVehicleDO.class);
                vehicle.setTaskId(task.getId());
                vehicle.setIsDelivered(false);
                deliveryTaskVehicleMapper.insert(vehicle);
            }
        }

        return task.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDeliveryTask(DeliveryTaskSaveReqVO updateReqVO) {
        DeliveryTaskDO task = validateDeliveryTaskExists(updateReqVO.getId());
        if (task.getDeliveryStatus().equals(1)) {
            throw exception(DELIVERY_TASK_ALREADY_DELIVERED);
        }

        DeliveryTaskDO updateObj = BeanUtils.toBean(updateReqVO, DeliveryTaskDO.class);
        updateObj.setVehicleCount(updateReqVO.getVehicles() != null ? updateReqVO.getVehicles().size() : 0);
        deliveryTaskMapper.updateById(updateObj);

        // Update vehicles: delete old, insert new
        if (updateReqVO.getVehicles() != null) {
            deliveryTaskVehicleMapper.deleteByTaskId(updateReqVO.getId());
            for (DeliveryTaskVehicleVO vehicleVO : updateReqVO.getVehicles()) {
                DeliveryTaskVehicleDO vehicle = BeanUtils.toBean(vehicleVO, DeliveryTaskVehicleDO.class);
                vehicle.setTaskId(updateReqVO.getId());
                vehicle.setIsDelivered(false);
                deliveryTaskVehicleMapper.insert(vehicle);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteDeliveryTask(Long id) {
        validateDeliveryTaskExists(id);
        deliveryTaskMapper.deleteById(id);
        deliveryTaskVehicleMapper.deleteByTaskId(id);
    }

    @Override
    public DeliveryTaskDO getDeliveryTask(Long id) {
        return deliveryTaskMapper.selectById(id);
    }

    @Override
    public DeliveryTaskRespVO getDeliveryTaskDetail(Long id) {
        DeliveryTaskDO task = validateDeliveryTaskExists(id);
        DeliveryTaskRespVO respVO = BeanUtils.toBean(task, DeliveryTaskRespVO.class);
        List<DeliveryTaskVehicleDO> vehicles = deliveryTaskVehicleMapper.selectListByTaskId(id);
        respVO.setVehicles(BeanUtils.toBean(vehicles, DeliveryTaskVehicleVO.class));
        return respVO;
    }

    @Override
    public PageResult<DeliveryTaskDO> getDeliveryTaskPage(DeliveryTaskPageReqVO pageReqVO) {
        return deliveryTaskMapper.selectPage(pageReqVO);
    }

    @Override
    public void suspendDeliveryTask(Long id) {
        DeliveryTaskDO task = validateDeliveryTaskExists(id);
        if (task.getDeliveryStatus().equals(1)) {
            throw exception(DELIVERY_TASK_ALREADY_DELIVERED);
        }
        deliveryTaskMapper.updateById(DeliveryTaskDO.builder().id(id).taskStatus(1).build());
    }

    @Override
    public void activateDeliveryTask(Long id) {
        validateDeliveryTaskExists(id);
        deliveryTaskMapper.updateById(DeliveryTaskDO.builder().id(id).taskStatus(0).build());
    }

    @Override
    public void activateDeliveryTask(DeliveryTaskActivateReqVO reqVO) {
        validateDeliveryTaskExists(reqVO.getId());
        DeliveryTaskDO updateObj = DeliveryTaskDO.builder()
                .id(reqVO.getId())
                .taskStatus(0)
                .expectedDeliveryDateStart(reqVO.getExpectedDeliveryDateStart())
                .expectedDeliveryDateEnd(reqVO.getExpectedDeliveryDateEnd())
                .billingStartDate(reqVO.getBillingStartDate())
                .build();
        deliveryTaskMapper.updateById(updateObj);
    }

    @Override
    public PageResult<DeliveryTaskContractGroupRespVO> getDeliveryTaskContractPage(DeliveryTaskPageReqVO pageReqVO) {
        // 1. 查询所有匹配的交车任务
        List<DeliveryTaskDO> allTasks = deliveryTaskMapper.selectListByFilters(pageReqVO);
        if (allTasks.isEmpty()) {
            return new PageResult<>(Collections.emptyList(), 0L);
        }

        // 2. 按合同 ID 分组
        Map<Long, List<DeliveryTaskDO>> groupedByContract = allTasks.stream()
                .collect(Collectors.groupingBy(DeliveryTaskDO::getContractId,
                        LinkedHashMap::new, Collectors.toList()));

        // 3. 对合同分组进行分页
        List<Long> contractIds = new ArrayList<>(groupedByContract.keySet());
        long total = contractIds.size();
        int start = (pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize();
        int end = Math.min(start + pageReqVO.getPageSize(), contractIds.size());
        if (start >= contractIds.size()) {
            return new PageResult<>(Collections.emptyList(), total);
        }
        List<Long> pageContractIds = contractIds.subList(start, end);

        // 4. 批量查询合同信息
        List<ContractDO> contracts = contractMapper.selectBatchIds(pageContractIds);
        Map<Long, ContractDO> contractMap = contracts.stream()
                .collect(Collectors.toMap(ContractDO::getId, c -> c, (a, b) -> a));

        // 5. 批量查询部门和用户名称
        Set<Long> deptIds = new HashSet<>();
        Set<Long> userIds = new HashSet<>();
        contracts.forEach(c -> {
            if (c.getBusinessDeptId() != null) deptIds.add(c.getBusinessDeptId());
            if (c.getBusinessManagerId() != null) userIds.add(c.getBusinessManagerId());
        });
        Map<Long, DeptRespDTO> deptMap = deptIds.isEmpty() ? Collections.emptyMap() : deptApi.getDeptMap(deptIds);
        Map<Long, AdminUserRespDTO> userMap = userIds.isEmpty() ? Collections.emptyMap() : adminUserApi.getUserMap(userIds);

        // 6. 批量查询车辆信息（用于 popover 展示）
        List<Long> taskIds = pageContractIds.stream()
                .flatMap(cid -> groupedByContract.get(cid).stream())
                .map(DeliveryTaskDO::getId)
                .toList();
        List<DeliveryTaskVehicleDO> allVehicles = taskIds.isEmpty() ? Collections.emptyList()
                : deliveryTaskVehicleMapper.selectList(
                    new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<DeliveryTaskVehicleDO>()
                        .in(DeliveryTaskVehicleDO::getTaskId, taskIds));
        Map<Long, List<DeliveryTaskVehicleDO>> vehiclesByTask = allVehicles.stream()
                .collect(Collectors.groupingBy(DeliveryTaskVehicleDO::getTaskId));

        // 7. 组装响应
        List<DeliveryTaskContractGroupRespVO> result = new ArrayList<>();
        for (Long contractId : pageContractIds) {
            DeliveryTaskContractGroupRespVO group = new DeliveryTaskContractGroupRespVO();
            ContractDO contract = contractMap.get(contractId);
            if (contract != null) {
                group.setContractId(contract.getId());
                group.setContractCode(contract.getContractCode());
                group.setProjectName(contract.getProjectName());
                group.setCustomerName(contract.getCustomerName());
                group.setStartDate(contract.getStartDate());
                group.setEndDate(contract.getEndDate());
                DeptRespDTO dept = deptMap.get(contract.getBusinessDeptId());
                group.setBusinessDeptName(dept != null ? dept.getName() : null);
                AdminUserRespDTO user = userMap.get(contract.getBusinessManagerId());
                group.setBusinessManagerName(user != null ? user.getNickname() : null);
            } else {
                DeliveryTaskDO firstTask = groupedByContract.get(contractId).get(0);
                group.setContractId(contractId);
                group.setContractCode(firstTask.getContractCode());
                group.setProjectName(firstTask.getProjectName());
                group.setCustomerName(firstTask.getCustomerName());
            }

            List<DeliveryTaskRespVO> taskVOs = groupedByContract.get(contractId).stream().map(task -> {
                DeliveryTaskRespVO vo = BeanUtils.toBean(task, DeliveryTaskRespVO.class);
                List<DeliveryTaskVehicleDO> vehicles = vehiclesByTask.getOrDefault(task.getId(), Collections.emptyList());
                vo.setVehicles(BeanUtils.toBean(vehicles, DeliveryTaskVehicleVO.class));
                return vo;
            }).toList();
            group.setTasks(taskVOs);
            result.add(group);
        }

        return new PageResult<>(result, total);
    }

    @Override
    public List<DeliveryTaskDO> getDeliveryTaskSimpleList() {
        return deliveryTaskMapper.selectList();
    }

    private DeliveryTaskDO validateDeliveryTaskExists(Long id) {
        DeliveryTaskDO task = deliveryTaskMapper.selectById(id);
        if (task == null) {
            throw exception(DELIVERY_TASK_NOT_EXISTS);
        }
        return task;
    }

    private String generateTaskCode(Long contractId, String contractCode) {
        long count = deliveryTaskMapper.selectCountByContractId(contractId);
        return contractCode + "JC" + String.format("%04d", count + 1);
    }

}
