package cn.ysk.cashier.service.impl.app;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import cn.ysk.cashier.cons.RedisConstant;
import cn.ysk.cashier.dto.calltable.*;
import cn.ysk.cashier.exception.BadRequestException;
import cn.ysk.cashier.mybatis.entity.TbCallQueue;
import cn.ysk.cashier.mybatis.entity.TbCallTable;
import cn.ysk.cashier.mybatis.mapper.TbShopUserMapper;
import cn.ysk.cashier.mybatis.service.TbCallQueueService;
import cn.ysk.cashier.mybatis.service.TbCallTableService;
import cn.ysk.cashier.pojo.shop.TbShopInfo;
import cn.ysk.cashier.pojo.shop.TbShopUser;
import cn.ysk.cashier.repository.shop.TbShopInfoRepository;
import cn.ysk.cashier.service.app.TbCallService;
import cn.ysk.cashier.utils.Utils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class TbCallServiceImpl implements TbCallService {

    private final TbCallTableService callTableService;
    private final TbCallQueueService callQueueService;
    private final TbShopUserMapper shopUserMapper;
    private final TbShopInfoRepository shopInfoRepository;
    private final StringRedisTemplate redisTemplate;

    @Override
    public Object add(CallTableDTO addCallTableDTO) {
        Long count = callTableService.lambdaQuery()
                .eq(TbCallTable::getShopId, addCallTableDTO.getShopId())
                .and(q -> {
                    q.eq(TbCallTable::getName, addCallTableDTO.getName())
                            .or()
                            .eq(TbCallTable::getPrefix, addCallTableDTO.getPrefix());
                }).count();
        if (count > 0) {
            throw new BadRequestException("名称或前缀已存在");
        }

        TbCallTable callTable = BeanUtil.copyProperties(addCallTableDTO, TbCallTable.class);
        callTable.setCreateTime(DateUtil.date().toInstant());
        return callTableService.save(callTable);
    }

    @Override
    public Object update(UpdateCallTableDTO callTableDTO) {
        TbCallTable callTable = callTableService.lambdaQuery()
                .eq(TbCallTable::getShopId, callTableDTO.getShopId())
                .eq(TbCallTable::getId, callTableDTO.getCallTableId()).one();

        if (callTable == null) {
            throw new BadRequestException("桌型不存在");
        }
        TbCallTable newInfo = BeanUtil.copyProperties(callTableDTO, TbCallTable.class);
        newInfo.setId(callTable.getId());
        newInfo.setUpdateTime(DateUtil.date().toInstant());

        return callTableService.updateById(newInfo);
    }

    @Override
    public Object delete(BaseCallTableDTO baseCallTableDTO) {
        return callTableService.remove(new LambdaQueryWrapper<TbCallTable>()
                .eq(TbCallTable::getId, baseCallTableDTO.getCallTableId())
                .eq(TbCallTable::getShopId, baseCallTableDTO.getShopId()));
    }

    private String getCallNumber(Integer shopId, TbCallTable callTable) {
        return Utils.runFunAndCheckKey(() -> {
            String callNumKey = RedisConstant.getTableCallNumKey(shopId, callTable.getId());
            String value = redisTemplate.opsForValue().get(callNumKey);
            AtomicReference<String> newVal = new AtomicReference<>("");
            // 初始化
            if (StrUtil.isBlank(value)) {
                Boolean setFlag = Utils.runFunAndRetry(() -> redisTemplate.opsForValue().setIfAbsent(callNumKey, callTable.getStart().toString()), flag -> !flag,
                        r -> newVal.set(redisTemplate.opsForValue().get(callNumKey)));

                if (setFlag) {
                    return callTable.getPrefix() + callTable.getStart();
                }else if (StrUtil.isNotBlank(newVal.get())){
                    value = String.valueOf((Integer.parseInt(newVal.get()) + 1));
                    redisTemplate.opsForValue().set(callNumKey, value);
                    return callTable.getPrefix() + value;
                }else {
                    throw new BadRequestException("生成排队号码失败");
                }

            }else {
                value = String.valueOf((Integer.parseInt(value) + 1));
                redisTemplate.opsForValue().set(callNumKey, value);
                return callTable.getPrefix() + value;
            }
        }, redisTemplate, RedisConstant.getLockKey("UPDATE_TABLE", shopId, callTable.getId()));

    }

    @Override
    public Object takeNumber(TakeNumberDTO takeNumberDTO) {
        TbShopInfo shopInfo = shopInfoRepository.findById(takeNumberDTO.getShopId()).orElse(null);
        if (shopInfo == null) {
            throw new BadRequestException("店铺信息不存在");
        }

        TbCallTable callTable = callTableService.lambdaQuery()
                .eq(TbCallTable::getShopId, takeNumberDTO.getShopId())
                .eq(TbCallTable::getId, takeNumberDTO.getCallTableId()).one();
        if (callTable == null) {
            throw new BadRequestException("桌型不存在");
        }

        // 查询当前

        // 拿取系统内部用户信息
        TbCallQueue callQueue;
        if (takeNumberDTO.getUserId() != null) {
            TbShopUser shopUser = shopUserMapper.selectOne(new LambdaQueryWrapper<TbShopUser>()
                    .eq(TbShopUser::getStatus, 1)
                    .eq(TbShopUser::getShopId, takeNumberDTO.getShopId())
                    .eq(TbShopUser::getId, takeNumberDTO.getUserId()));
            if (shopUser == null) {
                throw new BadRequestException("用户不存在");
            }

            callQueue = callQueueService.lambdaQuery()
                    .eq(TbCallQueue::getUserId, shopUser.getId())
                    .eq(TbCallQueue::getShopId, takeNumberDTO.getShopId())
                    .in(TbCallQueue::getState, 0, 1)
                    .eq(TbCallQueue::getCallTableId, takeNumberDTO.getCallTableId()).one();
            if (callQueue != null) {
                throw new BadRequestException("当前用户已取号");
            }

            callQueue = BeanUtil.copyProperties(takeNumberDTO, TbCallQueue.class);
            callQueue.setPhone(StrUtil.isBlank(takeNumberDTO.getPhone()) ? shopUser.getTelephone() : takeNumberDTO.getPhone());

//            callQueue.setOpenId(shopUser.getMiniOpenId());

        }else {
//            if (StrUtil.isBlank(takeNumberDTO.getPhone()) || StrUtil.isBlank(takeNumberDTO.getOpenId())) {
//                throw new BadRequestException("手机号或openId不能为空");
//            }

            callQueue = callQueueService.lambdaQuery()
                    .eq(TbCallQueue::getPhone, takeNumberDTO.getPhone())
                    .eq(TbCallQueue::getShopId, takeNumberDTO.getShopId())
                    .eq(TbCallQueue::getCallTableId, takeNumberDTO.getCallTableId()).one();
            if (callQueue != null) {
                throw new BadRequestException("当前用户已取号");
            }

            callQueue = BeanUtil.copyProperties(takeNumberDTO, TbCallQueue.class);
            callQueue.setPhone(takeNumberDTO.getPhone());
//            callQueue.setOpenId(takeNumberDTO.getOpenId());
            callQueue.setSubState(1);
        }

        callQueue.setCallNum(getCallNumber(takeNumberDTO.getShopId(), callTable));
        callQueue.setCreateTime(DateUtil.date().toInstant());
        callQueue.setShopId(shopInfo.getId());
        callQueue.setShopName(shopInfo.getShopName());

        return callQueueService.save(callQueue);
    }

    @Override
    public Object call(CallQueueDTO callQueueDTO) {
        TbCallQueue callQueue = callQueueService.lambdaQuery()
                .notIn(TbCallQueue::getState, -1, 2)
                .eq(TbCallQueue::getShopId, callQueueDTO.getShopId())
                .one();

        if (callQueue == null) {
            throw new BadRequestException("叫号用户不存在");
        }

        if (callQueue.getSubState().equals(0)) {
            throw new BadRequestException("当前用户未订阅微信提醒");
        }

        callQueue.setState((byte) 1);
        callQueue.setCallCount(callQueue.getCallCount() + 1);
        callQueue.setCallTime(DateUtil.date().toInstant());

        // 发送模板消息通知用户

        return callQueueService.updateById(callQueue);
    }

    @Override
    public Object updateInfo(UpdateCallQueueDTO updateCallQueueDTO) {
        TbCallQueue callQueue = callQueueService.lambdaQuery()
                .eq(TbCallQueue::getShopId, updateCallQueueDTO.getShopId())
                .eq(TbCallQueue::getId, updateCallQueueDTO.getCallQueueId()).one();
        if (callQueue == null) {
            throw new BadRequestException("叫号用户不存在");
        }

        switch (updateCallQueueDTO.getState()) {
            case -1:
                callQueue.setCancelTime(DateUtil.date().toInstant());
                break;
            case 0:
                if (callQueue.getSubState().equals(0)) {
                    throw new BadRequestException("当前用户未订阅微信提醒");
                }

                callQueue.setState((byte) 1);
                callQueue.setCallCount(callQueue.getCallCount() + 1);
                callQueue.setCallTime(DateUtil.date().toInstant());
                break;
            case 2:
                callQueue.setConfirmTime(DateUtil.date().toInstant());
                break;
            case 3:
                callQueue.setPassTime(DateUtil.date().toInstant());
                break;
            default:
                throw new BadRequestException("错误类型");

        }

        callQueue.setState(updateCallQueueDTO.getState());

        return callQueueService.updateById(callQueue);
    }

    @Override
    public Object get(Integer page, Integer size, Integer shopId, Integer callTableId, Integer state) {
        LambdaQueryWrapper<TbCallTable> query = new LambdaQueryWrapper<TbCallTable>()
                .eq(TbCallTable::getShopId, shopId)
                .eq(TbCallTable::getState, 1);

        if (callTableId != null) {
            query.eq(TbCallTable::getId, callTableId);
        }

        if (state != null) {
            query.eq(TbCallTable::getState, state);
        }
        return callTableService.page(new Page<>(page, size), query);
    }

    @Override
    public Object takeNumberCode(Integer shopId, Integer callTableId) {
        // 创建二维码配置对象，设置宽度和高度为400
        QrConfig config = new QrConfig(400, 400);

        // 使用字节数组输出流来存储二维码图片
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        // 生成二维码图片，输出到字节数组输出流
        QrCodeUtil.generate("Your QR Code Content Here", config, "png", outputStream);

        // 将图片转换为 Base64 字符串
        String base64 = Base64.getEncoder().encodeToString(outputStream.toByteArray());

        // 返回Base64格式的图片字符串
        return "data:image/png;base64," + base64;
    }

    @Override
    public Object getQueue(Integer shopId, Integer callTableId, Integer state, Integer page, Integer size) {
        List<Integer> tableIds;
        if (callTableId != null) {
            tableIds = Collections.singletonList(callTableId);
        }else {
            List<TbCallTable> list = callTableService.lambdaQuery()
                    .eq(TbCallTable::getShopId, shopId)
                    .eq(TbCallTable::getState, 1).list();
            if (list.isEmpty()) {
                return new Page<>();
            }
            tableIds = list.stream()
                    .map(TbCallTable::getId)
                    .collect(Collectors.toList());
        }

        LambdaQueryChainWrapper<TbCallQueue> query = callQueueService.lambdaQuery()
                .eq(TbCallQueue::getShopId, shopId)
                .in(TbCallQueue::getCallTableId, tableIds);
        if (state != null) {
            query.eq(TbCallQueue::getState, state);
        }
        Page<TbCallQueue> pageInfo = query
                .orderByAsc(TbCallQueue::getCreateTime)
                .orderByDesc(TbCallQueue::getState)
                .page(new Page<>(page, size));

        List<TbCallQueue> list1 = pageInfo.getRecords();

        // 创建返回的结果集
        List<Map<String, Object>> resultList = new ArrayList<>();

        // 遍历每一个叫号中的记录，计算前面状态为"0" (排队中) 的人数
        for (TbCallQueue calling : list1) {
            // 计算前面等待的人数 (状态为"0"且在叫号记录创建时间之前的)
            long waitingCount = 0;
            if (calling.getState() == 0) {
                waitingCount = list1.stream()
                        .filter(item -> item.getState() == 0 || item.getState() == 1)  // 过滤出状态为"排队中"的
                        .filter(item -> item.getCreateTime().compareTo(calling.getCreateTime()) < 0 )  // 时间在当前叫号之前
                        .count();
            }

            // 创建一个Map来保存叫号中的记录及其前面排队的人数
            Map<String, Object> map = BeanUtil.beanToMap(calling, false, false);
            map.put("waitingCount", waitingCount);
            // 将该map加入结果集
            resultList.add(map);
        }

        Map<String, Object> toMap = BeanUtil.beanToMap(pageInfo, false, false);
        toMap.put("records", resultList);

        // 返回结果列表
        return toMap;
    }
}
