package shop.alien.store.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import shop.alien.entity.store.StoreTable;
import shop.alien.entity.store.StoreTableLog;
import shop.alien.entity.store.dto.StoreTableChangeDTO;
import shop.alien.entity.store.dto.StoreTableDTO;
import shop.alien.entity.store.vo.StoreTableStatusVO;
import shop.alien.mapper.StoreTableLogMapper;
import shop.alien.mapper.StoreTableMapper;
import shop.alien.store.config.BaseRedisService;
import shop.alien.store.service.StoreTableService;
import shop.alien.store.service.WeChatMiniProgramQrCodeService;
import shop.alien.util.common.JwtUtil;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 桌号表 服务实现类
 *
 * @author system
 * @since 2025-01-XX
 */
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class StoreTableServiceImpl extends ServiceImpl<StoreTableMapper, StoreTable> implements StoreTableService {

    private final StoreTableLogMapper storeTableLogMapper;
    private final WeChatMiniProgramQrCodeService weChatMiniProgramQrCodeService;
    private final BaseRedisService baseRedisService;

    @Override
    public IPage<StoreTable> getTablePage(Integer pageNum, Integer pageSize, Integer storeId, Integer status) {
        log.info("StoreTableServiceImpl.getTablePage?pageNum={}&pageSize={}&storeId={}&status={}", pageNum, pageSize, storeId, status);
        LambdaQueryWrapper<StoreTable> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StoreTable::getStoreId, storeId)
                .eq(status != null, StoreTable::getStatus, status)
                .orderByAsc(StoreTable::getTableNumber);
        return this.page(new Page<>(pageNum, pageSize), wrapper);
    }

    @Override
    public boolean batchCreateTables(Integer storeId, List<String> tableNumbers) {
        log.info("StoreTableServiceImpl.batchCreateTables?storeId={}&tableNumbers={}", storeId, tableNumbers);
        
        // 从JWT获取当前登录用户ID
        Integer userId = getCurrentUserId();
        
        if (storeId == null || tableNumbers == null || tableNumbers.isEmpty()) {
            log.warn("批量创建桌号失败：参数不完整");
            return false;
        }

        // 验证桌号长度（限5字）
        for (String tableNumber : tableNumbers) {
            if (tableNumber != null && tableNumber.length() > 5) {
                log.warn("批量创建桌号失败：桌号长度超过5字，{}", tableNumber);
                throw new RuntimeException("桌号长度不能超过5字：" + tableNumber);
            }
        }
        
        // 检查桌号是否已存在
        LambdaQueryWrapper<StoreTable> checkWrapper = new LambdaQueryWrapper<>();
        checkWrapper.eq(StoreTable::getStoreId, storeId)
                .in(StoreTable::getTableNumber, tableNumbers);
        List<StoreTable> existingTables = this.list(checkWrapper);
        
        if (!existingTables.isEmpty()) {
            List<String> existingNumbers = existingTables.stream()
                    .map(StoreTable::getTableNumber)
                    .collect(Collectors.toList());
            log.warn("批量创建桌号失败：桌号已存在，{}", existingNumbers);
            throw new RuntimeException("此桌号已存在：" + String.join(",", existingNumbers));
        }

        // 批量创建
        List<StoreTable> tables = tableNumbers.stream()
                .map(tableNumber -> {
                    StoreTable table = new StoreTable();
                    table.setStoreId(storeId);
                    table.setTableNumber(tableNumber);
                    table.setStatus(0); // 默认空闲
                    table.setCreatedUserId(userId);
                    return table;
                })
                .collect(Collectors.toList());

        boolean result = this.saveBatch(tables);
        
        // 批量创建成功后，异步生成小程序二维码
        if (result) {
            asyncGenerateQrCodesForTables(storeId, tableNumbers);
        }
        
        return result;
    }

    /**
     * 异步为新创建的桌号生成二维码
     */
    @Async
    public void asyncGenerateQrCodesForTables(Integer storeId, List<String> tableNumbers) {
        log.info("开始异步生成桌号二维码, storeId={}, tableNumbers={}", storeId, tableNumbers);
        try {
            // 查询刚创建的桌号
            LambdaQueryWrapper<StoreTable> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(StoreTable::getStoreId, storeId)
                    .in(StoreTable::getTableNumber, tableNumbers)
                    .isNull(StoreTable::getQrcodeUrl);
            List<StoreTable> tables = this.list(wrapper);
            
            for (StoreTable table : tables) {
                try {
                    String qrCodeUrl = weChatMiniProgramQrCodeService.generateTableQrCode(table.getId(), storeId);
                    if (qrCodeUrl != null && !qrCodeUrl.isEmpty()) {
                        // 更新二维码URL
                        LambdaUpdateWrapper<StoreTable> updateWrapper = new LambdaUpdateWrapper<>();
                        updateWrapper.eq(StoreTable::getId, table.getId())
                                .set(StoreTable::getQrcodeUrl, qrCodeUrl);
                        this.update(updateWrapper);
                        log.info("桌号二维码生成成功, tableId={}, qrCodeUrl={}", table.getId(), qrCodeUrl);
                    }
                } catch (Exception e) {
                    log.error("生成桌号二维码失败, tableId={}, error={}", table.getId(), e.getMessage(), e);
                }
            }
            log.info("异步生成桌号二维码完成, storeId={}, count={}", storeId, tables.size());
        } catch (Exception e) {
            log.error("异步生成桌号二维码异常, storeId={}, error={}", storeId, e.getMessage(), e);
        }
    }

    @Override
    public boolean updateTable(StoreTableDTO dto) {
        log.info("StoreTableServiceImpl.updateTable?dto={}", dto);
        
        StoreTable table = this.getById(dto.getId());
        if (table == null) {
            log.warn("更新桌号失败：桌号不存在，id={}", dto.getId());
            return false;
        }

        // 如果修改了桌号，检查新桌号是否已存在
        if (!dto.getTableNumber().equals(table.getTableNumber())) {
            LambdaQueryWrapper<StoreTable> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(StoreTable::getStoreId, table.getStoreId())
                    .eq(StoreTable::getTableNumber, dto.getTableNumber())
                    .ne(StoreTable::getId, dto.getId());
            StoreTable existingTable = this.getOne(wrapper);
            if (existingTable != null) {
                log.warn("更新桌号失败：桌号已存在，tableNumber={}", dto.getTableNumber());
                throw new RuntimeException("桌号已存在：" + dto.getTableNumber());
            }
        }

        // 从JWT获取当前登录用户ID
        Integer userId = getCurrentUserId();

        LambdaUpdateWrapper<StoreTable> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(StoreTable::getId, dto.getId())
                .set(StoreTable::getTableNumber, dto.getTableNumber());
        if (userId != null) {
            updateWrapper.set(StoreTable::getUpdatedUserId, userId);
        }

        boolean result = this.update(updateWrapper);
        
        // 更新成功后，异步更新小程序二维码
        if (result) {
            asyncUpdateQrCodeForTable(dto.getId(), table.getStoreId());
        }
        
        return result;
    }

    /**
     * 异步更新桌号的小程序二维码
     */
    @Async
    public void asyncUpdateQrCodeForTable(Integer tableId, Integer storeId) {
        log.info("开始异步更新桌号二维码, tableId={}, storeId={}", tableId, storeId);
        try {
            String qrCodeUrl = weChatMiniProgramQrCodeService.generateTableQrCode(tableId, storeId);
            if (qrCodeUrl != null && !qrCodeUrl.isEmpty()) {
                // 更新二维码URL
                LambdaUpdateWrapper<StoreTable> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(StoreTable::getId, tableId)
                        .set(StoreTable::getQrcodeUrl, qrCodeUrl);
                this.update(updateWrapper);
                log.info("桌号二维码更新成功, tableId={}, qrCodeUrl={}", tableId, qrCodeUrl);
            }
        } catch (Exception e) {
            log.error("更新桌号二维码失败, tableId={}, error={}", tableId, e.getMessage(), e);
        }
    }

    @Override
    public boolean deleteTable(Integer id) {
        log.info("StoreTableServiceImpl.deleteTable?id={}", id);
        
        StoreTable table = this.getById(id);
        if (table == null) {
            log.warn("删除桌号失败：桌号不存在，id={}", id);
            return false;
        }

        // 如果桌号正在使用中，不允许删除
        if (table.getStatus() != null && table.getStatus() == 1) {
            log.warn("删除桌号失败：桌号正在使用中，id={}", id);
            throw new RuntimeException("桌号正在使用中，无法删除");
        }

        return this.removeById(id);
    }

    @Override
    public boolean changeTable(StoreTableChangeDTO dto) {
        log.info("StoreTableServiceImpl.changeTable?dto={}", dto);
        
        // 查询原桌号
        StoreTable fromTable = this.getById(dto.getFromTableId());
        if (fromTable == null) {
            log.warn("换桌失败：原桌号不存在，fromTableId={}", dto.getFromTableId());
            throw new RuntimeException("原桌号不存在");
        }

        // 检查原桌号的订单ID是否匹配
        if (fromTable.getCurrentOrderId() == null || !fromTable.getCurrentOrderId().equals(dto.getOrderId())) {
            log.warn("换桌失败：原桌号的订单ID不匹配，fromTableId={}, currentOrderId={}, orderId={}", 
                    dto.getFromTableId(), fromTable.getCurrentOrderId(), dto.getOrderId());
            throw new RuntimeException("原桌号的订单ID不匹配");
        }

        // 检查门店ID是否一致（先检查，避免后续不必要的操作）
        if (dto.getFromTableId().equals(dto.getToTableId())) {
            log.warn("换桌失败：原桌号和目标桌号不能相同");
            throw new RuntimeException("原桌号和目标桌号不能相同");
        }

        // 使用分布式锁锁定目标桌号，防止并发冲突
        String lockKey = "table:change:" + dto.getToTableId();
        String lockIdentifier = baseRedisService.lock(lockKey, 10000, 5000); // 锁10秒，获取锁超时5秒
        
        if (lockIdentifier == null) {
            log.warn("换桌失败：获取目标桌号锁失败，可能正在被其他操作占用，toTableId={}", dto.getToTableId());
            throw new RuntimeException("目标桌号正在被占用，请稍后重试");
        }

        try {
            // 重新查询目标桌号（在锁内查询，确保获取最新状态）
            StoreTable toTable = this.getById(dto.getToTableId());
            if (toTable == null) {
                log.warn("换桌失败：目标桌号不存在，toTableId={}", dto.getToTableId());
                throw new RuntimeException("目标桌号不存在");
            }

            // 检查门店ID是否一致
            if (!fromTable.getStoreId().equals(toTable.getStoreId())) {
                log.warn("换桌失败：桌号不在同一门店，fromTableId={}, toTableId={}", dto.getFromTableId(), dto.getToTableId());
                throw new RuntimeException("桌号不在同一门店");
            }

            // 从JWT获取当前登录用户ID
            Integer userId = getCurrentUserId();

            // 更新原桌号：状态为空闲，清空订单ID
            LambdaUpdateWrapper<StoreTable> fromUpdateWrapper = new LambdaUpdateWrapper<>();
            fromUpdateWrapper.eq(StoreTable::getId, dto.getFromTableId())
                    .eq(StoreTable::getCurrentOrderId, dto.getOrderId()) // 确保订单ID匹配
                    .set(StoreTable::getStatus, 0)
                    .set(StoreTable::getCurrentOrderId, null);
            boolean fromUpdateResult = this.update(fromUpdateWrapper);
            
            if (!fromUpdateResult) {
                log.warn("换桌失败：更新原桌号失败，可能订单状态已变化，fromTableId={}, orderId={}", 
                        dto.getFromTableId(), dto.getOrderId());
                throw new RuntimeException("原桌号的订单状态已变化，请刷新后重试");
            }

            // 使用条件更新目标桌号：只有在空闲状态且无订单时才能更新
            // 这样可以确保即使有其他用户扫码，也能保证原子性
            LambdaUpdateWrapper<StoreTable> toUpdateWrapper = new LambdaUpdateWrapper<>();
            toUpdateWrapper.eq(StoreTable::getId, dto.getToTableId())
                    .eq(StoreTable::getStatus, 0) // 必须是空闲状态
                    .isNull(StoreTable::getCurrentOrderId) // 必须没有订单
                    .set(StoreTable::getStatus, 1)
                    .set(StoreTable::getCurrentOrderId, dto.getOrderId());
            
            boolean toUpdateResult = this.update(toUpdateWrapper);
            
            if (!toUpdateResult) {
                log.warn("换桌失败：目标桌号已被占用（可能被其他用户扫码），toTableId={}", dto.getToTableId());
                // 回滚原桌号的更新
                LambdaUpdateWrapper<StoreTable> rollbackWrapper = new LambdaUpdateWrapper<>();
                rollbackWrapper.eq(StoreTable::getId, dto.getFromTableId())
                        .set(StoreTable::getStatus, 1)
                        .set(StoreTable::getCurrentOrderId, dto.getOrderId());
                this.update(rollbackWrapper);
                throw new RuntimeException("目标桌号已被占用，请选择其他桌号");
            }

            // 创建换桌记录（只有在成功更新后才记录）
            StoreTableLog tableLog = new StoreTableLog();
            tableLog.setStoreId(fromTable.getStoreId());
            tableLog.setOrderId(dto.getOrderId());
            tableLog.setFromTableId(dto.getFromTableId());
            tableLog.setFromTableNumber(fromTable.getTableNumber());
            tableLog.setToTableId(dto.getToTableId());
            tableLog.setToTableNumber(toTable.getTableNumber());
            tableLog.setChangeReason(dto.getChangeReason());
            tableLog.setCreatedUserId(userId);
            storeTableLogMapper.insert(tableLog);

            log.info("换桌成功：订单{}从桌号{}换到桌号{}", dto.getOrderId(), dto.getFromTableId(), dto.getToTableId());
            return true;
            
        } finally {
            // 释放分布式锁
            baseRedisService.unlock(lockKey, lockIdentifier);
        }
    }

    @Override
    public List<StoreTableStatusVO> batchQueryTableStatus(List<Integer> tableIds) {
        log.info("StoreTableServiceImpl.batchQueryTableStatus?tableIds={}", tableIds);
        
        if (tableIds == null || tableIds.isEmpty()) {
            log.warn("批量查询桌号状态失败：桌号ID列表为空");
            return Collections.emptyList();
        }
        
        LambdaQueryWrapper<StoreTable> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(StoreTable::getId, tableIds)
                .select(StoreTable::getId, StoreTable::getStatus);
        List<StoreTable> tables = this.list(wrapper);
        
        // 转换为VO
        return tables.stream()
                .map(table -> {
                    StoreTableStatusVO vo = new StoreTableStatusVO();
                    vo.setId(table.getId());
                    vo.setStatus(table.getStatus());
                    return vo;
                })
                .collect(Collectors.toList());
    }

    /**
     * 从JWT获取当前登录用户ID
     *
     * @return 用户ID，如果未登录返回null
     */
    private Integer getCurrentUserId() {
        try {
            JSONObject userInfo = JwtUtil.getCurrentUserInfo();
            if (userInfo != null) {
                return userInfo.getInteger("userId");
            }
        } catch (Exception e) {
            log.warn("获取当前登录用户ID失败: {}", e.getMessage());
        }
        return null;
    }
}
