package org.dromara.main.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.github.linpeilie.Converter;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.main.domain.CsSeatConfig;
import org.dromara.main.domain.CsSeatWaiter;
import org.dromara.main.domain.bo.CsSeatConfigBo;
import org.dromara.main.domain.vo.CsSeatConfigVo;
import org.dromara.main.mapper.CsSeatConfigMapper;
import org.dromara.main.mapper.CsSeatWaiterMapper;
import org.dromara.main.service.ICsSeatConfigService;
import org.dromara.system.domain.SysUser;
import org.dromara.system.mapper.SysUserMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Service
public class CsSeatConfigServiceImpl implements ICsSeatConfigService {

    private final CsSeatConfigMapper baseMapper;
    private final CsSeatWaiterMapper waiterMapper;
    private final SysUserMapper sysUserMapper;
    private final Converter converter;

    @Override
    public CsSeatConfigVo queryById(Long id) {
        CsSeatConfigVo vo = baseMapper.selectVoById(id);
        if (vo != null) {
            fillWaitersForVo(vo);
        }
        return vo;
    }

    @Override
    public TableDataInfo<CsSeatConfigVo> queryPageList(CsSeatConfigBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<CsSeatConfig> lqw = buildQueryWrapper(bo);
        // 使用 selectVoPage 直接查询 Vo 的 Page
        Page<CsSeatConfigVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        
        List<CsSeatConfigVo> records = result.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            // 批量查询所有关联记录以提高性能
            List<Long> seatIds = records.stream().map(CsSeatConfigVo::getId).collect(Collectors.toList());
            List<CsSeatWaiter> allWaiters = waiterMapper.selectList(Wrappers.lambdaQuery(CsSeatWaiter.class).in(CsSeatWaiter::getSeatId, seatIds));
            
            if (CollUtil.isNotEmpty(allWaiters)) {
                List<Long> allUserIds = allWaiters.stream().map(CsSeatWaiter::getUserId).distinct().collect(Collectors.toList());
                List<SysUser> users = sysUserMapper.selectBatchIds(allUserIds);
                Map<Long, SysUser> userMap = users.stream().collect(Collectors.toMap(SysUser::getUserId, u -> u));
                
                // 按 seatId 分组
                Map<Long, List<CsSeatWaiter>> seatWaiterMap = allWaiters.stream().collect(Collectors.groupingBy(CsSeatWaiter::getSeatId));
                
                for (CsSeatConfigVo vo : records) {
                    List<CsSeatWaiter> seatWaiters = seatWaiterMap.get(vo.getId());
                    if (CollUtil.isNotEmpty(seatWaiters)) {
                        List<CsSeatConfigVo.WaiterInfo> waiterInfos = seatWaiters.stream().map(sw -> {
                            SysUser user = userMap.get(sw.getUserId());
                            if (user != null) {
                                CsSeatConfigVo.WaiterInfo info = new CsSeatConfigVo.WaiterInfo();
                                info.setUserId(user.getUserId());
                                info.setUserName(user.getUserName());
                                info.setNickName(user.getNickName());
                                return info;
                            }
                            return null;
                        }).filter(info -> info != null).collect(Collectors.toList());
                        vo.setWaiters(waiterInfos);
                    }
                }
            }
        }
        
        return TableDataInfo.build(result);
    }

    @Override
    public List<CsSeatConfigVo> queryList(CsSeatConfigBo bo) {
        LambdaQueryWrapper<CsSeatConfig> lqw = buildQueryWrapper(bo);
        List<CsSeatConfigVo> list = baseMapper.selectVoList(lqw);
        if (CollUtil.isNotEmpty(list)) {
            for (CsSeatConfigVo vo : list) {
                fillWaitersForVo(vo);
            }
        }
        return list;
    }

    private void fillWaitersForVo(CsSeatConfigVo vo) {
        List<CsSeatWaiter> relations = waiterMapper.selectList(Wrappers.lambdaQuery(CsSeatWaiter.class).eq(CsSeatWaiter::getSeatId, vo.getId()));
        if (CollUtil.isNotEmpty(relations)) {
            List<Long> userIds = relations.stream().map(CsSeatWaiter::getUserId).collect(Collectors.toList());
            List<SysUser> users = sysUserMapper.selectBatchIds(userIds);
            if (CollUtil.isNotEmpty(users)) {
                List<CsSeatConfigVo.WaiterInfo> waiterInfos = users.stream().map(u -> {
                    CsSeatConfigVo.WaiterInfo info = new CsSeatConfigVo.WaiterInfo();
                    info.setUserId(u.getUserId());
                    info.setUserName(u.getUserName());
                    info.setNickName(u.getNickName());
                    return info;
                }).collect(Collectors.toList());
                vo.setWaiters(waiterInfos);
            }
        }
    }

    private LambdaQueryWrapper<CsSeatConfig> buildQueryWrapper(CsSeatConfigBo bo) {
        LambdaQueryWrapper<CsSeatConfig> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getSeatName()), CsSeatConfig::getSeatName, bo.getSeatName());
        lqw.eq(StringUtils.isNotBlank(bo.getModule()), CsSeatConfig::getModule, bo.getModule());
        lqw.eq(bo.getStatus() != null, CsSeatConfig::getStatus, bo.getStatus());
        lqw.orderByDesc(CsSeatConfig::getCreateTime);
        return lqw;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(CsSeatConfigBo bo) {
        CsSeatConfig add = BeanUtil.toBean(bo, CsSeatConfig.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
            if (CollUtil.isNotEmpty(bo.getWaiterIds())) {
                assignWaiters(add.getId(), bo.getWaiterIds());
            }
        }
        return flag;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(CsSeatConfigBo bo) {
        CsSeatConfig update = BeanUtil.toBean(bo, CsSeatConfig.class);
        validEntityBeforeSave(update);
        boolean flag = baseMapper.updateById(update) > 0;
        if (flag && bo.getWaiterIds() != null) {
            waiterMapper.delete(Wrappers.lambdaQuery(CsSeatWaiter.class)
                .eq(CsSeatWaiter::getSeatId, bo.getId()));
            if (CollUtil.isNotEmpty(bo.getWaiterIds())) {
                assignWaiters(bo.getId(), bo.getWaiterIds());
            }
        }
        return flag;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(Long id) {
        waiterMapper.delete(Wrappers.lambdaQuery(CsSeatWaiter.class)
            .eq(CsSeatWaiter::getSeatId, id));
        return baseMapper.deleteById(id) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteByIds(List<Long> ids) {
        waiterMapper.delete(Wrappers.lambdaQuery(CsSeatWaiter.class)
            .in(CsSeatWaiter::getSeatId, ids));
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public Boolean changeStatus(Long id, Integer status) {
        CsSeatConfig update = new CsSeatConfig();
        update.setId(id);
        update.setStatus(status);
        return baseMapper.updateById(update) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean assignWaiters(Long seatId, List<Long> waiterIds) {
        if (CollUtil.isEmpty(waiterIds)) {
            return true;
        }
        List<CsSeatWaiter> waiters = waiterIds.stream().map(userId -> {
            CsSeatWaiter waiter = new CsSeatWaiter();
            waiter.setSeatId(seatId);
            waiter.setUserId(userId);
            return waiter;
        }).collect(Collectors.toList());
        return waiterMapper.insertBatch(waiters);
    }

    private void validEntityBeforeSave(CsSeatConfig entity) {
        // TODO: 数据校验
    }
}
