package com.kexue.skills.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.kexue.skills.common.util.ExportUtils;
import com.kexue.skills.entity.ExchangeCode;
import com.kexue.skills.entity.PackageConfig;
import com.kexue.skills.entity.dto.ExchangeCodeQueryDto;
import com.kexue.skills.entity.dto.ExchangeRequestDto;
import com.kexue.skills.mapper.ExchangeCodeMapper;
import com.kexue.skills.service.AccountService;
import com.kexue.skills.service.ExchangeCodeService;
import com.kexue.skills.service.PackageConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;

/**
 * (ExchangeCode)表服务实现类
 */
@Service("exchangeCodeService")
@Transactional(rollbackFor = Exception.class)
public class ExchangeCodeServiceImpl implements ExchangeCodeService {

    private static final Logger log = LoggerFactory.getLogger(ExchangeCodeServiceImpl.class);

    @Resource
    private ExchangeCodeMapper exchangeCodeMapper;

    @Resource
    private PackageConfigService packageConfigService;

    @Resource
    private AccountService accountService;

    private static final BigDecimal PRICE_99 = new BigDecimal("9.9");
    private static final BigDecimal PRICE_499 = new BigDecimal("49.9");
    private static final BigDecimal PRICE_2999 = new BigDecimal("299.9");

    @Override
    public PageInfo<ExchangeCode> getPageList(ExchangeCodeQueryDto queryDto) {
        int pageNum = queryDto.getPageNum() != null ? queryDto.getPageNum() : 1;
        int pageSize = queryDto.getPageSize() != null ? queryDto.getPageSize() : 20;
        PageHelper.startPage(pageNum, pageSize);
        List<ExchangeCode> list = exchangeCodeMapper.getPageList(buildQueryEntity(queryDto));
        return new PageInfo<>(list);
    }

    @Override
    public List<ExchangeCode> getList(ExchangeCodeQueryDto queryDto) {
        return exchangeCodeMapper.getList(buildQueryEntity(queryDto));
    }

    @Override
    public ExchangeCode queryById(Long id) {
        return exchangeCodeMapper.queryById(id);
    }

    @Override
    public ExchangeCode queryByCode(String code) {
        return exchangeCodeMapper.queryByCode(code);
    }

    @Override
    public ExchangeCode insert(ExchangeCode exchangeCode) {
        Date now = new Date();
        exchangeCode.setCreateTime(now);
        exchangeCode.setUpdateTime(now);
        exchangeCode.setDeleteFlag(0);
        if (exchangeCode.getStatus() == null) {
            exchangeCode.setStatus(0);
        }
        exchangeCodeMapper.insert(exchangeCode);
        return exchangeCode;
    }

    @Override
    public ExchangeCode update(ExchangeCode exchangeCode) {
        exchangeCode.setUpdateTime(new Date());
        exchangeCodeMapper.update(exchangeCode);
        return queryById(exchangeCode.getId());
    }

    @Override
    public int deleteById(Long id) {
        return exchangeCodeMapper.deleteById(id);
    }

    @Override
    public int logicDeleteById(Long id, String updateBy) {
        return exchangeCodeMapper.logicDeleteById(id, updateBy);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> exchange(ExchangeRequestDto requestDto) {
        String code = requestDto.getCode();
        Long userId = requestDto.getUserId();

        log.info("[兑换码兑换] 开始处理兑换请求, code={}, userId={}", code, userId);

        Map<String, Object> result = new HashMap<>();

        ExchangeCode exchangeCode = exchangeCodeMapper.queryByCode(code);
        if (exchangeCode == null) {
            log.info("[兑换码兑换] 兑换码不存在, code={}", code);
            result.put("success", false);
            result.put("message", "无效的兑换码");
            return result;
        }

        if (exchangeCode.getStatus() == 1) {
            log.info("[兑换码兑换] 兑换码已被使用, code={}, usedUserId={}", code, exchangeCode.getUsedUserId());
            result.put("success", false);
            result.put("message", "兑换码已被使用");
            return result;
        }

        if (exchangeCode.getStatus() == 2) {
            log.info("[兑换码兑换] 兑换码已过期, code={}", code);
            result.put("success", false);
            result.put("message", "兑换码已过期");
            return result;
        }

        if (exchangeCode.getExpireTime() != null && exchangeCode.getExpireTime().before(new Date())) {
            exchangeCode.setStatus(2);
            exchangeCodeMapper.update(exchangeCode);
            log.info("[兑换码兑换] 兑换码过期时间已到, code={}, expireTime={}", code, exchangeCode.getExpireTime());
            result.put("success", false);
            result.put("message", "兑换码已过期");
            return result;
        }

        PackageConfig packageConfig = findPackageByPrice(exchangeCode.getPackagePrice());
        if (packageConfig == null) {
            log.info("[兑换码兑换] 未找到对应套餐, code={}, packagePrice={}", code, exchangeCode.getPackagePrice());
            result.put("success", false);
            result.put("message", "未找到对应的套餐配置");
            return result;
        }

        log.info("[兑换码兑换] 找到套餐, code={}, packageId={}, packageName={}, price={}", 
                code, packageConfig.getId(), packageConfig.getName(), packageConfig.getPrice());

        String transactionNo = generateTransactionNo();
        int rechargeResult = accountService.addBalance(
                userId,
                packageConfig.getPrice(),
                false,
                transactionNo,
                packageConfig.getId(),
                "exchange_code",
                "兑换码兑换：" + packageConfig.getName()
        );

        if (rechargeResult > 0) {
            exchangeCodeMapper.useCode(exchangeCode.getId(), userId);
            BigDecimal totalAmount = packageConfig.getBaseAmount().add(packageConfig.getGiftAmount());
            log.info("[兑换码兑换] 兑换成功, code={}, userId={}, packageId={}, packageName={}, baseAmount={}, giftAmount={}, totalAmount={}", 
                    code, userId, packageConfig.getId(), packageConfig.getName(), 
                    packageConfig.getBaseAmount(), packageConfig.getGiftAmount(), totalAmount);
            result.put("success", true);
            result.put("message", "兑换成功");
            result.put("packageName", packageConfig.getName());
            result.put("packagePrice", packageConfig.getPrice());
            result.put("baseAmount", packageConfig.getBaseAmount());
            result.put("giftAmount", packageConfig.getGiftAmount());
            result.put("totalAmount", totalAmount);
        } else {
            log.info("[兑换码兑换] 兑换失败, 充值未成功, code={}, userId={}", code, userId);
            result.put("success", false);
            result.put("message", "兑换失败，充值未成功");
        }

        return result;
    }

    @Override
    public Map<String, Object> importExcel(MultipartFile file) {
        log.info("[兑换码导入] 开始导入Excel文件, fileName={}, size={}bytes", file.getOriginalFilename(), file.getSize());

        Map<String, Object> result = new HashMap<>();
        int successCount = 0;
        int failCount = 0;
        List<String> failMessages = new ArrayList<>();

        try (ExcelReader reader = ExcelUtil.getReader(file.getInputStream())) {
            List<String> sheetNames = reader.getSheetNames();
            log.info("[兑换码导入] Excel文件包含sheet: {}", sheetNames);

            for (String sheetName : sheetNames) {
                reader.setSheet(sheetName);
                BigDecimal packagePrice = parsePriceFromSheetName(sheetName);

                if (packagePrice == null) {
                    log.info("[兑换码导入] 无法识别sheet名称, sheetName={}", sheetName);
                    failMessages.add("无法识别sheet名称: " + sheetName);
                    continue;
                }

                PackageConfig packageConfig = findPackageByPrice(packagePrice);
                if (packageConfig == null) {
                    log.info("[兑换码导入] 未找到对应价格的套餐, sheetName={}, packagePrice={}", sheetName, packagePrice);
                    failMessages.add("未找到对应价格的套餐: " + packagePrice);
                    continue;
                }

                log.info("[兑换码导入] 处理sheet, sheetName={}, packagePrice={}, packageId={}, packageName={}", 
                        sheetName, packagePrice, packageConfig.getId(), packageConfig.getName());

                List<List<Object>> rows = reader.read();
                if (rows == null || rows.isEmpty()) {
                    log.info("[兑换码导入] sheet为空, sheetName={}", sheetName);
                    continue;
                }

                List<ExchangeCode> codeList = new ArrayList<>();
                Date now = new Date();

                for (int i = 0; i < rows.size(); i++) {
                    List<Object> row = rows.get(i);
                    if (row == null || row.isEmpty()) {
                        continue;
                    }

                    if (row.size() < 2) {
                        continue;
                    }

                    Object indexObj = row.get(0);
                    Object codeObj = row.get(1);

                    if (indexObj == null && codeObj == null) {
                        continue;
                    }

                    if (codeObj == null) {
                        continue;
                    }

                    String code = String.valueOf(codeObj).trim();
                    if (code.isEmpty()) {
                        continue;
                    }

                    ExchangeCode existCode = exchangeCodeMapper.queryByCode(code);
                    if (existCode != null) {
                        failCount++;
                        failMessages.add("兑换码已存在: " + code);
                        continue;
                    }

                    ExchangeCode exchangeCode = new ExchangeCode();
                    exchangeCode.setCode(code);
                    exchangeCode.setPackagePrice(packagePrice);
                    exchangeCode.setPackageId(packageConfig.getId());
                    exchangeCode.setStatus(0);
                    exchangeCode.setCreateTime(now);
                    exchangeCode.setUpdateTime(now);
                    exchangeCode.setDeleteFlag(0);
                    codeList.add(exchangeCode);
                }

                if (!codeList.isEmpty()) {
                    exchangeCodeMapper.batchInsert(codeList);
                    successCount += codeList.size();
                    log.info("[兑换码导入] sheet处理完成, sheetName={}, 成功导入{}条", sheetName, codeList.size());
                }
            }
        } catch (Exception e) {
            log.error("[兑换码导入] 导入Excel失败", e);
            result.put("success", false);
            result.put("message", "导入失败: " + e.getMessage());
            return result;
        }

        log.info("[兑换码导入] 导入完成, 成功={}, 失败={}", successCount, failCount);
        result.put("success", true);
        result.put("successCount", successCount);
        result.put("failCount", failCount);
        result.put("failMessages", failMessages);
        return result;
    }

    @Override
    public void exportExcel(ExchangeCodeQueryDto queryDto, HttpServletResponse response) throws Exception {
        List<ExchangeCode> list = exchangeCodeMapper.getList(buildQueryEntity(queryDto));
        String fileName = "exchange_codes_" + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
        ExportUtils.webExport(list, ExchangeCode.class, fileName, response);
    }

    @Override
    public Map<String, Integer> getStatistics() {
        Map<String, Integer> statistics = new HashMap<>();
        statistics.put("total", 0);
        statistics.put("unused", 0);
        statistics.put("used", 0);
        statistics.put("expired", 0);

        List<ExchangeCode> list = exchangeCodeMapper.getList(new ExchangeCode());
        for (ExchangeCode code : list) {
            statistics.put("total", statistics.get("total") + 1);
            if (code.getStatus() == 0) {
                statistics.put("unused", statistics.get("unused") + 1);
            } else if (code.getStatus() == 1) {
                statistics.put("used", statistics.get("used") + 1);
            } else if (code.getStatus() == 2) {
                statistics.put("expired", statistics.get("expired") + 1);
            }
        }

        return statistics;
    }

    @Override
    public int countAvailableByPrice(BigDecimal packagePrice) {
        return exchangeCodeMapper.countAvailableByPrice(packagePrice);
    }

    private ExchangeCode buildQueryEntity(ExchangeCodeQueryDto queryDto) {
        if (queryDto == null) {
            return new ExchangeCode();
        }
        ExchangeCode entity = new ExchangeCode();
        entity.setCode(queryDto.getCode());
        entity.setPackagePrice(queryDto.getPackagePrice());
        entity.setStatus(queryDto.getStatus());
        return entity;
    }

    private PackageConfig findPackageByPrice(BigDecimal price) {
        if (price == null) {
            return null;
        }
        List<PackageConfig> packages = packageConfigService.getList(null);
        for (PackageConfig pkg : packages) {
            System.out.println("pkg.price=" + pkg.getPrice());
            if (pkg.getPrice().compareTo(price) == 0) {
                return pkg;
            }
        }
        return null;
    }

    private BigDecimal parsePriceFromSheetName(String sheetName) {
        if (sheetName == null || sheetName.trim().isEmpty()) {
            return null;
        }

        String trimmed = sheetName.trim();
        if (trimmed.contains("299.9") || trimmed.equals("299.9")) {
            return PRICE_2999;
        } else if (trimmed.contains("49.9") || trimmed.equals("49.9")) {
            return PRICE_499;
        } else if (trimmed.contains("9.9") || trimmed.equals("9.9")) {
            return PRICE_99;
        }

        try {
            return new BigDecimal(trimmed);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    private String generateTransactionNo() {
        return "EXC" + System.currentTimeMillis() + String.format("%04d", new Random().nextInt(10000));
    }
}