package com.zcloud.personnel.positioning.command;

import com.alibaba.cola.exception.BizException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zcloud.personnel.positioning.dto.FenceCompanyAreaSaveCmd;
import com.zcloud.personnel.positioning.dto.clientobject.FenceCompanyAreaCO;
import com.zcloud.personnel.positioning.persistence.dataobject.AreaDO;
import com.zcloud.personnel.positioning.persistence.dataobject.FenceDO;
import com.zcloud.personnel.positioning.persistence.repository.AreaRepository;
import com.zcloud.personnel.positioning.persistence.repository.FenceRepository;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

@Component
@AllArgsConstructor
public class FenceCompanyAreaSaveExe {
    private static final String AREA_TYPE_COMPANY_AREA = "COMPANY_AREA";

    private final AreaRepository areaRepository;
    private final FenceRepository fenceRepository;
    private final FencePersistenceSupport fencePersistenceSupport;
    private final FindsPendingSyncService findsPendingSyncService;
    private final FencePointPersistenceService fencePointPersistenceService;

    @Transactional(rollbackFor = Exception.class)
    public FenceCompanyAreaCO save(FenceCompanyAreaSaveCmd cmd) {
        validate(cmd);
        AreaDO area = saveArea(cmd);
        FenceDO fence = saveFence(cmd, area);
        area.setBindFenceId(fence.getId());
        area.setFindsFenceId(fence.getFindsFenceId());
        areaRepository.updateById(area);
        return map(area, fence);
    }

    public FenceCompanyAreaCO query(String corpinfoId, String corpinfoName) {
        AreaDO area = queryArea(null, corpinfoId, corpinfoName);
        FenceDO fence = null;
        if (area != null && area.getBindFenceId() != null) {
            fence = fenceRepository.getById(area.getBindFenceId());
        }
        if (fence == null) {
            fence = queryFence(null, corpinfoId, corpinfoName);
        }
        if (area == null && fence == null) {
            return null;
        }
        return map(area, fence);
    }

    private AreaDO saveArea(FenceCompanyAreaSaveCmd cmd) {
        AreaDO area = queryArea(cmd.getAreaId(), cmd.getCorpinfoId(), cmd.getCorpinfoName());
        boolean create = area == null;
        if (create) {
            area = new AreaDO();
            area.setAreaCode("CA" + System.currentTimeMillis());
            area.setAreaType(AREA_TYPE_COMPANY_AREA);
        }
        AreaDO portArea = queryPortArea(cmd.getParentId(), cmd.getParentCode());
        if (portArea != null) {
            area.setParentId(portArea.getId());
            area.setParentCode(portArea.getAreaCode());
        }
        area.setAreaLevel(area.getParentId() == null ? 1 : 2);
        area.setAreaName(cmd.getAreaName());
        area.setCorpinfoId(cmd.getCorpinfoId());
        area.setCorpinfoName(cmd.getCorpinfoName());
        area.setPolygonJson(cmd.getPolygonJson());
        area.setCenterLon(cmd.getCenterLon());
        area.setCenterLat(cmd.getCenterLat());
        area.setStatus(fencePersistenceSupport.normalizeStatus(cmd.getStatus()));
        if (create) {
            boolean res = areaRepository.save(area);
            if (!res) {
                throw new BizException("Company area save failed");
            }
        } else if (!areaRepository.updateById(area)) {
            throw new BizException("Company area update failed");
        }
        return area;
    }

    private AreaDO queryPortArea(Long parentId, String parentCode) {
        if (parentId == null && !StringUtils.hasText(parentCode)) {
            return null;
        }
        QueryWrapper<AreaDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("area_type", "PORT");
        queryWrapper.and(wrapper -> wrapper.isNull("delete_enum").or().eq("delete_enum", "FALSE"));
        if (parentId != null) {
            queryWrapper.eq("id", parentId);
        } else {
            queryWrapper.eq("area_code", parentCode.trim());
        }
        queryWrapper.last("LIMIT 1");
        AreaDO portArea = areaRepository.getOne(queryWrapper, false);
        if (portArea == null) {
            throw new BizException("所属港区不存在或已删除");
        }
        return portArea;
    }

    private FenceDO saveFence(FenceCompanyAreaSaveCmd cmd, AreaDO area) {
        FenceDO fence = queryFence(cmd.getFenceId(), cmd.getCorpinfoId(), cmd.getCorpinfoName());
        boolean create = fence == null;
        if (create) {
            fence = new FenceDO();
            fence.setFenceCode("CF" + System.currentTimeMillis());
        }
        fence.setFenceName(cmd.getAreaName());
        fence.setFenceScope(FencePersistenceSupport.SCOPE_COMPANY_AREA);
        fence.setFenceBizName(FencePersistenceSupport.DEFAULT_BIZ_NAME);
        fence.setCorpinfoId(cmd.getCorpinfoId());
        fence.setCorpinfoName(cmd.getCorpinfoName());
        fence.setAreaId(area.getId());
        fence.setAreaName(area.getAreaName());
        fence.setCoor(cmd.getCoor());
        fence.setSpaceType(cmd.getSpaceType());
        fence.setBuildingId(cmd.getBuildingId());
        fence.setFloor(cmd.getFloor());
        fence.setPolygonJson(cmd.getPolygonJson());
        fence.setCenterLon(cmd.getCenterLon());
        fence.setCenterLat(cmd.getCenterLat());
        fence.setSilence(0);
        fence.setStatus(cmd.getStatus());
        fencePersistenceSupport.normalize(fence, create);
        fence.setSyncStatus(FindsPendingSyncService.STATUS_WAIT);
        fence.setSyncToken(FindsSyncToken.next());
        if (create) {
            boolean res = fenceRepository.save(fence);
            if (!res) {
                throw new BizException("Company area fence save failed");
            }
        } else if (!fenceRepository.updateById(fence)) {
            throw new BizException("Company area fence update failed");
        }
        fencePointPersistenceService.rebuild(fence);
        findsPendingSyncService.scheduleFence(fence.getId());
        return fence;
    }

    private AreaDO queryArea(Long areaId, String corpinfoId, String corpinfoName) {
        if (areaId != null) {
            return areaRepository.getById(areaId);
        }
        QueryWrapper<AreaDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("area_type", AREA_TYPE_COMPANY_AREA);
        queryWrapper.and(wrapper -> wrapper.isNull("delete_enum").or().eq("delete_enum", "FALSE"));
        applyCompany(queryWrapper, corpinfoId, corpinfoName);
        queryWrapper.orderByDesc("update_time");
        queryWrapper.last("LIMIT 1");
        return areaRepository.getOne(queryWrapper, false);
    }

    private FenceDO queryFence(Long fenceId, String corpinfoId, String corpinfoName) {
        if (fenceId != null) {
            return fenceRepository.getById(fenceId);
        }
        QueryWrapper<FenceDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("fence_scope", FencePersistenceSupport.SCOPE_COMPANY_AREA);
        queryWrapper.and(wrapper -> wrapper.isNull("delete_enum").or().eq("delete_enum", "FALSE"));
        applyCompany(queryWrapper, corpinfoId, corpinfoName);
        queryWrapper.orderByDesc("update_time");
        queryWrapper.last("LIMIT 1");
        return fenceRepository.getOne(queryWrapper, false);
    }

    private <T> void applyCompany(QueryWrapper<T> queryWrapper, String corpinfoId, String corpinfoName) {
        if (corpinfoId != null) {
            queryWrapper.eq("corpinfo_id", corpinfoId);
            return;
        }
        if (StringUtils.hasText(corpinfoName)) {
            queryWrapper.eq("corpinfo_name", corpinfoName);
        }
    }

    private void validate(FenceCompanyAreaSaveCmd cmd) {
        if (cmd == null) {
            throw new BizException("Company area payload is required");
        }
        if (cmd.getCorpinfoId() == null && !StringUtils.hasText(cmd.getCorpinfoName())) {
            throw new BizException("Company is required");
        }
        if (!StringUtils.hasText(cmd.getAreaName())) {
            throw new BizException("Area name is required");
        }
        if (!StringUtils.hasText(cmd.getPolygonJson())) {
            throw new BizException("Polygon json is required");
        }
    }

    private FenceCompanyAreaCO map(AreaDO area, FenceDO fence) {
        FenceCompanyAreaCO co = new FenceCompanyAreaCO();
        if (area != null) {
            co.setAreaId(area.getId());
            co.setAreaName(area.getAreaName());
            co.setParentId(area.getParentId());
            co.setParentCode(area.getParentCode());
            co.setCorpinfoId(area.getCorpinfoId());
            co.setCorpinfoName(area.getCorpinfoName());
            co.setPolygonJson(area.getPolygonJson());
            co.setCenterLon(area.getCenterLon());
            co.setCenterLat(area.getCenterLat());
            co.setStatus(area.getStatus());
            co.setFindsFenceId(area.getFindsFenceId());
        }
        if (fence != null) {
            BeanUtils.copyProperties(fence, co);
            co.setFenceId(fence.getId());
            co.setAreaId(fence.getAreaId() == null ? co.getAreaId() : fence.getAreaId());
            co.setAreaName(StringUtils.hasText(fence.getAreaName()) ? fence.getAreaName() : co.getAreaName());
            co.setFindsFenceId(fence.getFindsFenceId());
            co.setFindsFenceCode(fence.getFindsFenceCode());
            co.setSyncStatus(fence.getSyncStatus());
            co.setLastSyncTime(fence.getLastSyncTime());
        }
        return co;
    }
}
