package com.multictrl.modules.business.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.exception.RenException;
import com.multictrl.common.page.PageData;
import com.multictrl.common.service.impl.CrudServiceImpl;
import com.multictrl.common.utils.CacheUtils;
import com.multictrl.modules.business.dao.NoflyZoneDao;
import com.multictrl.modules.business.dto.NoflyZoneDTO;
import com.multictrl.modules.business.entity.DockEntity;
import com.multictrl.modules.business.entity.NoflyZoneEntity;
import cn.hutool.core.util.StrUtil;
import com.multictrl.modules.business.service.DockService;
import com.multictrl.modules.business.service.NoflyZoneService;
import com.multictrl.modules.sys.entity.SysDeptEntity;
import com.multictrl.modules.sys.service.SysDeptService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 禁飞区
 *
 * @author Sdy
 * @since 1.0.0 2026-06-09
 */
@Service
@RequiredArgsConstructor
public class NoflyZoneServiceImpl extends CrudServiceImpl<NoflyZoneDao, NoflyZoneEntity, NoflyZoneDTO> implements NoflyZoneService {
    private final DockService dockService;
    private final SysDeptService deptService;

    @Override
    public QueryWrapper<NoflyZoneEntity> getWrapper(Map<String, Object> params) {
        String name = (String) params.get("f.name");

        QueryWrapper<NoflyZoneEntity> wrapper = new QueryWrapper<>();
        wrapper.like(StrUtil.isNotBlank(name), "f.name", name);
        wrapper.orderByDesc("f.create_date");

        return wrapper;
    }

    @Override
    public PageData<NoflyZoneDTO> pageList(Map<String, Object> params) {
        IPage<NoflyZoneDTO> page = baseDao.pageList(getPage(params, null, false), getWrapper(params));
        for (NoflyZoneDTO record : page.getRecords()) {
            String extentWkt = record.getExtentWkt();
            List<List<NoflyZoneDTO.point>> lists = parseMultipolygonZ(extentWkt);
            record.setExtent(lists);
        }
        return getPageData(page, currentDtoClass());
    }

    @Override
    public void saveNoFlying(NoflyZoneDTO noflyZone) {
        NoflyZoneEntity entity = new NoflyZoneEntity();
        entity.setName(noflyZone.getName());
        entity.setStatus(noflyZone.getStatus());
        StringBuilder extent = new StringBuilder("'MULTIPOLYGON Z (");
        List<List<NoflyZoneDTO.point>> lists = noflyZone.getExtent();
        for (List<NoflyZoneDTO.point> list : lists) {
            if (list.size() < 3) {
                throw new RenException("无效的面");
            }
            NoflyZoneDTO.point first = list.get(0);
            StringBuilder sb = new StringBuilder("((");
            for (NoflyZoneDTO.point point : list) {
                sb.append(point.getLng()).append(" ").append(point.getLat()).append(" ").append(point.getHeight()).append(",");
            }
            sb.append(first.getLng()).append(" ").append(first.getLat()).append(" ").append(first.getHeight()).append("))");
            extent.append(sb).append(",");
        }
        extent.deleteCharAt(extent.length() - 1);
        extent.append(")'");
        entity.setExtent(extent.toString());
        baseDao.saveNoFlyZone(entity);
    }

    @Override
    public void updateNoFlying(NoflyZoneDTO noflyZone) {
        update(null, new UpdateWrapper<NoflyZoneEntity>()
                .eq("id", noflyZone.getId())
                .set(StringUtils.isNotBlank(noflyZone.getStatus()), "status", noflyZone.getStatus())
                .set(StringUtils.isNotBlank(noflyZone.getName()), "name", noflyZone.getName()));
    }

    @Override
    public List<NoflyZoneDTO> getNoFlyZoneByDockSn(String dockSn) {
        DockEntity dockEntity = dockService.getDao().selectOne(new QueryWrapper<DockEntity>().eq("dock_sn", dockSn));
        if (dockEntity == null) {
            return List.of();
        }

        SysDeptEntity deptEntity = deptService.selectById(dockEntity.getDeptId());
        List<Long> deptIds = new ArrayList<>();
        deptIds.add(dockEntity.getDeptId());
        if (StringUtils.isNotBlank(deptEntity.getPids())) {
            Arrays.stream(deptEntity.getPids().split(","))
                    .filter(StringUtils::isNotBlank)
                    .map(Long::parseLong)
                    .forEach(deptIds::add);
        }

        List<NoflyZoneDTO> list = baseDao.getNoFlyZoneList(new QueryWrapper<NoflyZoneEntity>()
                .eq("f.status", "1").and(v ->
                        v.in("f.dept_id", deptIds).or().isNull("f.dept_id")));

        list.forEach(dto -> dto.setExtent(parseMultipolygonZ(dto.getExtentWkt())));

        CacheUtils.set(BusinessConstant.DOCK_NOFLY_ZONE + dockSn, list);
        return list;
    }

    @Override
    public Boolean isInNoFlyZone(List<List<NoflyZoneDTO.point>> noFlyZone, NoflyZoneDTO.point point) {
        if (noFlyZone.size() != 2 || noFlyZone.get(0).size() < 3 || noFlyZone.get(1).size() < 3) {
            throw new IllegalArgumentException("立方体必须由两个多边形面构成，每个面至少3个点");
        }

        List<NoflyZoneDTO.point> bottomFace = noFlyZone.get(0);
        List<NoflyZoneDTO.point> topFace = noFlyZone.get(1);

        // 检查高度是否在立方体范围内
        double minHeight = Math.min(bottomFace.get(0).getHeight(), topFace.get(0).getHeight());
        double maxHeight = Math.max(bottomFace.get(0).getHeight(), topFace.get(0).getHeight());

        if (point.getHeight() < minHeight || point.getHeight() > maxHeight) {
            return false;
        }

        // 检查点是否在底面多边形内
        boolean inBottomFace = isPointInPolygon(bottomFace, point);

        // 检查点是否在顶面多边形内
        boolean inTopFace = isPointInPolygon(topFace, point);

        // 如果点在底面或顶面多边形内，则它在立方体内
        return inBottomFace || inTopFace;
    }

    /**
     * 解析MULTIPOLYGON Z字符串
     *
     * @param multipolygonZ MULTIPOLYGON Z格式的字符串
     * @return 解析后的范围数据
     */
    private List<List<NoflyZoneDTO.point>> parseMultipolygonZ(String multipolygonZ) {
        List<List<NoflyZoneDTO.point>> result = new ArrayList<>();

        // 使用正则表达式匹配多边形数据
        Pattern pattern = Pattern.compile("\\(\\(([^)]+)\\)\\)");
        Matcher matcher = pattern.matcher(multipolygonZ);

        while (matcher.find()) {
            String polygonData = matcher.group(1);
            List<NoflyZoneDTO.point> polygon = parsePolygon(polygonData);
            result.add(polygon);
        }

        return result;
    }

    /**
     * 解析单个多边形数据
     *
     * @param polygonData 多边形数据字符串
     * @return 多边形点列表
     */
    private List<NoflyZoneDTO.point> parsePolygon(String polygonData) {
        List<NoflyZoneDTO.point> points = new ArrayList<>();
        String[] coordinates = polygonData.split(",");

        for (String coord : coordinates) {
            NoflyZoneDTO.point point = parseCoordinate(coord.trim());
            if (point != null) {
                points.add(point);
            }
        }
        // 去除最后一个点（如果它与第一个点相同，WKT格式多边形首尾闭合）
        if (points.size() > 1) {
            NoflyZoneDTO.point first = points.get(0);
            NoflyZoneDTO.point last = points.get(points.size() - 1);
            if (first.getLng().equals(last.getLng())
                    && first.getLat().equals(last.getLat())
                    && first.getHeight().equals(last.getHeight())) {
                points.remove(points.size() - 1);
            }
        }
        return points;
    }

    /**
     * 解析单个坐标点
     *
     * @param coordinate 坐标字符串
     * @return 点对象，解析失败返回null
     */
    private NoflyZoneDTO.point parseCoordinate(String coordinate) {
        // 使用正则表达式匹配三个浮点数
        Pattern pattern = Pattern.compile("([\\d.]+)\\s+([\\d.]+)\\s+([\\d.]+)");
        Matcher matcher = pattern.matcher(coordinate);
        if (matcher.find()) {
            return new NoflyZoneDTO.point(
                    Double.parseDouble(matcher.group(1)),
                    Double.parseDouble(matcher.group(2)),
                    Double.parseDouble(matcher.group(3))
            );
        }
        return null;
    }

    /**
     * 判断点是否在多边形内（射线法 Ray Casting）
     * <p>
     * 将3D多边形投影到2D平面（经纬度），忽略高度，
     * 使用射线法判断点是否落在多边形内部。
     * </p>
     *
     * @param polygon 多边形顶点列表
     * @param point   要检查的点
     * @return 如果点在多边形内返回true，否则返回false
     */
    private boolean isPointInPolygon(List<NoflyZoneDTO.point> polygon, NoflyZoneDTO.point point) {
        int n = polygon.size();
        if (n < 3) return false;

        double px = point.getLng();
        double py = point.getLat();
        boolean inside = false;

        for (int i = 0, j = n - 1; i < n; j = i++) {
            double xi = polygon.get(i).getLng(), yi = polygon.get(i).getLat();
            double xj = polygon.get(j).getLng(), yj = polygon.get(j).getLat();

            if (((yi > py) != (yj > py)) &&
                    (px < (xj - xi) * (py - yi) / (yj - yi) + xi)) {
                inside = !inside;
            }
        }
        return inside;
    }
}