package com.sikey.wa04.business.infrastructure.adapter.dao;

import com.sikey.wa04.business.domain.adapter.LocationAdapter;
import com.sikey.wa04.business.domain.entity.Address;
import com.sikey.wa04.business.domain.entity.ChildId;
import com.sikey.wa04.business.domain.entity.DeviceId;
import com.sikey.wa04.business.domain.entity.Location;
import com.sikey.wa04.business.infrastructure.dto.model.MapTrajectoryModel;
import com.sikey.wa04.business.infrastructure.dto.model.MapTrajectoryPoiModel;
import com.sikey.wa04.business.infrastructure.repository.MapTrajectoryLogRepository;
import com.sikey.wa04.business.infrastructure.repository.MapTrajectoryPoiRepository;
import com.sikey.wa04.business.infrastructure.repository.MapTrajectoryRepository;
import com.sikey.wa04.common.enums.LocationType;
import com.sikey.wa04.common.enums.TrajectoryChannel;
import jakarta.annotation.Resource;
import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Limit;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 定位位置适配器实现类
 *
 * @author luoyangwei
 * @see LocationAdapter
 */
@Slf4j
@Component
public class LocationAdapterImpl implements LocationAdapter {

    @Resource
    private MapTrajectoryRepository mapTrajectoryRepository;

    @Resource
    private MapTrajectoryPoiRepository mapTrajectoryPoiRepository;

    @Resource
    private MapTrajectoryLogRepository mapTrajectoryLogRepository;

    @Override
    public Location findLastLocation(ChildId cid) {
        Sort sort = Sort.by(Sort.Direction.DESC, "positionTime");
        Limit limit = Limit.of(1);
        List<MapTrajectoryModel> mapTrajectoryModels = mapTrajectoryRepository.findByChildIdOrderByPositionTimeDesc(cid.getId(), sort, limit);
        if (mapTrajectoryModels.isEmpty()) {
            return null;
        }
        return convertMapTrajectoryModelToLocation(mapTrajectoryModels.getFirst()).setChildId(cid);
    }

    /**
     * 根据child_id + 时间范围查询历史轨迹
     * @param cid   小孩ID
     * @param start 开始时间
     * @param end   结束时间
     * @return      历史轨迹列表
     */
    @Override
    public List<Location> findByChildIdAndCreatedAtBetween(ChildId cid, Instant start, Instant end) {
        List<Location> locations = new ArrayList<>();
        List<MapTrajectoryModel> trajectoryModels = mapTrajectoryRepository.findByChildIdAndCreatedAtBetween(cid.getId(), start, end);
       // log.info("[历史轨迹]查询结果：{}", JSONUtil.toJsonStr(trajectoryModels));
        for (MapTrajectoryModel trajectoryModel : trajectoryModels) {
            Location location = convertMapTrajectoryModelToLocation(trajectoryModel);
            locations.add(location.setChildId(cid));
        }
        return locations;
    }

    /**
     * To Location entity converter
     *
     * @param trajectoryModel model
     * @return entity
     */
    private Location convertMapTrajectoryModelToLocation(MapTrajectoryModel trajectoryModel) {
        Location location = new Location();
        location.setId(trajectoryModel.getId()).setDeviceId(new DeviceId(trajectoryModel.getDeviceId())).setType(LocationType.targetValueOf(trajectoryModel.getType())).setChannel(TrajectoryChannel.valueOf(trajectoryModel.getChannel())).setPositionTime(trajectoryModel.getPositionTime());

        Optional<MapTrajectoryPoiModel> optionalTrajectoryPoiModel = mapTrajectoryPoiRepository.findByTrajectoryId(trajectoryModel.getId());
        MapTrajectoryPoiModel trajectoryPoiModel = optionalTrajectoryPoiModel.orElse(new MapTrajectoryPoiModel());

        Address address = new Address();
        address.setLongitude(trajectoryModel.getLongitude()).setLatitude(trajectoryModel.getLatitude()).setRadius(trajectoryModel.getRadius());

        address.setDesc(trajectoryPoiModel.getDesc()).setCountry(trajectoryPoiModel.getCountry()).setProvince(trajectoryPoiModel.getProvince()).setCity(trajectoryPoiModel.getCity()).setCityCode(trajectoryPoiModel.getCatyCode()).setAdCode(trajectoryPoiModel.getAdCode()).setRoad(trajectoryPoiModel.getRoad()).setPoi(trajectoryPoiModel.getPoi());

        location.setAddress(address);
        return location;
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    public Long save(Location location, Boolean isInvalid) {
        MapTrajectoryModel mapTrajectoryModel = new MapTrajectoryModel();
        mapTrajectoryModel.setChildId(location.getChildId().getId()).setDeviceId(location.getDeviceId().getId()).setPositionTime(location.getPositionTime()).setType(Integer.parseInt(location.getType().getTargetValue())).setChannel(location.getChannel().getValue()).setLongitude(location.getAddress().getLongitude()).setLatitude(location.getAddress().getLatitude()).setRadius(location.getAddress().getRadius()).setIsInvalid(isInvalid);
        mapTrajectoryRepository.save(mapTrajectoryModel);
        MapTrajectoryPoiModel mapTrajectoryPoiModel = new MapTrajectoryPoiModel();
        mapTrajectoryPoiModel.setTrajectoryId(mapTrajectoryModel.getId()).setDesc(location.getAddress().getDesc()).setPoi(location.getAddress().getPoi()).setCountry(location.getAddress().getCountry()).setProvince(location.getAddress().getProvince()).setCity(location.getAddress().getCity()).setCatyCode(location.getAddress().getCityCode()).setAdCode(location.getAddress().getAdCode()).setRoad(location.getAddress().getRoad());
        mapTrajectoryPoiRepository.save(mapTrajectoryPoiModel);
        return mapTrajectoryModel.getId();
    }

    /**
     * 查找上一个位置
     *
     * @param deviceId 设备id
     * @return
     */
    @Override
    public Location findPreviousPosition(Long deviceId) {
        Sort orders = Sort.by(Sort.Direction.DESC, "createdAt");
        Limit limit1 = Limit.of(1);
        MapTrajectoryModel mapTrajectoryModels = mapTrajectoryRepository.findByDeviceIdOrderByCreatedAtDesc(deviceId, orders, limit1);
        if (Objects.nonNull(mapTrajectoryModels)) {
            return convertMapTrajectoryModelToLocation(mapTrajectoryModels);
        }
        return null;
    }
}
