package org.dromara.system.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.domain.TblDzbcDriverTrip;
import org.dromara.system.domain.TblDzbcPathScheduling;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.domain.bo.TblDzbcDriverTripBo;
import org.dromara.system.domain.vo.TblDzbcCargoOrderVo;
import org.dromara.system.domain.vo.TblDzbcDriverTripVo;
import org.dromara.system.domain.vo.TblDzbcPathSiteVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.mapper.TblDzbcDriverTripMapper;
import org.dromara.system.mapper.TblDzbcPathSchedulingMapper;
import org.dromara.system.mapper.TblDzbcPathSiteMapper;
import org.dromara.system.service.ITblDzbcCargoOrderService;
import org.dromara.system.service.ITblDzbcDriverTripService;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;

@RequiredArgsConstructor
@Service
public class TblDzbcDriverTripServiceImpl implements ITblDzbcDriverTripService {

    private final TblDzbcDriverTripMapper baseMapper;
    private final TblDzbcPathSchedulingMapper pathSchedulingMapper;
    private final SysUserMapper userMapper;
    private final TblDzbcPathSiteMapper pathSiteMapper;
    private final ITblDzbcCargoOrderService cargoOrderService;

    @Override
    public TblDzbcDriverTripVo queryById(Long id) {
        TblDzbcDriverTripVo vo = baseMapper.selectVoById(id);
        if (vo != null) {
            fillDetails(vo);
        }
        return vo;
    }

    @Override
    public TblDzbcDriverTripVo queryByTripNo(String tripNo) {
        TblDzbcDriverTripVo vo = baseMapper.selectByTripNo(tripNo);
        if (vo != null) {
            fillDetails(vo);
        }
        return vo;
    }

    @Override
    public TableDataInfo<TblDzbcDriverTripVo> queryPageList(TblDzbcDriverTripBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TblDzbcDriverTrip> lqw = buildQueryWrapper(bo);
        Page<TblDzbcDriverTripVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    @Override
    public List<TblDzbcDriverTripVo> queryList(TblDzbcDriverTripBo bo) {
        LambdaQueryWrapper<TblDzbcDriverTrip> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    @Override
    public TblDzbcDriverTripVo createTrip(TblDzbcDriverTripBo bo) {
        SysUserVo driver = userMapper.selectUserById(bo.getDriverId());
        if (driver == null) {
            throw new RuntimeException("司机不存在");
        }

        TblDzbcDriverTrip trip = new TblDzbcDriverTrip();
        trip.setTripNo(generateTripNo());
        trip.setPathId(bo.getPathId());
        trip.setDriverId(bo.getDriverId());
        trip.setDriverName(driver.getNickName());
        trip.setDriverPhone(driver.getPhonenumber());
        trip.setCarId(bo.getCarId());
        trip.setStartTime(bo.getStartTime());
        trip.setExt1(bo.getExt1());
        trip.setExt2(bo.getExt2());
        trip.setStatus(0);

        validEntityBeforeSave(trip);
        baseMapper.insert(trip);

        TblDzbcDriverTripVo vo = queryById(trip.getId());
        return vo;
    }

    @Override
    public Boolean startTrip(Long id) {
        return generateNewTripNo(id) != null;
    }

    @Override
    public Boolean startTripBySchedulingId(Long schedulingId) {
        // 根据 schedulingId 查找对应的 driverTrip
        TblDzbcDriverTrip trip = baseMapper.selectOne(
            new LambdaQueryWrapper<TblDzbcDriverTrip>()
                .apply("ext2->'$.schedulingId' = {0}", schedulingId)
                .last("LIMIT 1")
        );

        if (trip != null) {
            // 已存在 driverTrip，直接更新状态为发车
            if (trip.getStatus() != null && trip.getStatus() >= 1) {
                throw new ServiceException("该排班已发车");
            }
            TblDzbcDriverTrip update = new TblDzbcDriverTrip();
            update.setId(trip.getId());
            update.setStatus(1); // 状态设为1（发车中）
            return baseMapper.updateById(update) > 0;
        }

        // 不存在 driverTrip，需要根据排班信息创建并直接发车
        TblDzbcPathScheduling scheduling = pathSchedulingMapper.selectById(schedulingId);
        if (scheduling == null) {
            throw new ServiceException("排班信息不存在");
        }

        // 解析 ext1 获取司机和车辆信息
        if (scheduling.getExt1() == null) {
            throw new ServiceException("排班扩展信息不存在");
        }

        JSONObject ext1 = JSONUtil.parseObj(scheduling.getExt1());
        JSONObject driverInfo = ext1.getJSONObject("driverInfo");
        JSONObject carInfo = ext1.getJSONObject("carInfo");

        if (driverInfo == null || carInfo == null) {
            throw new ServiceException("司机或车辆信息不存在");
        }

        // 创建 driverTrip
        TblDzbcDriverTrip newTrip = new TblDzbcDriverTrip();
        newTrip.setTripNo(generateTripNo());
        newTrip.setPathId(scheduling.getPathId());
        newTrip.setDriverId(driverInfo.getLong("userId"));
        newTrip.setDriverName(driverInfo.getStr("nickName"));
        newTrip.setDriverPhone(driverInfo.getStr("phonenumber"));
        newTrip.setCarId(carInfo.getLong("id"));
        newTrip.setStatus(1); // 直接设为发车状态

        // 设置开始时间
        if (scheduling.getMdate() != null && scheduling.getMtime() != null) {
            String timeStr = scheduling.getMtime();
            if (org.dromara.common.core.utils.StringUtils.countMatches(timeStr, ":") == 1) {
                timeStr += ":00";
            }
            String dateTimeStr = DateUtil.formatDate(scheduling.getMdate()) + " " + timeStr;
            newTrip.setStartTime(LocalDateTime.parse(dateTimeStr, java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }

        // 设置 ext1 存储排班快照
        newTrip.setExt1(JSONUtil.toJsonStr(scheduling));

        // 设置 ext2 关联 schedulingId
        JSONObject ext2 = JSONUtil.createObj();
        ext2.set("schedulingId", schedulingId);
        newTrip.setExt2(ext2.toString());

        validEntityBeforeSave(newTrip);
        return baseMapper.insert(newTrip) > 0;
    }

    @Override
    public TblDzbcDriverTripVo generateNewTripNo(Long id) {
        // 查询原车次信息
        TblDzbcDriverTrip originalTrip = baseMapper.selectById(id);
        if (originalTrip == null) {
            throw new RuntimeException("原车次不存在");
        }

        // 创建新的车次记录，复制原车次的司机和班次信息
        TblDzbcDriverTrip newTrip = new TblDzbcDriverTrip();
        newTrip.setTripNo(generateTripNo());
        newTrip.setPathId(originalTrip.getPathId());
        newTrip.setDriverId(originalTrip.getDriverId());
        newTrip.setDriverName(originalTrip.getDriverName());
        newTrip.setDriverPhone(originalTrip.getDriverPhone());
        newTrip.setCarId(originalTrip.getCarId());
        newTrip.setStartTime(originalTrip.getStartTime());
        newTrip.setExt1(originalTrip.getExt1());
        newTrip.setExt2(originalTrip.getExt2());
        newTrip.setStatus(1); // 新车次状态设为1（发车中）
        newTrip.setCurrentSiteId(originalTrip.getCurrentSiteId());
        newTrip.setCurrentSiteName(originalTrip.getCurrentSiteName());

        // 保存新车次
        validEntityBeforeSave(newTrip);
        baseMapper.insert(newTrip);

        // 更新原车次状态为已生成新车次
        TblDzbcDriverTrip updateOriginal = new TblDzbcDriverTrip();
        updateOriginal.setId(id);
        updateOriginal.setStatus(1); // 原车次也设为1，表示已发车
        baseMapper.updateById(updateOriginal);

        return queryById(newTrip.getId());
    }

    @Override
    public Boolean arriveSite(Long id, Long siteId) {
        TblDzbcDriverTripVo trip = queryById(id);
        if (trip == null) {
            throw new RuntimeException("任务不存在");
        }

        TblDzbcPathSiteVo site = pathSiteMapper.selectVoById(siteId);
        if (site == null) {
            throw new RuntimeException("站点不存在");
        }

        TblDzbcDriverTrip update = new TblDzbcDriverTrip();
        update.setId(id);
        update.setCurrentSiteId(siteId);
        update.setCurrentSiteName(site.getSiteName());

        return baseMapper.updateById(update) > 0;
    }

    @Override
    public Boolean completeTrip(Long id) {
        TblDzbcDriverTrip trip = new TblDzbcDriverTrip();
        trip.setId(id);
        trip.setStatus(2);
        trip.setEndTime(LocalDateTime.now());
        return baseMapper.updateById(trip) > 0;
    }

    @Override
    public Boolean cancelTrip(Long id) {
        TblDzbcDriverTrip trip = new TblDzbcDriverTrip();
        trip.setId(id);
        trip.setStatus(3);
        return baseMapper.updateById(trip) > 0;
    }

    @Override
    public Boolean updateByBo(TblDzbcDriverTripBo bo) {
        TblDzbcDriverTrip update = MapstructUtils.convert(bo, TblDzbcDriverTrip.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    private LambdaQueryWrapper<TblDzbcDriverTrip> buildQueryWrapper(TblDzbcDriverTripBo bo) {
        LambdaQueryWrapper<TblDzbcDriverTrip> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getId() != null, TblDzbcDriverTrip::getId, bo.getId());
        lqw.eq(bo.getPathId() != null, TblDzbcDriverTrip::getPathId, bo.getPathId());
        lqw.eq(bo.getDriverId() != null, TblDzbcDriverTrip::getDriverId, bo.getDriverId());
        lqw.like(StrUtil.isNotBlank(bo.getTripNo()), TblDzbcDriverTrip::getTripNo, bo.getTripNo());
        lqw.eq(bo.getStatus() != null, TblDzbcDriverTrip::getStatus, bo.getStatus());
        // startTime 时间范围筛选
        if (bo.getParams() != null) {
            if (bo.getParams().containsKey("beginStartTime")) {
                lqw.ge(TblDzbcDriverTrip::getStartTime, bo.getParams().get("beginStartTime"));
            }
            if (bo.getParams().containsKey("endStartTime")) {
                lqw.le(TblDzbcDriverTrip::getStartTime, bo.getParams().get("endStartTime"));
            }
        }
        lqw.orderByDesc(TblDzbcDriverTrip::getCreateTime);
        return lqw;
    }

    private void validEntityBeforeSave(TblDzbcDriverTrip entity) {
        // 校验同一时间的同一辆车只能发车一次
        Long carId = entity.getCarId();
        LocalDateTime startTime = entity.getStartTime();

        // 如果是更新操作，且字段缺失，需要查询数据库补充
        if (entity.getId() != null) {
            TblDzbcDriverTrip old = baseMapper.selectById(entity.getId());
            if (old != null) {
                if (carId == null) carId = old.getCarId();
                if (startTime == null) startTime = old.getStartTime();
            }
        }

        // 只有当 carId 和 startTime 都存在时才进行校验
        if (carId != null && startTime != null) {
            LambdaQueryWrapper<TblDzbcDriverTrip> lqw = Wrappers.lambdaQuery();
            lqw.eq(TblDzbcDriverTrip::getCarId, carId);
            lqw.eq(TblDzbcDriverTrip::getStartTime, startTime);
            lqw.ne(TblDzbcDriverTrip::getStatus, 3); // 排除已取消
            if (entity.getId() != null) {
                lqw.ne(TblDzbcDriverTrip::getId, entity.getId());
            }
            if (baseMapper.selectCount(lqw) > 0) {
                throw new ServiceException("该车辆在该时间段已有任务");
            }
        }
    }

    private void fillDetails(TblDzbcDriverTripVo vo) {
        List<TblDzbcPathSiteVo> sites = pathSiteMapper.selectByPathId(vo.getPathId());
        vo.setSites(sites);

        List<TblDzbcCargoOrderVo> cargoOrders = cargoOrderService.queryByTripId(vo.getId());
        vo.setCargoOrders(cargoOrders);

        if (cargoOrders != null) {
            vo.setCargoCount(cargoOrders.size());
            int totalPrice = cargoOrders.stream()
                .mapToInt(order -> order.getPrice() != null ? order.getPrice() : 0)
                .sum();
            vo.setCargoTotalPrice(totalPrice);
        }
    }

    private String generateTripNo() {
        return "TRIP" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") + IdUtil.getSnowflakeNextIdStr().substring(8);
    }
}
