package com.aegis.resource.expert.service.impl;

import com.aegis.common.exception.ServiceException;
import com.aegis.common.utils.SecurityUtils;
import com.aegis.common.utils.StringUtils;
import com.aegis.resource.expert.constant.ExpertConstants;
import com.aegis.resource.expert.domain.Expert;
import com.aegis.resource.expert.mapper.ResourceExpertMapper;
import com.aegis.resource.expert.service.IResourceExpertService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 专家Service业务层处理（MyBatis-Plus）
 *
 * @author aegis
 */
@Service
public class ResourceExpertServiceImpl extends ServiceImpl<ResourceExpertMapper, Expert>
    implements IResourceExpertService {

    @Override
    public List<Expert> listByCondition(Expert condition) {
        LambdaQueryWrapper<Expert> wrapper = buildListWrapper(condition);
        wrapper.orderByDesc(Expert::getRating).orderByDesc(Expert::getCreateTime);
        return list(wrapper);
    }

    @Override
    public Expert getById(Serializable id) {
        if (id == null) {
            return null;
        }
        return lambdaQuery()
            .eq(Expert::getId, id)
            .eq(Expert::getDelFlag, ExpertConstants.DEL_FLAG_NORMAL)
            .one();
    }

    @Override
    public List<Expert> listExpertsBySpecialty(String specialty) {
        return lambdaQuery()
            .eq(Expert::getDelFlag, ExpertConstants.DEL_FLAG_NORMAL)
            .eq(Expert::getStatus, ExpertConstants.STATUS_ACTIVE)
            .like(StringUtils.isNotBlank(specialty), Expert::getSpecialties, specialty)
            .orderByDesc(Expert::getRating)
            .orderByDesc(Expert::getCreateTime)
            .list();
    }

    @Override
    @Transactional
    public boolean save(Expert expert) {
        if (expert == null) {
            throw new ServiceException("专家信息不能为空");
        }
        ensureExpertCodeUnique(expert.getExpertCode(), null);
        ensureSysUserLinkUnique(expert.getSysUserId(), null);
        Expert deleted = findDeletedExpertByCode(expert.getExpertCode());
        String operator = SecurityUtils.getUsername();
        Date now = new Date();
        Expert toSave = buildExpertForSave(expert, operator, now);
        if (deleted != null) {
            toSave.setId(deleted.getId());
            toSave.setCreateBy(deleted.getCreateBy());
            toSave.setCreateTime(deleted.getCreateTime());
            return super.updateById(toSave);
        }
        return super.save(toSave);
    }

    @Override
    @Transactional
    public boolean updateById(Expert expert) {
        if (expert == null || expert.getId() == null) {
            throw new ServiceException("专家ID不能为空");
        }
        Expert oldExpert = super.getById(expert.getId());
        Date now = new Date();
        String operator = SecurityUtils.getUsername();
        if (oldExpert == null) {
            Expert deletedExpert = findDeletedExpertByCode(expert.getExpertCode());
            if (deletedExpert != null && deletedExpert.getId().equals(expert.getId())) {
                Expert restored = buildExpertForUpdate(deletedExpert, expert, operator, now);
                restored.setDelFlag(ExpertConstants.DEL_FLAG_NORMAL);
                return super.updateById(restored);
            }
            throw new ServiceException("专家不存在");
        }

        if (StringUtils.isNotEmpty(expert.getExpertCode())
            && !expert.getExpertCode().equals(oldExpert.getExpertCode())) {
        ensureExpertCodeUnique(expert.getExpertCode(), expert.getId());
        ensureSysUserLinkUnique(expert.getSysUserId(), expert.getId());
            Expert deletedExpertWithNewCode = findDeletedExpertByCode(expert.getExpertCode());
            if (deletedExpertWithNewCode != null) {
                throw new ServiceException("修改专家'" + expert.getExpertName() + "'失败，该专家编号已被删除，请先恢复该记录");
            }
        }

        Expert updated = buildExpertForUpdate(oldExpert, expert, operator, now);
        return super.updateById(updated);
    }

    @Override
    @Transactional
    public int rateExpert(Long id, Integer rating) {
        Expert expert = this.getById(id);
        if (expert == null) {
            throw new ServiceException("专家不存在");
        }
        validateRating(rating);
        if (ExpertConstants.STATUS_BLACKLISTED.equals(expert.getStatus())) {
            throw new ServiceException("黑名单专家无法进行评分");
        }
        expert.setRating(rating);
        expert.setUpdateBy(SecurityUtils.getUsername());
        expert.setUpdateTime(new Date());
        return super.updateById(expert) ? 1 : 0;
    }

    @Override
    @Transactional
    public boolean removeByIds(Collection<?> idList) {
        if (idList == null || idList.isEmpty()) {
            return false;
        }
        String operator = SecurityUtils.getUsername();
        Date now = new Date();
        boolean allSuccess = true;
        for (Object idObj : idList) {
            if (idObj == null) {
                continue;
            }
            Long id = (Long) idObj;
            Expert expert = super.getById(id);
            if (expert == null) {
                throw new ServiceException("专家不存在，ID: " + id);
            }
            softDelete(expert, operator, now);
            allSuccess &= super.updateById(expert);
        }
        return allSuccess;
    }

    @Override
    @Transactional
    public boolean removeById(Serializable id) {
        if (id == null) {
            throw new ServiceException("专家ID不能为空");
        }
        Expert expert = super.getById(id);
        if (expert == null) {
            throw new ServiceException("专家不存在");
        }
        softDelete(expert, SecurityUtils.getUsername(), new Date());
        return super.updateById(expert);
    }

    @Override
    @Transactional
    public int activateExpert(Long id) {
        Expert expert = this.getById(id);
        if (expert == null) {
            throw new ServiceException("专家不存在");
        }
        updateStatus(expert, ExpertConstants.STATUS_ACTIVE);
        expert.setUpdateBy(SecurityUtils.getUsername());
        expert.setUpdateTime(new Date());
        return super.updateById(expert) ? 1 : 0;
    }

    @Override
    @Transactional
    public int deactivateExpert(Long id) {
        Expert expert = this.getById(id);
        if (expert == null) {
            throw new ServiceException("专家不存在");
        }
        updateStatus(expert, ExpertConstants.STATUS_INACTIVE);
        expert.setUpdateBy(SecurityUtils.getUsername());
        expert.setUpdateTime(new Date());
        return super.updateById(expert) ? 1 : 0;
    }

    @Override
    @Transactional
    public int blacklistExpert(Long id) {
        Expert expert = this.getById(id);
        if (expert == null) {
            throw new ServiceException("专家不存在");
        }
        updateStatus(expert, ExpertConstants.STATUS_BLACKLISTED);
        expert.setUpdateBy(SecurityUtils.getUsername());
        expert.setUpdateTime(new Date());
        return super.updateById(expert) ? 1 : 0;
    }

    @Override
    @Transactional
    public int unblacklistExpert(Long id) {
        Expert expert = this.getById(id);
        if (expert == null) {
            throw new ServiceException("专家不存在");
        }
        if (!ExpertConstants.STATUS_BLACKLISTED.equals(expert.getStatus())) {
            return 0;
        }
        expert.setStatus(ExpertConstants.STATUS_INACTIVE);
        expert.setUpdateBy(SecurityUtils.getUsername());
        expert.setUpdateTime(new Date());
        return super.updateById(expert) ? 1 : 0;
    }


    private LambdaQueryWrapper<Expert> buildListWrapper(Expert condition) {
        LambdaQueryWrapper<Expert> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Expert::getDelFlag, ExpertConstants.DEL_FLAG_NORMAL);
        if (condition == null) {
            return wrapper;
        }
        wrapper.eq(condition.getSysUserId() != null, Expert::getSysUserId, condition.getSysUserId());
        wrapper.like(StringUtils.isNotBlank(condition.getExpertName()), Expert::getExpertName, condition.getExpertName())
            .eq(StringUtils.isNotBlank(condition.getExpertCode()), Expert::getExpertCode, condition.getExpertCode())
            .like(StringUtils.isNotBlank(condition.getOrganization()), Expert::getOrganization, condition.getOrganization())
            .like(StringUtils.isNotBlank(condition.getSpecialties()), Expert::getSpecialties, condition.getSpecialties())
            .eq(StringUtils.isNotBlank(condition.getStatus()), Expert::getStatus, condition.getStatus())
            .eq(condition.getRating() != null, Expert::getRating, condition.getRating())
            .like(StringUtils.isNotBlank(condition.getPhone()), Expert::getPhone, condition.getPhone())
            .like(StringUtils.isNotBlank(condition.getLocationName()), Expert::getLocationName, condition.getLocationName());
        return wrapper;
    }

    private Expert findDeletedExpertByCode(String expertCode) {
        if (StringUtils.isBlank(expertCode)) {
            return null;
        }
        return lambdaQuery()
            .eq(Expert::getExpertCode, expertCode)
            .eq(Expert::getDelFlag, ExpertConstants.DEL_FLAG_DELETED)
            .last("limit 1")
            .one();
    }

    private Expert buildExpertForSave(Expert source, String operator, Date now) {
        Expert target = new Expert();
        copyBasicFields(target, source);
        applyDefaults(target, operator, now);
        return target;
    }

    private Expert buildExpertForUpdate(Expert original, Expert incoming, String operator, Date now) {
        copyBasicFields(original, incoming);
        if (incoming.getRating() != null) {
            validateRating(incoming.getRating());
            original.setRating(incoming.getRating());
        }
        updateStatus(original, incoming.getStatus());
        original.setUpdateBy(operator);
        original.setUpdateTime(now);
        return original;
    }

    private void copyBasicFields(Expert target, Expert source) {
        target.setExpertName(source.getExpertName());
        target.setExpertCode(source.getExpertCode());
        target.setIdCard(source.getIdCard());
        target.setOrganization(source.getOrganization());
        target.setSpecialties(source.getSpecialties());
        target.setPhone(source.getPhone());
        target.setEmail(source.getEmail());
        target.setCapabilities(source.getCapabilities());
        target.setContacts(source.getContacts());
        target.setSysUserId(source.getSysUserId());
        target.setRemark(source.getRemark());
        validateLocation(source.getLocationName(), source.getLongitude(), source.getLatitude());
        target.setLocationName(normalizeLocation(source.getLocationName()));
        target.setLongitude(source.getLongitude());
        target.setLatitude(source.getLatitude());
    }

    private void applyDefaults(Expert expert, String operator, Date now) {
        if (StringUtils.isBlank(expert.getStatus())) {
            expert.setStatus(ExpertConstants.STATUS_ACTIVE);
        }
        if (expert.getRating() == null) {
            expert.setRating(ExpertConstants.DEFAULT_RATING);
        } else {
            validateRating(expert.getRating());
        }
        expert.setCreateBy(operator);
        expert.setCreateTime(now);
        expert.setUpdateBy(operator);
        expert.setUpdateTime(now);
        expert.setDelFlag(ExpertConstants.DEL_FLAG_NORMAL);
    }

    private void validateRating(Integer rating) {
        if (rating == null) {
            return;
        }
        if (rating < ExpertConstants.MIN_RATING || rating > ExpertConstants.MAX_RATING) {
            throw new ServiceException("评分必须在" + ExpertConstants.MIN_RATING + "-" + ExpertConstants.MAX_RATING + "之间");
        }
    }

    private void validateLocation(String locationName, BigDecimal longitude, BigDecimal latitude) {
        if (StringUtils.isNotBlank(locationName) && locationName.length() > 255) {
            throw new ServiceException("常驻地点名称长度不能超过255个字符");
        }
        if (longitude != null && (longitude.compareTo(BigDecimal.valueOf(-180)) < 0
            || longitude.compareTo(BigDecimal.valueOf(180)) > 0)) {
            throw new ServiceException("经度范围必须在-180到180之间");
        }
        if (latitude != null && (latitude.compareTo(BigDecimal.valueOf(-90)) < 0
            || latitude.compareTo(BigDecimal.valueOf(90)) > 0)) {
            throw new ServiceException("纬度范围必须在-90到90之间");
        }
    }

    private void updateStatus(Expert expert, String newStatus) {
        if (StringUtils.isBlank(newStatus) || newStatus.equals(expert.getStatus())) {
            return;
        }
        String current = StringUtils.defaultIfBlank(expert.getStatus(), ExpertConstants.STATUS_INACTIVE);
        switch (newStatus) {
            case ExpertConstants.STATUS_ACTIVE:
                if (ExpertConstants.STATUS_BLACKLISTED.equals(current)) {
                    throw new ServiceException("黑名单专家不能直接启用");
                }
                expert.setStatus(ExpertConstants.STATUS_ACTIVE);
                break;
            case ExpertConstants.STATUS_INACTIVE:
                if (ExpertConstants.STATUS_BLACKLISTED.equals(current)) {
                    throw new ServiceException("黑名单专家请先解除黑名单后再停用");
                }
                expert.setStatus(ExpertConstants.STATUS_INACTIVE);
                break;
            case ExpertConstants.STATUS_BLACKLISTED:
                if (!ExpertConstants.STATUS_ACTIVE.equals(current) && !ExpertConstants.STATUS_INACTIVE.equals(current)) {
                    throw new ServiceException("当前状态无法加入黑名单");
                }
                expert.setStatus(ExpertConstants.STATUS_BLACKLISTED);
                break;
            default:
                expert.setStatus(newStatus);
                break;
        }
    }

    private void softDelete(Expert expert, String operator, Date now) {
        if (ExpertConstants.DEL_FLAG_DELETED.equals(expert.getDelFlag())) {
            return;
        }
        expert.setDelFlag(ExpertConstants.DEL_FLAG_DELETED);
        expert.setUpdateBy(operator);
        expert.setUpdateTime(now);
    }

    private String normalizeLocation(String locationName) {
        return StringUtils.isBlank(locationName) ? null : locationName.trim();
    }

    private void ensureExpertCodeUnique(String expertCode, Long currentId) {
        if (StringUtils.isBlank(expertCode)) {
            return;
        }
        LambdaQueryWrapper<Expert> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Expert::getExpertCode, expertCode)
            .eq(Expert::getDelFlag, ExpertConstants.DEL_FLAG_NORMAL);
        if (currentId != null) {
            wrapper.ne(Expert::getId, currentId);
        }
        long exists = count(wrapper);
        if (exists > 0) {
            throw new ServiceException("专家编号已存在：" + expertCode);
        }
    }

    private void ensureSysUserLinkUnique(Long sysUserId, Long currentId) {
        if (sysUserId == null) {
            return;
        }
        LambdaQueryWrapper<Expert> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Expert::getSysUserId, sysUserId)
            .eq(Expert::getDelFlag, ExpertConstants.DEL_FLAG_NORMAL);
        if (currentId != null) {
            wrapper.ne(Expert::getId, currentId);
        }
        if (count(wrapper) > 0) {
            throw new ServiceException("系统用户已绑定其他专家，无法重复绑定");
        }
    }
}