package com.usky.vpp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.usky.common.core.bean.CommonPage;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.security.utils.SecurityUtils;
import com.usky.vpp.domain.VppCustomer;
import com.usky.vpp.domain.VppEnergyReadingMonthly;
import com.usky.vpp.domain.VppSettlementBill;
import com.usky.vpp.domain.VppSite;
import com.usky.vpp.mapper.VppCustomerMapper;
import com.usky.vpp.mapper.VppEnergyReadingMonthlyMapper;
import com.usky.vpp.mapper.VppSettlementBillMapper;
import com.usky.vpp.mapper.VppSiteMapper;
import com.usky.vpp.service.VppSettlementService;
import com.usky.vpp.util.VppAuditHelper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * VppSettlementService 默认实现
 */
@Service
public class VppSettlementServiceImpl implements VppSettlementService {

    @Autowired
    private VppCustomerMapper vppCustomerMapper;
    @Autowired
    private VppEnergyReadingMonthlyMapper energyReadingMonthlyMapper;
    @Autowired
    private VppSiteMapper siteMapper;
    @Autowired
    private VppSettlementBillMapper settlementBillMapper;

    @Override
    public CommonPage<VppEnergyReadingMonthly> pageReading(Long id, String customerName, String calcCycle,
                                                           Integer current, Integer size) {
        IPage<VppEnergyReadingMonthly> page = new Page<>(current, size);
        LambdaQueryWrapper<VppEnergyReadingMonthly> wrapper = buildReadingQueryWrapper(id, customerName, calcCycle);
        if (wrapper == null) {
            return new CommonPage<>(Collections.emptyList(), 0L, size, current);
        }
        page = energyReadingMonthlyMapper.selectPage(page, wrapper);
        List<VppEnergyReadingMonthly> records = page.getRecords();
        fillCustomerNames(records);
        fillSiteNames(records);
        return new CommonPage<>(records, page.getTotal(), size, current);
    }

    @Override
    public void exportReading(String scope, String fileName, Long id, String customerName, String calcCycle,
                              Integer current, Integer size, HttpServletResponse response) {
        List<VppEnergyReadingMonthly> readingList;

        switch (scope) {
            case "current":
                if (current == null || size == null) {
                    throw new BusinessException("current 模式下分页参数 current 和 size 不能为空！");
                }
                IPage<VppEnergyReadingMonthly> page = new Page<>(current, size);
                LambdaQueryWrapper<VppEnergyReadingMonthly> currWrapper = baseReadingQueryWrapper();
                currWrapper.orderByDesc(VppEnergyReadingMonthly::getSettleYear)
                        .orderByDesc(VppEnergyReadingMonthly::getSettleMonth);
                page = energyReadingMonthlyMapper.selectPage(page, currWrapper);
                readingList = page.getRecords();
                break;

            case "all":
                LambdaQueryWrapper<VppEnergyReadingMonthly> allWrapper = baseReadingQueryWrapper();
                allWrapper.orderByDesc(VppEnergyReadingMonthly::getSettleYear)
                        .orderByDesc(VppEnergyReadingMonthly::getSettleMonth);
                readingList = energyReadingMonthlyMapper.selectList(allWrapper);
                break;

            case "filtered":
                boolean hasFilter = id != null || StringUtils.isNotBlank(customerName)
                        || StringUtils.isNotBlank(calcCycle);
                if (!hasFilter) {
                    throw new BusinessException("filtered 模式下请至少提供一个筛选条件（id / customerName / calcCycle）！");
                }
                LambdaQueryWrapper<VppEnergyReadingMonthly> filteredWrapper = buildReadingQueryWrapper(
                        id, customerName, calcCycle);
                if (filteredWrapper == null) {
                    readingList = Collections.emptyList();
                    break;
                }
                readingList = energyReadingMonthlyMapper.selectList(filteredWrapper);
                break;

            default:
                throw new BusinessException("不支持的导出范围：" + scope + "，请使用 current / all / filtered");
        }

        if (readingList == null) {
            readingList = Collections.emptyList();
        }
        fillCustomerNames(readingList);
        fillSiteNames(readingList);
        writeReadingExcel(fileName, readingList, response);
    }

    @Override
    public CommonPage<VppSettlementBill> pageBill(Long id, String customerName, Integer current, Integer size) {
        IPage<VppSettlementBill> page = new Page<>(current, size);
        LambdaQueryWrapper<VppSettlementBill> wrapper = buildBillQueryWrapper(id, customerName);
        if (wrapper == null) {
            return new CommonPage<>(Collections.emptyList(), 0L, size, current);
        }
        page = settlementBillMapper.selectPage(page, wrapper);
        List<VppSettlementBill> records = page.getRecords();
        fillBillCustomerNames(records);
        fillBillSiteNames(records);
        fillBillContractPrice(records);
        return new CommonPage<>(records, page.getTotal(), size, current);
    }

    @Override
    public void exportBill(String scope, String fileName, Long id, String customerName,
                           Integer current, Integer size, HttpServletResponse response) {
        List<VppSettlementBill> billList;

        switch (scope) {
            case "current":
                if (current == null || size == null) {
                    throw new BusinessException("current 模式下分页参数 current 和 size 不能为空！");
                }
                IPage<VppSettlementBill> page = new Page<>(current, size);
                LambdaQueryWrapper<VppSettlementBill> currWrapper = baseBillQueryWrapper();
                currWrapper.orderByDesc(VppSettlementBill::getSettleYear)
                        .orderByDesc(VppSettlementBill::getSettleMonth);
                page = settlementBillMapper.selectPage(page, currWrapper);
                billList = page.getRecords();
                break;

            case "all":
                LambdaQueryWrapper<VppSettlementBill> allWrapper = baseBillQueryWrapper();
                allWrapper.orderByDesc(VppSettlementBill::getSettleYear)
                        .orderByDesc(VppSettlementBill::getSettleMonth);
                billList = settlementBillMapper.selectList(allWrapper);
                break;

            case "filtered":
                boolean hasFilter = id != null || StringUtils.isNotBlank(customerName);
                if (!hasFilter) {
                    throw new BusinessException("filtered 模式下请至少提供一个筛选条件（id / customerName）！");
                }
                LambdaQueryWrapper<VppSettlementBill> filteredWrapper = buildBillQueryWrapper(id, customerName);
                if (filteredWrapper == null) {
                    billList = Collections.emptyList();
                    break;
                }
                billList = settlementBillMapper.selectList(filteredWrapper);
                break;

            default:
                throw new BusinessException("不支持的导出范围：" + scope + "，请使用 current / all / filtered");
        }

        if (billList == null) {
            billList = Collections.emptyList();
        }
        fillBillCustomerNames(billList);
        fillBillSiteNames(billList);
        fillBillContractPrice(billList);
        writeBillExcel(fileName, billList, response);
    }

    private LambdaQueryWrapper<VppEnergyReadingMonthly> baseReadingQueryWrapper() {
        return new LambdaQueryWrapper<VppEnergyReadingMonthly>()
                .eq(VppEnergyReadingMonthly::getTenantId, SecurityUtils.getTenantId())
                .eq(VppEnergyReadingMonthly::getDeleteFlag, VppAuditHelper.NOT_DELETED);
    }

    private LambdaQueryWrapper<VppEnergyReadingMonthly> buildReadingQueryWrapper(Long customerId,
                                                                                 String customerName,
                                                                                 String calcCycle) {
        Map<Long, String> customerNameMap = resolveCustomerNameMap(customerName);
        if (StringUtils.isNotBlank(customerName) && customerNameMap.isEmpty()) {
            return null;
        }

        Map<String, Object> cycleMap = null;
        if (StringUtils.isNotBlank(calcCycle)) {
            cycleMap = resolveCalcCycle(calcCycle);
        }

        return baseReadingQueryWrapper()
                .eq(customerId != null, VppEnergyReadingMonthly::getCustomerId, customerId)
                .in(StringUtils.isNotBlank(customerName), VppEnergyReadingMonthly::getCustomerId, customerNameMap.keySet())
                .eq(cycleMap != null, VppEnergyReadingMonthly::getSettleYear,
                        cycleMap != null ? cycleMap.get("settleYear") : null)
                .eq(cycleMap != null, VppEnergyReadingMonthly::getSettleMonth,
                        cycleMap != null ? cycleMap.get("settleMonth") : null)
                .orderByDesc(VppEnergyReadingMonthly::getSettleYear)
                .orderByDesc(VppEnergyReadingMonthly::getSettleMonth);
    }

    private LambdaQueryWrapper<VppSettlementBill> baseBillQueryWrapper() {
        return new LambdaQueryWrapper<VppSettlementBill>()
                .eq(VppSettlementBill::getTenantId, SecurityUtils.getTenantId())
                .eq(VppSettlementBill::getDeleteFlag, VppAuditHelper.NOT_DELETED);
    }

    private LambdaQueryWrapper<VppSettlementBill> buildBillQueryWrapper(Long customerId, String customerName) {
        Map<Long, String> customerNameMap = resolveCustomerNameMap(customerName);
        if (StringUtils.isNotBlank(customerName) && customerNameMap.isEmpty()) {
            return null;
        }

        return baseBillQueryWrapper()
                .eq(customerId != null, VppSettlementBill::getCustomerId, customerId)
                .in(StringUtils.isNotBlank(customerName), VppSettlementBill::getCustomerId, customerNameMap.keySet())
                .orderByDesc(VppSettlementBill::getSettleYear)
                .orderByDesc(VppSettlementBill::getSettleMonth);
    }

    private Map<Long, String> resolveCustomerNameMap(String customerName) {
        LambdaQueryWrapper<VppCustomer> customerQueryWrapper = new LambdaQueryWrapper<VppCustomer>()
                .eq(VppCustomer::getTenantId, SecurityUtils.getTenantId())
                .eq(VppCustomer::getDeleteFlag, VppAuditHelper.NOT_DELETED)
                .like(StringUtils.isNotBlank(customerName), VppCustomer::getCustomerName, customerName);
        return vppCustomerMapper.selectList(customerQueryWrapper)
                .stream()
                .collect(Collectors.toMap(VppCustomer::getId, VppCustomer::getCustomerName, (a, b) -> a));
    }

    private void fillCustomerNames(List<VppEnergyReadingMonthly> records) {
        if (records == null || records.isEmpty()) {
            return;
        }
        Set<Long> customerIds = records.stream()
                .map(VppEnergyReadingMonthly::getCustomerId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (customerIds.isEmpty()) {
            return;
        }
        Map<Long, String> customerNameMap = vppCustomerMapper.selectBatchIds(customerIds).stream()
                .collect(Collectors.toMap(VppCustomer::getId, VppCustomer::getCustomerName, (a, b) -> a));
        records.forEach(reading -> reading.setCustomerName(customerNameMap.get(reading.getCustomerId())));
    }

    private void fillSiteNames(List<VppEnergyReadingMonthly> records) {
        if (records == null || records.isEmpty()) {
            return;
        }
        Map<Long, String> siteNameMap = loadSiteNameMap(records.stream()
                .map(VppEnergyReadingMonthly::getSiteId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet()));
        records.forEach(reading -> reading.setSiteName(resolveSiteName(reading.getSiteId(), siteNameMap)));
    }

    private void fillBillSiteNames(List<VppSettlementBill> records) {
        if (records == null || records.isEmpty()) {
            return;
        }
        Map<Long, String> siteNameMap = loadSiteNameMap(records.stream()
                .map(VppSettlementBill::getSiteId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet()));
        records.forEach(bill -> bill.setSiteName(resolveSiteName(bill.getSiteId(), siteNameMap)));
    }

    private Map<Long, String> loadSiteNameMap(Set<Long> siteIds) {
        if (siteIds == null || siteIds.isEmpty()) {
            return Collections.emptyMap();
        }
        return siteMapper.selectBatchIds(siteIds).stream()
                .collect(Collectors.toMap(VppSite::getId, VppSite::getSiteName, (a, b) -> a));
    }

    private String resolveSiteName(Long siteId, Map<Long, String> siteNameMap) {
        if (siteId == null) {
            return "户号级";
        }
        return siteNameMap.get(siteId);
    }

    private void fillBillCustomerNames(List<VppSettlementBill> records) {
        if (records == null || records.isEmpty()) {
            return;
        }
        Set<Long> customerIds = records.stream()
                .map(VppSettlementBill::getCustomerId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (customerIds.isEmpty()) {
            return;
        }
        Map<Long, String> customerNameMap = vppCustomerMapper.selectBatchIds(customerIds).stream()
                .collect(Collectors.toMap(VppCustomer::getId, VppCustomer::getCustomerName, (a, b) -> a));
        records.forEach(bill -> bill.setCustomerName(customerNameMap.get(bill.getCustomerId())));
    }

    private void fillBillContractPrice(List<VppSettlementBill> records) {
        if (records == null || records.isEmpty()) {
            return;
        }
        records.forEach(bill -> bill.setContractPrice(calcContractPrice(bill.getTotalEnergyKwh(), bill.getTotalAmount())));
    }

    private BigDecimal calcContractPrice(BigDecimal totalEnergyKwh, BigDecimal totalAmount) {
        if (totalEnergyKwh == null || totalAmount == null
                || totalEnergyKwh.compareTo(BigDecimal.ZERO) <= 0) {
            return null;
        }
        return totalAmount.divide(totalEnergyKwh, 4, RoundingMode.HALF_UP);
    }

    private Map<String, Object> resolveCalcCycle(String calcCycle) {
        Map<String, Object> calcCycleMap = new HashMap<>();
        String[] split = calcCycle.split("-");
        try {
            calcCycleMap.put("settleYear", split[0]);
            calcCycleMap.put("settleMonth", Integer.valueOf(split[1]));
        } catch (Exception e) {
            throw new BusinessException("解析核算周期失败！请核对参数！");
        }
        return calcCycleMap;
    }

    private void writeReadingExcel(String fileName, List<VppEnergyReadingMonthly> readingList,
                                   HttpServletResponse response) {
        try (org.apache.poi.xssf.usermodel.XSSFWorkbook workbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook()) {
            org.apache.poi.xssf.usermodel.XSSFSheet sheet = workbook.createSheet("月度电量核算");

            org.apache.poi.xssf.usermodel.XSSFCellStyle headerStyle = workbook.createCellStyle();
            headerStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.GREY_25_PERCENT.getIndex());
            headerStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
            org.apache.poi.xssf.usermodel.XSSFFont headerFont = workbook.createFont();
            headerFont.setBold(true);
            headerStyle.setFont(headerFont);

            String[] headers = {
                    "序号", "客户名称", "站点名称", "核算周期",
                    "总用电量(kWh)", "峰电量(kWh)", "平电量(kWh)", "谷电量(kWh)", "尖电量(kWh)",
                    "发电量(kWh)", "核算状态", "核算时间", "创建时间"
            };
            org.apache.poi.xssf.usermodel.XSSFRow headerRow = sheet.createRow(0);
            for (int i = 0; i < headers.length; i++) {
                org.apache.poi.xssf.usermodel.XSSFCell cell = headerRow.createCell(i);
                cell.setCellValue(headers[i]);
                cell.setCellStyle(headerStyle);
            }

            int rowIdx = 1;
            for (VppEnergyReadingMonthly reading : readingList) {
                org.apache.poi.xssf.usermodel.XSSFRow row = sheet.createRow(rowIdx++);
                setCell(row, 0, rowIdx - 1);
                setCell(row, 1, reading.getCustomerName());
                setCell(row, 2, reading.getSiteName());
                setCell(row, 3, settleCycleLabel(reading.getSettleYear(), reading.getSettleMonth()));
                setCell(row, 4, numberStr(reading.getTotalEnergyKwh()));
                setCell(row, 5, numberStr(reading.getPeakEnergyKwh()));
                setCell(row, 6, numberStr(reading.getFlatEnergyKwh()));
                setCell(row, 7, numberStr(reading.getValleyEnergyKwh()));
                setCell(row, 8, numberStr(reading.getSharpEnergyKwh()));
                setCell(row, 9, numberStr(reading.getGenEnergyKwh()));
                setCell(row, 10, calcStatusLabel(reading.getCalcStatus()));
                setCell(row, 11, dateTimeStr(reading.getCalcAt()));
                setCell(row, 12, dateTimeStr(reading.getCreateTime()));
            }

            for (int i = 0; i < headers.length; i++) {
                sheet.autoSizeColumn(i);
                int width = sheet.getColumnWidth(i);
                if (width > 15000) {
                    sheet.setColumnWidth(i, 15000);
                }
            }

            writeExcelResponse(fileName, workbook, response);
        } catch (IOException e) {
            throw new BusinessException("导出月度电量核算数据失败: " + e.getMessage());
        }
    }

    private void writeBillExcel(String fileName, List<VppSettlementBill> billList, HttpServletResponse response) {
        try (org.apache.poi.xssf.usermodel.XSSFWorkbook workbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook()) {
            org.apache.poi.xssf.usermodel.XSSFSheet sheet = workbook.createSheet("电费账单");

            org.apache.poi.xssf.usermodel.XSSFCellStyle headerStyle = workbook.createCellStyle();
            headerStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.GREY_25_PERCENT.getIndex());
            headerStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
            org.apache.poi.xssf.usermodel.XSSFFont headerFont = workbook.createFont();
            headerFont.setBold(true);
            headerStyle.setFont(headerFont);

            String[] headers = {
                    "序号", "客户名称", "站点名称", "结算周期", "用电量（kWh）", "合同电价（元/kWh）",
                    "电费（元）", "结算时间"
            };
            org.apache.poi.xssf.usermodel.XSSFRow headerRow = sheet.createRow(0);
            for (int i = 0; i < headers.length; i++) {
                org.apache.poi.xssf.usermodel.XSSFCell cell = headerRow.createCell(i);
                cell.setCellValue(headers[i]);
                cell.setCellStyle(headerStyle);
            }

            int rowIdx = 1;
            for (VppSettlementBill bill : billList) {
                org.apache.poi.xssf.usermodel.XSSFRow row = sheet.createRow(rowIdx++);
                setCell(row, 0, rowIdx - 1);
                setCell(row, 1, bill.getCustomerName());
                setCell(row, 2, bill.getSiteName());
                setCell(row, 3, settleCycleLabel(bill.getSettleYear(), bill.getSettleMonth()));
                setCell(row, 4, numberStr(bill.getTotalEnergyKwh()));
                setCell(row, 5, numberStr(bill.getContractPrice()));
                setCell(row, 6, numberStr(bill.getTotalAmount()));
                setCell(row, 7, dateTimeStr(bill.getGeneratedAt()));
            }

            for (int i = 0; i < headers.length; i++) {
                sheet.autoSizeColumn(i);
                int width = sheet.getColumnWidth(i);
                if (width > 15000) {
                    sheet.setColumnWidth(i, 15000);
                }
            }

            writeExcelResponse(fileName, workbook, response);
        } catch (IOException e) {
            throw new BusinessException("导出电费账单数据失败: " + e.getMessage());
        }
    }

    private void writeExcelResponse(String fileName, org.apache.poi.xssf.usermodel.XSSFWorkbook workbook,
                                    HttpServletResponse response) throws IOException {
        String encodedFileName = java.net.URLEncoder.encode(fileName + ".xlsx", StandardCharsets.UTF_8.name())
                .replace("+", "%20");
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName
                + "; filename*=UTF-8''" + encodedFileName);
        workbook.write(response.getOutputStream());
        response.getOutputStream().flush();
    }

    private static void setCell(org.apache.poi.xssf.usermodel.XSSFRow row, int col, Object value) {
        if (value != null) {
            row.createCell(col).setCellValue(String.valueOf(value));
        } else {
            row.createCell(col).setCellValue("");
        }
    }

    private static String numberStr(Object value) {
        return value == null ? "" : value.toString();
    }

    private static String dateTimeStr(Object value) {
        return value == null ? "" : value.toString();
    }

    private static String settleCycleLabel(String settleYear, Integer settleMonth) {
        if (StringUtils.isBlank(settleYear) || settleMonth == null) {
            return "";
        }
        return settleYear + "-" + String.format("%02d", settleMonth);
    }

    private static String calcStatusLabel(Integer calcStatus) {
        if (calcStatus == null) {
            return "";
        }
        switch (calcStatus) {
            case 0:
                return "待核算";
            case 1:
                return "已核算";
            case 2:
                return "异常";
            default:
                return String.valueOf(calcStatus);
        }
    }
}
