package com.aegis.emergency.dispatch.service.impl;

import com.aegis.common.exception.ServiceException;
import com.aegis.common.utils.SecurityUtils;
import com.aegis.common.utils.StringUtils;
import com.aegis.emergency.dispatch.constant.ResourceType;
import com.aegis.emergency.dispatch.constant.ResourceUsageStatus;
import com.aegis.emergency.dispatch.domain.DispatchResourceUsageLink;
import com.aegis.emergency.dispatch.dto.EquipmentAllocationRequest;
import com.aegis.emergency.dispatch.dto.MaterialAllocationRequest;
import com.aegis.emergency.dispatch.dto.VehicleAllocationRequest;
import com.aegis.emergency.dispatch.mapper.DispatchResourceUsageLinkMapper;
import com.aegis.emergency.dispatch.service.IDispatchResourceUsageLinkService;
import com.aegis.resource.equipment.service.IResourceEquipmentService;
import com.aegis.resource.material.dto.InventoryAction;
import com.aegis.resource.material.service.IResourceMaterialInventoryService;
import com.aegis.resource.vehicle.domain.Vehicle;
import com.aegis.resource.vehicle.service.IResourceVehicleService;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 调度任务资源 usage 关联 Service 实现
 *
 * @author aegis
 */
@Service
public class DispatchResourceUsageLinkServiceImpl
        extends ServiceImpl<DispatchResourceUsageLinkMapper, DispatchResourceUsageLink>
        implements IDispatchResourceUsageLinkService {

    @Autowired
    private IResourceMaterialInventoryService materialInventoryService;

    @Autowired
    private IResourceEquipmentService equipmentService;

    @Autowired
    private IResourceVehicleService vehicleService;

    @Override
    public List<DispatchResourceUsageLink> listByTaskId(Long taskId) {
        if (taskId == null) {
            return Collections.emptyList();
        }
        return this.list(new LambdaQueryWrapper<DispatchResourceUsageLink>()
            .eq(DispatchResourceUsageLink::getTaskId, taskId)
            .eq(DispatchResourceUsageLink::getDelFlag, "0")
            .orderByDesc(DispatchResourceUsageLink::getCreateTime));
    }

    @Override
    public List<DispatchResourceUsageLink> listByStepId(Long stepId) {
        if (stepId == null) {
            return Collections.emptyList();
        }
        return this.list(new LambdaQueryWrapper<DispatchResourceUsageLink>()
            .eq(DispatchResourceUsageLink::getStepId, stepId)
            .eq(DispatchResourceUsageLink::getDelFlag, "0")
            .orderByDesc(DispatchResourceUsageLink::getCreateTime));
    }

    @Override
    public List<DispatchResourceUsageLink> listByTaskAndType(Long taskId, String resourceType) {
        if (taskId == null || StringUtils.isBlank(resourceType)) {
            return Collections.emptyList();
        }
        return this.list(new LambdaQueryWrapper<DispatchResourceUsageLink>()
            .eq(DispatchResourceUsageLink::getTaskId, taskId)
            .eq(DispatchResourceUsageLink::getResourceType, resourceType)
            .eq(DispatchResourceUsageLink::getDelFlag, "0")
            .orderByDesc(DispatchResourceUsageLink::getCreateTime));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean detachResource(Long taskId, Long linkId) {
        DispatchResourceUsageLink link = requireLinkBelongToTask(taskId, linkId);
        ResourceType type = ResourceType.fromCode(link.getResourceType());
        if (type == null) {
            throw new ServiceException("不支持的资源类型：" + link.getResourceType());
        }
        switch (type) {
            case MATERIAL:
                ensureMaterialLinkIsReleasable(link);
                break;
            case EQUIPMENT:
                equipmentService.releaseEquipment(link.getResourceId(), "调度释放");
                break;
            case VEHICLE:
                markVehicleStatus(link.getResourceId(), "available");
                break;
            default:
                throw new ServiceException("未处理的资源类型：" + type.getCode());
        }
        link.setDelFlag("2");
        link.setStatus("released");
        String operator = SecurityUtils.getUsername();
        link.setUpdateBy(operator);
        link.setUpdateTime(new Date());
        return this.updateById(link);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DispatchResourceUsageLink allocateMaterialToStep(Long taskId, Long stepId, MaterialAllocationRequest request) {
        if (request == null || request.getMaterialId() == null) {
            throw new ServiceException("物资信息不能为空");
        }
        InventoryAction action = buildInventoryAction(taskId, request);
        boolean locked = materialInventoryService.lockInventory(action);
        if (!locked) {
            throw new ServiceException("物资锁定失败");
        }
        return createLink(taskId, stepId, ResourceType.MATERIAL, request.getMaterialId(), null,
            JSON.toJSONString(action), ResourceUsageStatus.LOCKED.getCode());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean releaseMaterialLink(Long taskId, Long linkId) {
        DispatchResourceUsageLink link = requireMaterialLink(taskId, linkId);
        return releaseMaterialLinkInternal(link);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean consumeMaterialLink(Long taskId, Long linkId) {
        DispatchResourceUsageLink link = requireMaterialLink(taskId, linkId);
        return consumeMaterialLinkInternal(link);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean returnMaterialLink(Long taskId, Long linkId) {
        DispatchResourceUsageLink link = requireMaterialLink(taskId, linkId);
        return returnMaterialLinkInternal(link);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DispatchResourceUsageLink allocateEquipmentToStep(Long taskId, Long stepId, EquipmentAllocationRequest request) {
        if (request == null || request.getEquipmentId() == null) {
            throw new ServiceException("装备信息不能为空");
        }
        int result = equipmentService.assignEquipment(request.getEquipmentId(), taskId, null, request.getNotes());
        if (result <= 0) {
            throw new ServiceException("装备分配失败");
        }
        // Store equipment allocation snapshot in note field (similar to materials)
        Map<String, Object> snapshot = new HashMap<>();
        snapshot.put("equipmentId", request.getEquipmentId());
        snapshot.put("warehouseId", request.getWarehouseId());
        snapshot.put("expectedFrom", request.getExpectedFrom());
        snapshot.put("expectedUntil", request.getExpectedUntil());
        snapshot.put("notes", request.getNotes());
        return createLink(taskId, stepId, ResourceType.EQUIPMENT, request.getEquipmentId(), null,
            JSON.toJSONString(snapshot), "assigned");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DispatchResourceUsageLink allocateVehicleToStep(Long taskId, Long stepId, VehicleAllocationRequest request) {
        if (request == null || request.getVehicleId() == null) {
            throw new ServiceException("车辆信息不能为空");
        }
        markVehicleStatus(request.getVehicleId(), "busy");
        // Store vehicle allocation snapshot in note field (similar to equipment)
        Map<String, Object> snapshot = new HashMap<>();
        snapshot.put("vehicleId", request.getVehicleId());
        snapshot.put("route", request.getRoute());
        snapshot.put("dispatchTime", request.getDispatchTime());
        snapshot.put("durationMinutes", request.getDurationMinutes());
        snapshot.put("notes", request.getNotes());
        return createLink(taskId, stepId, ResourceType.VEHICLE, request.getVehicleId(), null,
            JSON.toJSONString(snapshot), "busy");
    }

    private DispatchResourceUsageLink createLink(Long taskId, Long stepId, ResourceType type,
                                                 Long resourceId, Long usageRecordId,
                                                 String note, String status) {
        if (taskId == null) {
            throw new ServiceException("任务ID不能为空");
        }
        if (type == null) {
            throw new ServiceException("资源类型不能为空");
        }

        DispatchResourceUsageLink link = new DispatchResourceUsageLink();
        link.setTaskId(taskId);
        link.setStepId(stepId);
        link.setResourceType(type.getCode());
        link.setResourceId(resourceId);
        link.setUsageRecordId(usageRecordId);
        link.setStatus(status);
        link.setNote(note);
        link.setDelFlag("0");

        String operator = SecurityUtils.getUsername();
        Date now = new Date();
        link.setCreateBy(operator);
        link.setCreateTime(now);
        link.setUpdateBy(operator);
        link.setUpdateTime(now);

        this.save(link);
        return link;
    }

    private InventoryAction buildInventoryAction(Long taskId, MaterialAllocationRequest request) {
        if (request.getWarehouseId() == null) {
            throw new ServiceException("仓库ID不能为空");
        }
        if (request.getQuantity() == null || request.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("占用数量必须大于0");
        }
        InventoryAction action = new InventoryAction();
        action.setMaterialId(request.getMaterialId());
        action.setWarehouseId(request.getWarehouseId());
        action.setQty(request.getQuantity());
        action.setTaskId(taskId);
        action.setRemark(request.getUsageContext());
        action.setUsageMode(StringUtils.defaultIfBlank(request.getUsageMode(), "occupy"));
        return action;
    }

    private DispatchResourceUsageLink requireLinkBelongToTask(Long taskId, Long linkId) {
        if (taskId == null || linkId == null) {
            throw new ServiceException("任务ID和资源链接ID不能为空");
        }
        DispatchResourceUsageLink link = this.getById(linkId);
        if (link == null || !"0".equals(link.getDelFlag())) {
            throw new ServiceException("资源链接不存在");
        }
        if (!taskId.equals(link.getTaskId())) {
            throw new ServiceException("资源链接不属于该任务");
        }
        return link;
    }

    private DispatchResourceUsageLink requireMaterialLink(Long taskId, Long linkId) {
        DispatchResourceUsageLink link = requireLinkBelongToTask(taskId, linkId);
        if (!ResourceType.MATERIAL.getCode().equals(link.getResourceType())) {
            throw new ServiceException("仅支持物资资源操作");
        }
        return link;
    }

    private boolean releaseMaterialLinkInternal(DispatchResourceUsageLink link) {
        return performMaterialTransition(
            link,
            ResourceUsageStatus.LOCKED,
            ResourceUsageStatus.UNLOCKED,
            action -> materialInventoryService.unlockInventory(action),
            "释放物资失败，请检查库存"
        );
    }

    private boolean consumeMaterialLinkInternal(DispatchResourceUsageLink link) {
        return performMaterialTransition(
            link,
            ResourceUsageStatus.LOCKED,
            ResourceUsageStatus.CONSUMED,
            action -> materialInventoryService.consumeInventory(action),
            "消耗物资失败，请检查库存"
        );
    }

    private boolean returnMaterialLinkInternal(DispatchResourceUsageLink link) {
        return performMaterialTransition(
            link,
            ResourceUsageStatus.CONSUMED,
            ResourceUsageStatus.RETURNED,
            action -> materialInventoryService.returnInventory(action),
            "归还物资失败，请检查库存"
        );
    }

    private boolean performMaterialTransition(DispatchResourceUsageLink link,
                                              ResourceUsageStatus requiredStatus,
                                              ResourceUsageStatus targetStatus,
                                              Function<InventoryAction, Boolean> operator,
                                              String failureMessage) {
        ResourceUsageStatus currentStatus = ResourceUsageStatus.fromCode(link.getStatus());
        if (currentStatus != requiredStatus) {
            throw new ServiceException(String.format("资源当前状态为 %s，无法执行此操作",
                currentStatus != null ? currentStatus.getCode() : "unknown"));
        }
        InventoryAction action = extractInventoryAction(link);
        boolean result = operator.apply(action);
        if (!result) {
            throw new ServiceException(failureMessage);
        }
        updateLinkStatus(link, targetStatus);
        return true;
    }

    private InventoryAction extractInventoryAction(DispatchResourceUsageLink link) {
        if (StringUtils.isBlank(link.getNote())) {
            throw new ServiceException("缺少物资占用上下文，无法操作");
        }
        InventoryAction action = JSON.parseObject(link.getNote(), InventoryAction.class);
        if (action == null) {
            throw new ServiceException("解析物资占用信息失败");
        }
        action.setTaskId(link.getTaskId());
        return action;
    }

    private void updateLinkStatus(DispatchResourceUsageLink link, ResourceUsageStatus status) {
        link.setStatus(status.getCode());
        link.setUpdateBy(SecurityUtils.getUsername());
        link.setUpdateTime(new Date());
        boolean updated = this.updateById(link);
        if (!updated) {
            throw new ServiceException("更新资源状态失败，请稍后重试");
        }
    }

    private void ensureMaterialLinkIsReleasable(DispatchResourceUsageLink link) {
        ResourceUsageStatus current = ResourceUsageStatus.fromCode(link.getStatus());
        if (current == null || current == ResourceUsageStatus.LOCKED) {
            InventoryAction action = extractInventoryAction(link);
            if ("consume".equalsIgnoreCase(action.getUsageMode())) {
                consumeMaterialLinkInternal(link);
            } else {
                releaseMaterialLinkInternal(link);
            }
            current = ResourceUsageStatus.fromCode(link.getStatus());
        }
        if (current == ResourceUsageStatus.LOCKED) {
            throw new ServiceException("物资仍处于锁定状态，请先释放/消耗/归还");
        }
    }

    private void markVehicleStatus(Long vehicleId, String status) {
        Vehicle vehicle = vehicleService.getById(vehicleId);
        if (vehicle == null) {
            throw new ServiceException("车辆不存在");
        }
        vehicle.setStatus(status);
        vehicle.setUpdateBy(SecurityUtils.getUsername());
        vehicle.setUpdateTime(new Date());
        boolean updated = vehicleService.updateById(vehicle);
        if (!updated) {
            throw new ServiceException("更新车辆状态失败");
        }
    }
}