package com.czg.service.account.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import com.czg.account.dto.table.ShopTableAddDTO;
import com.czg.account.dto.table.ShopTableBindDTO;
import com.czg.account.dto.table.ShopTableClearDTO;
import com.czg.account.dto.table.ShopTableDTO;
import com.czg.account.entity.ShopTable;
import com.czg.account.entity.ShopTableArea;
import com.czg.account.entity.ShopTableCode;
import com.czg.account.service.ShopTableAreaService;
import com.czg.account.service.ShopTableCodeService;
import com.czg.account.service.ShopTableService;
import com.czg.account.vo.ShopTableVO;
import com.czg.constants.ParamCodeCst;
import com.czg.enums.ShopTableStatusEnum;
import com.czg.exception.CzgException;
import com.czg.sa.StpKit;
import com.czg.service.account.mapper.ShopTableMapper;
import com.czg.system.service.SysParamsService;
import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 台桌配置 服务层实现。
 *
 * @author zs
 * @since 2025-02-13
 */
@Slf4j
@DubboService
public class ShopTableServiceImpl extends ServiceImpl<ShopTableMapper, ShopTable> implements ShopTableService {
    @Resource
    private ShopTableAreaService shopAreaService;
    @Resource
    private ShopTableCodeService shopTableCodeService;
    @DubboReference
    private SysParamsService sysParamsService;

    @PostConstruct
    public void init() {
        updateChain()
                .set(ShopTable::getClearTime, null)
                .set(ShopTable::getStatus, ShopTableStatusEnum.IDLE.getValue())
                .isNotNull(ShopTable::getTableCode)
                .isNotNull(ShopTable::getClearTime)
                .lt(ShopTable::getClearTime, LocalDateTime.now())
                .update();
    }


    @Override
    public Boolean add(Long shopId, ShopTableAddDTO shopTableAddDTO) {
        ShopTableArea shopArea = shopAreaService.getOne(new QueryWrapper().eq(ShopTableArea::getShopId, shopId)
                .eq(ShopTableArea::getId, shopTableAddDTO.getAreaId()));
        if (shopArea == null) {
            throw new CzgException("台桌区域不存在");
        }

        List<String> existisList = queryChain().eq(ShopTable::getShopId, shopId).like(ShopTable::getName, shopTableAddDTO.getSign()).list().stream().map(ShopTable::getName).toList();
        StringBuilder msg = new StringBuilder();
        ArrayList<ShopTable> tableArrayList = new ArrayList<>();
        for (int i = shopTableAddDTO.getStart(); i <= shopTableAddDTO.getEnd(); i++) {
            String name = shopTableAddDTO.getSign() + i;
            if (!existisList.isEmpty() && existisList.contains(name)) {
                msg.append(name).append(";");
                continue;
            }
            ShopTable shopTable = new ShopTable();
            shopTable.setShopId(shopId);
            shopTable.setName(name);
            shopTable.setMaxCapacity(shopTableAddDTO.getMaxCapacity());
            shopTable.setAreaId(shopTableAddDTO.getAreaId());
            shopTable.setStatus(ShopTableStatusEnum.UNBOUND.getValue());
            tableArrayList.add(shopTable);
        }

        mapper.insertBatchSelective(tableArrayList, 50);
        if (StrUtil.isNotBlank(msg.toString())) {
            msg.append("台桌名已存在，未添加");
            throw new CzgException(msg.toString());
        }
        return true;
    }

    @Override
    public ShopTable getOneByTableCode(Long shopId, String tableCode) {
        return queryChain().eq(ShopTable::getShopId, shopId).eq(ShopTable::getTableCode, tableCode).one();
    }

    @Override
    public String getTableAreaAndName(Long shopId, String tableCode, String tableName) {
        ShopTable shopTable = getOneByTableCode(shopId, tableCode);
        if (shopTable == null) {
            return tableName;
        } else {
            ShopTableArea shopArea = shopAreaService.getOne(new QueryWrapper().eq(ShopTableArea::getShopId, shopId)
                    .eq(ShopTableArea::getId, shopTable.getAreaId()));
            if (shopArea == null) {
                return tableName;
            } else {
                return shopArea.getName() + "-" + shopTable.getName();
            }
        }
    }

    public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) {
        response.setCharacterEncoding(request.getCharacterEncoding());
        response.setContentType("application/octet-stream");
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
            IoUtil.copy(fis, response.getOutputStream());
            response.flushBuffer();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                    if (deleteOnExit) {
                        if (file.exists()) {
                            boolean delete = file.delete();
                            if (delete) {
                                log.info("删除临时qrcode文件成功");
                            }
                        }
                    }
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
    }

    @Override
    public void createQrCode(Long shopId, Integer num, HttpServletResponse response, HttpServletRequest request) throws IOException {
        // 查询 shop 下面有多少台桌
        long count = queryChain().eq(ShopTable::getShopId, shopId).count();
        if (count == 0) {
            throw new CzgException("未添加台桌，请先添加台桌");
        }

        // 获取当前店铺下的最大ID，确保只查询当前shopId的记录
        List<ShopTableCode> records = shopTableCodeService.queryChain()
                .eq(ShopTableCode::getShopId, shopId)
                .orderBy(ShopTableCode::getId, false)
                .page(new Page<>(1, 1))
                .getRecords();
        long maxId = records.isEmpty() ? 0 : records.getFirst().getId();

        // 获取当前店铺下所有已存在的tableCode，避免重复
        Map<String, Long> codeMap = shopTableCodeService.queryChain()
                .eq(ShopTableCode::getShopId, shopId)
                .select(ShopTableCode::getTableCode, ShopTableCode::getId)
                .list()
                .stream()
                .collect(Collectors.toMap(ShopTableCode::getTableCode, ShopTableCode::getId));

        // 临时目录路径
        String tempDir = "/usr/local/temp/qrcode/%d".formatted(shopId);
        File dir = new File(tempDir);
        if (!dir.exists()) {
            if (!dir.mkdirs()) {
                log.error("创建临时目录失败");
                throw new CzgException("生成失败，请稍后再试");
            }
        }

        // 预生成所有二维码数据和记录，避免在写入ZIP过程中出现异常
        List<ShopTableCode> codeList = new ArrayList<>();

        for (int i = 0; i < num; i++) {
            String tableCode = generateCode(1, shopId, ++maxId, codeMap);
            codeMap.put(tableCode, maxId);

            codeList.add(new ShopTableCode()
                    .setShopId(shopId)
                    .setTableCode(tableCode));

            // 图片保存到临时目录
            File qrFile = new File(dir, tableCode + ".png");

            // 生成二维码图片
            QrCodeUtil.generate(sysParamsService.getSysParamValue(ParamCodeCst.System.TABLE_CODE_URL) + tableCode, 300, 300, qrFile);
        }

        // 先保存数据库记录，确保生成ZIP前无异常
        shopTableCodeService.saveBatch(codeList);

        // 设置响应头
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=shop_qrcodes_" + shopId + ".zip");
        response.setCharacterEncoding(request.getCharacterEncoding());

        // 使用 Hutool 创建临时 ZIP 文件
        String tempZipPath = "%s.zip".formatted(tempDir);
        ZipUtil.zip(tempDir, tempZipPath);

        downloadFile(request, response, new File(tempZipPath), true);

        log.info("桌码生成成功");

        if (dir.exists()) {
            for (File file : Objects.requireNonNull(dir.listFiles())) {
                if (file.isFile()) {
                    if (!file.delete()) {
                        log.info("删除临时qrcode文件失败");
                    }
                }
            }
        }
    }


    private String generateCode(Integer count, Long shopId, Long id, Map<String, Long> map) {
        if (count > 100) {
            throw new CzgException("桌码生成失败");
        }
        String tableCode = "40" + shopId % 10 + id % 10 + RandomUtil.randomNumbers(7);
        if (map.containsKey(tableCode)) {
            generateCode(++count, shopId, id + 1, map);
        }
        return tableCode;

    }

    @Override
    public Boolean bind(Long shopId, ShopTableBindDTO shopTableBindDTO) {
        if (!StrUtil.startWith(shopTableBindDTO.getTableCode(), "40")) {
            throw new CzgException("错误码");
        }

        ShopTableCode tableCode = shopTableCodeService.queryChain().eq(ShopTableCode::getTableCode, shopTableBindDTO.getTableCode())
                .eq(ShopTableCode::getShopId, shopId).one();
        if (tableCode == null) {
            throw new CzgException("此码非本店铺生成");
        }

        if (tableCode.getState() == 1) {
            throw new CzgException("此桌码已绑定，请勿重复绑定");
        }

        ShopTable shopTable = queryChain().eq(ShopTable::getShopId, shopId).eq(ShopTable::getId, shopTableBindDTO.getId()).one();
        if (shopTable == null) {
            throw new CzgException("台桌不存在");
        }

        tableCode.setBindTime(DateUtil.date().toLocalDateTime());
        tableCode.setState(1);
        shopTableCodeService.updateById(tableCode);

        shopTable.setTableCode(tableCode.getTableCode());
        shopTable.setStatus(ShopTableStatusEnum.IDLE.getValue());
        return updateById(shopTable);
    }

    @Override
    public Boolean updateInfo(long shopId, ShopTableDTO shopTableDTO) {
        ShopTable shopTable = BeanUtil.copyProperties(shopTableDTO, ShopTable.class);
        ShopTable shopTableSource = getOne(new QueryWrapper().eq(ShopTable::getShopId, shopId).eq(ShopTable::getId, shopTableDTO.getId()));
        if (StrUtil.isBlank(shopTableSource.getTableCode()) && StrUtil.isNotBlank(shopTable.getStatus()) && ShopTableStatusEnum.UNBOUND.equalsVal(shopTable.getStatus())) {
            throw new CzgException("修改失败，该台桌未绑定桌码，绑定桌码后自动进入空闲状态");
        }
        return update(shopTable, new QueryWrapper().eq(ShopTable::getShopId, StpKit.USER.getShopId()).eq(ShopTable::getId, shopTableDTO.getId()));
    }

    @Override
    public Boolean updateStatus(long shopId, Long tableId, String tableCode, String status) {
        ShopTable shopTable = new ShopTable();
        shopTable.setStatus(status);
        return update(shopTable, query().eq(ShopTable::getShopId, shopId).eq(ShopTable::getId, tableId).eq(ShopTable::getTableCode, tableCode));
    }

    @Override
    public Boolean clear(long shopId, ShopTableClearDTO shopTableClearDTO) {
        ShopTable shopTable = getOne(new QueryWrapper().eq(ShopTable::getShopId, shopId).eq(ShopTable::getId, shopTableClearDTO.getId()));
        if (!ShopTableStatusEnum.SETTLED.equalsVal(shopTable.getStatus())) {
            throw new CzgException("台桌不处于待清台状态");
        }
        shopTable.setStatus(ShopTableStatusEnum.IDLE.getValue());
        shopTable.setEndTime(DateUtil.date().toLocalDateTime());
        shopTable.setProductNum(0);
        shopTable.setTotalAmount(BigDecimal.ZERO);
        shopTable.setRealAmount(BigDecimal.ZERO);
        shopTable.setUseNum(0);
        return update(shopTable, new QueryWrapper().eq(ShopTable::getShopId, shopId)
                .eq(ShopTable::getId, shopTableClearDTO.getId()));
    }

    @Override
    public Boolean expiredTable(long tableId) {
        ShopTable shopTable = new ShopTable();
        shopTable.setClearTime(null);
        shopTable.setStatus(ShopTableStatusEnum.IDLE.getValue());
        return update(shopTable, QueryWrapper.create().eq(ShopTable::getId, tableId));
    }

    @Override
    public Page<ShopTableVO> pageInfo(Long shopId, Integer areaId, String tableCode, String status, String name, Boolean isBind) {
        PageHelper.startPage(PageUtil.buildPageHelp());
        List<ShopTableVO> shopTableVOS = mapper.pageInfo(shopId, areaId, tableCode, status, name, isBind);
        for (ShopTableVO shopTableVO : shopTableVOS) {
            shopTableVO.setStatusMsg(ShopTableStatusEnum.getMsgByValue(shopTableVO.getStatus()));
        }
        return PageUtil.convert(new PageInfo<>(shopTableVOS));
    }
}
