package com.usky.ems.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.security.utils.SecurityUtils;
import com.usky.ems.domain.BaseSpace;
import com.usky.ems.domain.BaseSpaceGateway;
import com.usky.ems.domain.DmpDevice;
import com.usky.ems.domain.EmsDeviceItemCode;
import com.usky.ems.domain.EmsEnergyItemCode;
import com.usky.ems.domain.EmsProductEnergyType;
import com.usky.ems.domain.EmsProject;
import com.usky.ems.domain.EmsProjectDeviceSystem;
import com.usky.ems.mapper.BaseSpaceGatewayMapper;
import com.usky.ems.mapper.DmpDeviceMapper;
import com.usky.ems.mapper.EmsDeviceItemCodeMapper;
import com.usky.ems.mapper.EmsEnergyItemCodeMapper;
import com.usky.ems.mapper.EmsProductEnergyTypeMapper;
import com.usky.ems.mapper.EmsProjectDeviceSystemMapper;
import com.usky.ems.mapper.EmsProjectMapper;
import com.usky.ems.service.BaseSpaceService;
import com.usky.ems.service.EmsProjectService;
import com.usky.ems.service.EmsSystemDictRegionService;
import com.usky.ems.service.vo.EmsDeviceItemCodeSaveRequest;
import com.usky.ems.service.vo.EmsProductEnergyTypeSaveRequest;
import com.usky.ems.service.vo.EmsProjectResponse;
import com.usky.ems.service.vo.EmsProjectSaveRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 项目维护实现（逻辑参考原 ProjectServiceImpl：省市区校验、创建项目空间、设备系统关联、删除前校验叶子节点等）
 */
@Service
public class EmsProjectServiceImpl implements EmsProjectService {

    private static final int SPACE_TYPE_PROJECT = 1;

    @Autowired
    private EmsProjectMapper emsProjectMapper;
    @Autowired
    private EmsProjectDeviceSystemMapper emsProjectDeviceSystemMapper;
    @Autowired
    private BaseSpaceService baseSpaceService;
    @Autowired
    private EmsSystemDictRegionService emsSystemDictRegionService;
    @Autowired
    private EmsProductEnergyTypeMapper emsProductEnergyTypeMapper;
    @Autowired
    private EmsDeviceItemCodeMapper emsDeviceItemCodeMapper;
    @Autowired
    private BaseSpaceGatewayMapper baseSpaceGatewayMapper;
    @Autowired
    private DmpDeviceMapper dmpDeviceMapper;
    @Autowired
    private EmsEnergyItemCodeMapper emsEnergyItemCodeMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public EmsProjectResponse save(EmsProjectSaveRequest request) {
        validateSaveRequest(request, false);
        Integer currentTenantId = SecurityUtils.getTenantId();
        Map<String, String> regionMap = emsSystemDictRegionService.checkAndBuildMap(
                request.getProvinceCode(), request.getCityCode(), request.getDistrictCode());
        Long spaceId = createProjectSpace(request.getName());
        EmsProject po = new EmsProject();
        copyRequestToEntity(request, po, regionMap);
        po.setSpaceId(spaceId);
        po.setTenantId(currentTenantId);
        fillAuditOnCreate(po);
        emsProjectMapper.insert(po);
        replaceDeviceSystems(po.getId(), request.getDeviceSystemList());
        return toResponse(po, request.getDeviceSystemList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public EmsProjectResponse update(EmsProjectSaveRequest request) {
        if (request.getSpaceId() == null) {
            throw new BusinessException("修改项目时 空间id 不能为空");
        }
        EmsProject old = emsProjectMapper.selectOne(
                new LambdaQueryWrapper<EmsProject>().eq(EmsProject::getSpaceId, request.getSpaceId()));
        if (old == null) {
            throw new BusinessException("错误的空间id值");
        }
        Map<String, String> regionMap = emsSystemDictRegionService.checkAndBuildMap(
                request.getProvinceCode(), request.getCityCode(), request.getDistrictCode());
        EmsProject po = new EmsProject();
        copyRequestToEntity(request, po, regionMap);
        po.setSpaceId(old.getSpaceId());
        po.setTenantId(old.getTenantId());
        fillAuditOnUpdate(po);
        emsProjectMapper.updateById(po);
        baseSpaceService.updateProjectSpaceName(old.getSpaceId(), po.getName());
        replaceDeviceSystems(po.getId(), request.getDeviceSystemList());
        return toResponse(po, request.getDeviceSystemList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(Long spaceId) {
        EmsProject project = emsProjectMapper.selectOne(
                new LambdaQueryWrapper<EmsProject>().eq(EmsProject::getSpaceId, spaceId));
        if (project == null) {
            throw new BusinessException("错误的空间id值");
        }
        if (!baseSpaceService.isLeafSpace(project.getSpaceId())) {
            throw new BusinessException("当前项目下有空间节点不允许删除");
        }
        if (hasLoggedUserForProject(project.getId())) {
            throw new BusinessException("当前项目有关联的用户使用;不允许删除");
        }
        emsProjectDeviceSystemMapper.delete(
                new LambdaQueryWrapper<EmsProjectDeviceSystem>().eq(EmsProjectDeviceSystem::getProjectId, project.getId()));
        emsProjectMapper.deleteById(project.getId());
        baseSpaceService.removeById(spaceId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveProductEnergyTypes(EmsProductEnergyTypeSaveRequest request) {
        if (request == null || request.getEnergyType() == null) {
            throw new BusinessException("能耗类型不能为空");
        }
        Integer tenantId = SecurityUtils.getTenantId();
        emsProductEnergyTypeMapper.delete(
                new LambdaQueryWrapper<EmsProductEnergyType>()
                        .eq(EmsProductEnergyType::getTenantId, tenantId)
                        .eq(EmsProductEnergyType::getEnergyType, request.getEnergyType()));
        if (CollectionUtils.isEmpty(request.getProductIdList())) {
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        String auditUser = SecurityUtils.getUsername();
        for (Long productId : request.getProductIdList()) {
            if (productId == null) {
                continue;
            }
            EmsProductEnergyType row = new EmsProductEnergyType();
            row.setProductId(productId);
            row.setEnergyType(request.getEnergyType());
            row.setTenantId(tenantId);
            row.setCreatedBy(auditUser);
            row.setUpdatedBy(auditUser);
            row.setCreatedTime(now);
            row.setUpdatedTime(now);
            emsProductEnergyTypeMapper.insert(row);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveDeviceItemCodes(EmsDeviceItemCodeSaveRequest request) {
        if (request == null || request.getEnergyType() == null) {
            throw new BusinessException("能耗类型不能为空");
        }
        if (!StringUtils.hasText(request.getItemCode())) {
            throw new BusinessException("能耗分项编码不能为空");
        }
        Integer tenantId = SecurityUtils.getTenantId();
        String itemCode = request.getItemCode().trim();
        emsDeviceItemCodeMapper.delete(
                new LambdaQueryWrapper<EmsDeviceItemCode>()
                        .eq(EmsDeviceItemCode::getTenantId, tenantId)
                        .eq(EmsDeviceItemCode::getItemCode, itemCode));
        if (CollectionUtils.isEmpty(request.getDeviceUuidList())) {
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        String auditUser = SecurityUtils.getUsername();
        for (String deviceUuid : request.getDeviceUuidList()) {
            if (!StringUtils.hasText(deviceUuid)) {
                continue;
            }
            EmsDeviceItemCode row = new EmsDeviceItemCode();
            row.setDeviceUuid(deviceUuid.trim());
            row.setItemCode(itemCode);
            row.setEnergyType(request.getEnergyType());
            row.setTenantId(tenantId);
            row.setCreatedBy(auditUser);
            row.setUpdatedBy(auditUser);
            row.setCreatedTime(now);
            row.setUpdatedTime(now);
            emsDeviceItemCodeMapper.insert(row);
        }
    }

    @Override
    public List<DmpDevice> listAreaDevices(Integer spaceId) {
        List<String> gatewayUuids = resolveGatewayUuidsBySpaceId(spaceId);
        if (gatewayUuids.isEmpty()) {
            return Collections.emptyList();
        }
        return dmpDeviceMapper.selectList(
                Wrappers.<DmpDevice>lambdaQuery()
                        .in(DmpDevice::getGatewayUuid, gatewayUuids)
                        .eq(DmpDevice::getCategoryType, 3)
                        .eq(DmpDevice::getDeleteFlag, 0));
    }

    @Override
    public List<EmsEnergyItemCode> listAreaDeviceItemCodes(Integer spaceId) {
        List<DmpDevice> devices = listAreaDevices(spaceId);
        if (devices.isEmpty()) {
            return Collections.emptyList();
        }
        List<String> deviceUuids = devices.stream()
                .map(DmpDevice::getDeviceUuid)
                .filter(StringUtils::hasText)
                .map(String::trim)
                .distinct()
                .collect(Collectors.toList());
        if (deviceUuids.isEmpty()) {
            return Collections.emptyList();
        }
        List<EmsDeviceItemCode> deviceItemCodes = emsDeviceItemCodeMapper.selectList(
                Wrappers.<EmsDeviceItemCode>lambdaQuery()
                        .in(EmsDeviceItemCode::getDeviceUuid, deviceUuids)
                        .eq(EmsDeviceItemCode::getTenantId, SecurityUtils.getTenantId()));
        if (deviceItemCodes.isEmpty()) {
            return Collections.emptyList();
        }
        List<String> itemCodes = deviceItemCodes.stream()
                .map(EmsDeviceItemCode::getItemCode)
                .filter(StringUtils::hasText)
                .map(String::trim)
                .distinct()
                .collect(Collectors.toList());
        if (itemCodes.isEmpty()) {
            return Collections.emptyList();
        }
        return emsEnergyItemCodeMapper.selectList(
                Wrappers.<EmsEnergyItemCode>lambdaQuery()
                        .in(EmsEnergyItemCode::getCode, itemCodes)
                        .orderByAsc(EmsEnergyItemCode::getCode));
    }

    private List<String> resolveGatewayUuidsBySpaceId(Integer spaceId) {
        if (spaceId == null) {
            throw new BusinessException("空间id不能为空");
        }
        BaseSpace space = baseSpaceService.getById(spaceId.longValue());
        if (space == null) {
            throw new BusinessException("空间不存在");
        }
        List<Long> spaceIds = baseSpaceService.getAuthorizedSpaceIds(space.getId());
        if (spaceIds.isEmpty()) {
            return Collections.emptyList();
        }
        List<BaseSpaceGateway> links = baseSpaceGatewayMapper.selectList(
                Wrappers.<BaseSpaceGateway>lambdaQuery()
                        .in(BaseSpaceGateway::getSpaceId, spaceIds));
        if (CollectionUtils.isEmpty(links)) {
            return Collections.emptyList();
        }
        return links.stream()
                .map(BaseSpaceGateway::getGatewayUuid)
                .filter(StringUtils::hasText)
                .map(String::trim)
                .distinct()
                .collect(Collectors.toList());
    }

    /**
     * 对应原 UserService.hasLoggedUser(projectId)。接入统一用户/权限模块后可在此查询用户与项目绑定关系。
     */
    private boolean hasLoggedUserForProject(Long projectId) {
        return false;
    }

    private void validateSaveRequest(EmsProjectSaveRequest request, boolean update) {
        if (!StringUtils.hasText(request.getName())) {
            throw new BusinessException("项目名称不能为空");
        }
        if (request.getTypeId() == null || !StringUtils.hasText(request.getTypeName())) {
            throw new BusinessException("项目类型不能为空");
        }
    }

    private Long createProjectSpace(String projectName) {
        LocalDateTime now = LocalDateTime.now();
        String audit = SecurityUtils.getUsername();
        BaseSpace space = new BaseSpace();
        space.setName(projectName);
        space.setDeep(1);
        space.setRootId(0L);
        space.setParentId(0L);
        space.setType(SPACE_TYPE_PROJECT);
        space.setPath("");
        space.setTenantId(SecurityUtils.getTenantId());
        space.setCreatedBy(audit);
        space.setUpdatedBy(audit);
        space.setCreateTime(now);
        space.setUpdateTime(now);
        baseSpaceService.save(space);
        return space.getId();
    }

    private void copyRequestToEntity(EmsProjectSaveRequest request, EmsProject po, Map<String, String> regionMap) {
        po.setId(request.getId());
        po.setName(request.getName());
        po.setPlatformName(request.getPlatformName());
        po.setAbbreviation(request.getAbbreviation());
        po.setArea(request.getArea());
        po.setCommonArea(request.getCommonArea());
        po.setAirConditionedArea(request.getAirConditionedArea());
        po.setResidentPopulation(request.getResidentPopulation());
        po.setProvinceCode(request.getProvinceCode());
        po.setCityCode(request.getCityCode());
        po.setDistrictCode(request.getDistrictCode());
        po.setProvinceName(regionMap.get(request.getProvinceCode()));
        po.setCityName(regionMap.get(request.getCityCode()));
        po.setDistrictName(regionMap.get(request.getDistrictCode()));
        po.setLocation(request.getLocation());
        po.setAddress(request.getAddress());
        po.setTypeId(request.getTypeId());
        po.setTypeName(request.getTypeName());
        po.setImage(request.getImage());
        po.setIntroduction(request.getIntroduction());
        po.setLogo(request.getLogo());
        po.setLogoMin(request.getLogoMin());
    }

    private void fillAuditOnCreate(EmsProject po) {
        LocalDateTime now = LocalDateTime.now();
        String uname = SecurityUtils.getUsername();
        po.setCreatedBy(uname);
        po.setUpdatedBy(uname);
        po.setCreateTime(now);
        po.setUpdateTime(now);
    }

    private void fillAuditOnUpdate(EmsProject po) {
        LocalDateTime now = LocalDateTime.now();
        String uname = SecurityUtils.getUsername();
        po.setUpdatedBy(uname);
        po.setUpdateTime(now);
    }

    private void replaceDeviceSystems(Long projectId, List<Integer> deviceSystemList) {
        emsProjectDeviceSystemMapper.delete(
                new LambdaQueryWrapper<EmsProjectDeviceSystem>().eq(EmsProjectDeviceSystem::getProjectId, projectId));
        if (CollectionUtils.isEmpty(deviceSystemList)) {
            return;
        }
        for (Integer code : deviceSystemList) {
            if (code == null) {
                continue;
            }
            EmsProjectDeviceSystem row = new EmsProjectDeviceSystem();
            row.setProjectId(projectId);
            row.setDeviceSystem(code);
            LocalDateTime now = LocalDateTime.now();
            String uname = SecurityUtils.getUsername();
            row.setUpdatedBy(uname);
            row.setUpdateTime(now);
            row.setCreatedBy(uname);
            row.setCreateTime(now);
            emsProjectDeviceSystemMapper.insert(row);
        }
    }

    private EmsProjectResponse toResponse(EmsProject po, List<Integer> deviceSystemList) {
        EmsProjectResponse resp = new EmsProjectResponse();
        resp.setId(po.getId());
        resp.setSpaceId(po.getSpaceId());
        resp.setName(po.getName());
        resp.setAbbreviation(po.getAbbreviation());
        resp.setArea(po.getArea());
        resp.setCommonArea(po.getCommonArea());
        resp.setAirConditionedArea(po.getAirConditionedArea());
        if (po.getResidentPopulation() != null) {
            resp.setResidentPopulation(java.math.BigDecimal.valueOf(po.getResidentPopulation()));
        }
        resp.setDeviceSystemList(deviceSystemList);
        resp.setProvinceCode(po.getProvinceCode());
        resp.setCityCode(po.getCityCode());
        resp.setDistrictCode(po.getDistrictCode());
        resp.setProvinceName(po.getProvinceName());
        resp.setCityName(po.getCityName());
        resp.setDistrictName(po.getDistrictName());
        resp.setLocation(po.getLocation());
        resp.setAddress(po.getAddress());
        resp.setTypeId(po.getTypeId());
        resp.setTypeName(po.getTypeName());
        resp.setImage(po.getImage());
        resp.setIntroduction(po.getIntroduction());
        resp.setPlatformName(po.getPlatformName());
        resp.setLogo(po.getLogo());
        resp.setLogoMin(po.getLogoMin());
        resp.setUpdatedBy(po.getUpdatedBy());
        resp.setCreatedBy(po.getCreatedBy());
        if (po.getUpdateTime() != null) {
            resp.setUpdateTime(po.getUpdateTime().toString());
        }
        if (po.getCreateTime() != null) {
            resp.setCreateTime(po.getCreateTime().toString());
        }
        return resp;
    }
}
