package com.nanri.aiimage.modules.dedupe.service;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.modules.dedupe.mapper.DedupeTotalDataMapper;
import com.nanri.aiimage.modules.dedupe.model.dto.DedupeTotalDataCreateRequest;
import com.nanri.aiimage.modules.dedupe.model.dto.DedupeTotalDataUpdateRequest;
import com.nanri.aiimage.modules.dedupe.model.entity.DedupeTotalDataEntity;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataImportProgressVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataImportStartVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataImportVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataItemVo;
import com.nanri.aiimage.modules.dedupe.model.vo.DedupeTotalDataPageVo;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Service
@RequiredArgsConstructor
public class DedupeTotalDataService {

    private static final int COMPARE_BATCH_SIZE = 1000;

    private final DedupeTotalDataMapper dedupeTotalDataMapper;
    private final Map<String, DedupeTotalDataImportProgressVo> importProgressMap = new ConcurrentHashMap<>();
    private final Map<String, DedupeTotalDataImportProgressVo> deleteImportProgressMap = new ConcurrentHashMap<>();

    public DedupeTotalDataPageVo page(long page, long pageSize, String keyword) {
        long safePage = Math.max(page, 1);
        long safePageSize = Math.min(Math.max(pageSize, 1), 100);
        String safeKeyword = keyword == null ? "" : keyword.trim();
        LambdaQueryWrapper<DedupeTotalDataEntity> query = new LambdaQueryWrapper<DedupeTotalDataEntity>()
                .like(!safeKeyword.isEmpty(), DedupeTotalDataEntity::getDataValue, safeKeyword)
                .orderByDesc(DedupeTotalDataEntity::getId);
        Long total = dedupeTotalDataMapper.selectCount(query);
        List<DedupeTotalDataItemVo> items = dedupeTotalDataMapper.selectList(query.last("LIMIT " + ((safePage - 1) * safePageSize) + ", " + safePageSize))
                .stream()
                .map(this::toItemVo)
                .toList();
        DedupeTotalDataPageVo vo = new DedupeTotalDataPageVo();
        vo.setItems(items);
        vo.setTotal(total);
        vo.setPage(safePage);
        vo.setPageSize(safePageSize);
        return vo;
    }

    @Transactional
    public DedupeTotalDataItemVo create(DedupeTotalDataCreateRequest request) {
        String dataValue = normalizeComparableValue(request.getDataValue());
        ensureUnique(dataValue, null);
        DedupeTotalDataEntity entity = new DedupeTotalDataEntity();
        entity.setDataValue(dataValue);
        dedupeTotalDataMapper.insert(entity);
        return toItemVo(getById(entity.getId()));
    }

    public Set<String> listComparableValues() {
        return dedupeTotalDataMapper.selectAllDataValues().stream()
                .map(this::normalizeComparableValue)
                .filter(value -> !value.isEmpty())
                .collect(java.util.stream.Collectors.toSet());
    }

    public Set<String> findExistingComparableValues(Collection<String> values) {
        if (values == null || values.isEmpty()) {
            return Set.of();
        }
        List<String> normalizedValues = values.stream()
                .map(this::normalizeComparableValueOrBlank)
                .filter(value -> !value.isEmpty())
                .distinct()
                .toList();
        if (normalizedValues.isEmpty()) {
            return Set.of();
        }
        Set<String> existingValues = new HashSet<>();
        for (int start = 0; start < normalizedValues.size(); start += COMPARE_BATCH_SIZE) {
            int end = Math.min(start + COMPARE_BATCH_SIZE, normalizedValues.size());
            existingValues.addAll(dedupeTotalDataMapper.selectExistingDataValues(normalizedValues.subList(start, end)));
        }
        return existingValues;
    }

    public DedupeTotalDataImportStartVo startImport(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException("请上传 xlsx 文件");
        }
        String importId = IdUtil.fastSimpleUUID();
        DedupeTotalDataImportProgressVo progress = new DedupeTotalDataImportProgressVo();
        progress.setStatus("pending");
        progress.setTotalRows(0);
        progress.setProcessedRows(0);
        progress.setAsinCount(0);
        progress.setInsertedCount(0);
        progress.setSkippedCount(0);
        importProgressMap.put(importId, progress);

        try {
            byte[] fileBytes = file.getBytes();
            String filename = file.getOriginalFilename();
            Thread.ofVirtual().start(() -> runImportTask(importId, fileBytes, filename));
        } catch (Exception e) {
            importProgressMap.remove(importId);
            throw new BusinessException("读取上传文件失败");
        }

        DedupeTotalDataImportStartVo vo = new DedupeTotalDataImportStartVo();
        vo.setImportId(importId);
        return vo;
    }

    public DedupeTotalDataImportProgressVo getImportProgress(String importId) {
        DedupeTotalDataImportProgressVo progress = importProgressMap.get(importId);
        if (progress == null) {
            throw new BusinessException("导入任务不存在");
        }
        return progress;
    }

    public DedupeTotalDataImportStartVo startDeleteImport(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException("请上传 xlsx 文件");
        }
        String importId = IdUtil.fastSimpleUUID();
        DedupeTotalDataImportProgressVo progress = new DedupeTotalDataImportProgressVo();
        progress.setStatus("pending");
        progress.setTotalRows(0);
        progress.setProcessedRows(0);
        progress.setAsinCount(0);
        progress.setInsertedCount(0);
        progress.setSkippedCount(0);
        deleteImportProgressMap.put(importId, progress);

        try {
            byte[] fileBytes = file.getBytes();
            String filename = file.getOriginalFilename();
            Thread.ofVirtual().start(() -> runDeleteImportTask(importId, fileBytes, filename));
        } catch (Exception e) {
            deleteImportProgressMap.remove(importId);
            throw new BusinessException("读取上传文件失败");
        }

        DedupeTotalDataImportStartVo vo = new DedupeTotalDataImportStartVo();
        vo.setImportId(importId);
        return vo;
    }

    public DedupeTotalDataImportProgressVo getDeleteImportProgress(String importId) {
        DedupeTotalDataImportProgressVo progress = deleteImportProgressMap.get(importId);
        if (progress == null) {
            throw new BusinessException("删除任务不存在");
        }
        return progress;
    }

    private void runDeleteImportTask(String importId, byte[] fileBytes, String filename) {
        DedupeTotalDataImportProgressVo progress = deleteImportProgressMap.get(importId);
        if (progress == null) {
            return;
        }
        progress.setStatus("running");
        try (InputStream inputStream = new ByteArrayInputStream(fileBytes)) {
            DedupeTotalDataImportVo result = deleteFromExcelInternal(inputStream, filename, progress);
            progress.setTotalRows(result.getTotalRows());
            progress.setAsinCount(result.getAsinCount());
            progress.setInsertedCount(result.getInsertedCount());
            progress.setSkippedCount(result.getSkippedCount());
            progress.setProcessedRows(result.getTotalRows());
            progress.setStatus("success");
        } catch (Exception e) {
            progress.setStatus("failed");
            progress.setErrorMessage(e instanceof BusinessException ? e.getMessage() : "删除 Excel 匹配数据失败");
        }
    }

    private void runImportTask(String importId, byte[] fileBytes, String filename) {
        DedupeTotalDataImportProgressVo progress = importProgressMap.get(importId);
        if (progress == null) {
            return;
        }
        progress.setStatus("running");
        try (InputStream inputStream = new ByteArrayInputStream(fileBytes)) {
            DedupeTotalDataImportVo result = importFromExcelInternal(inputStream, filename, progress);
            progress.setTotalRows(result.getTotalRows());
            progress.setAsinCount(result.getAsinCount());
            progress.setInsertedCount(result.getInsertedCount());
            progress.setSkippedCount(result.getSkippedCount());
            progress.setProcessedRows(result.getTotalRows());
            progress.setStatus("success");
        } catch (Exception e) {
            progress.setStatus("failed");
            progress.setErrorMessage(e instanceof BusinessException ? e.getMessage() : "导入 Excel 失败");
        }
    }

    @Transactional
    public DedupeTotalDataImportVo importFromExcel(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException("请上传 xlsx 文件");
        }
        try (InputStream inputStream = file.getInputStream()) {
            return importFromExcelInternal(inputStream, file.getOriginalFilename(), null);
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException("导入 Excel 失败");
        }
    }

    @Transactional
    public DedupeTotalDataImportVo deleteFromExcel(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException("请上传 xlsx 文件");
        }
        try (InputStream inputStream = file.getInputStream()) {
            return deleteFromExcelInternal(inputStream, file.getOriginalFilename(), null);
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException("删除 Excel 匹配数据失败");
        }
    }

    @Transactional
    private DedupeTotalDataImportVo importFromExcelInternal(InputStream inputStream, String filename, DedupeTotalDataImportProgressVo progress) {
        String lowerFilename = filename == null ? "" : filename.toLowerCase();
        if (!(lowerFilename.endsWith(".xlsx") || lowerFilename.endsWith(".xls"))) {
            throw new BusinessException("仅支持 .xlsx 或 .xls 文件");
        }

        DataFormatter formatter = new DataFormatter();
        try (Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(inputStream)) {
            Sheet sheet = workbook.getSheetAt(0);
            Row headerRow = sheet.getRow(0);
            if (headerRow == null) {
                throw new BusinessException("Excel 表头为空");
            }

            Map<String, Integer> headerMap = new HashMap<>();
            for (int i = 0; i < headerRow.getLastCellNum(); i++) {
                Cell cell = headerRow.getCell(i);
                String value = normalizeExcelText(cell == null ? null : formatter.formatCellValue(cell));
                if (!value.isBlank() && !headerMap.containsKey(value)) {
                    headerMap.put(value, i);
                }
            }

            Integer asinIndex = headerMap.get("ASIN");
            if (asinIndex == null) {
                throw new BusinessException("缺少 ASIN 列");
            }

            Set<String> seenInFile = new HashSet<>();
            int totalRows = Math.max(sheet.getLastRowNum(), 0);
            int asinCount = 0;
            int insertedCount = 0;
            int skippedCount = 0;
            if (progress != null) {
                progress.setTotalRows(totalRows);
                progress.setProcessedRows(0);
                progress.setAsinCount(0);
                progress.setInsertedCount(0);
                progress.setSkippedCount(0);
                progress.setErrorMessage(null);
            }

            for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row row = sheet.getRow(rowNum);
                if (row == null) {
                    skippedCount++;
                    if (progress != null) {
                        progress.setProcessedRows(rowNum);
                        progress.setAsinCount(asinCount);
                        progress.setInsertedCount(insertedCount);
                        progress.setSkippedCount(skippedCount);
                    }
                    continue;
                }
                String asin = normalizeComparableValueOrBlank(formatter.formatCellValue(row.getCell(asinIndex)));
                if (asin.isBlank()) {
                    skippedCount++;
                    if (progress != null) {
                        progress.setProcessedRows(rowNum);
                        progress.setAsinCount(asinCount);
                        progress.setInsertedCount(insertedCount);
                        progress.setSkippedCount(skippedCount);
                    }
                    continue;
                }
                asinCount++;
                String dataValue = asin;
                if (!seenInFile.add(dataValue)) {
                    skippedCount++;
                    if (progress != null) {
                        progress.setProcessedRows(rowNum);
                        progress.setAsinCount(asinCount);
                        progress.setInsertedCount(insertedCount);
                        progress.setSkippedCount(skippedCount);
                    }
                    continue;
                }
                if (existsDataValue(dataValue)) {
                    skippedCount++;
                    if (progress != null) {
                        progress.setProcessedRows(rowNum);
                        progress.setAsinCount(asinCount);
                        progress.setInsertedCount(insertedCount);
                        progress.setSkippedCount(skippedCount);
                    }
                    continue;
                }
                DedupeTotalDataEntity entity = new DedupeTotalDataEntity();
                entity.setDataValue(dataValue);
                dedupeTotalDataMapper.insert(entity);
                insertedCount++;
                if (progress != null) {
                    progress.setProcessedRows(rowNum);
                    progress.setAsinCount(asinCount);
                    progress.setInsertedCount(insertedCount);
                    progress.setSkippedCount(skippedCount);
                }
            }

            DedupeTotalDataImportVo vo = new DedupeTotalDataImportVo();
            vo.setTotalRows(totalRows);
            vo.setAsinCount(asinCount);
            vo.setInsertedCount(insertedCount);
            vo.setSkippedCount(skippedCount);
            return vo;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException("导入 Excel 失败");
        }
    }

    @Transactional
    private DedupeTotalDataImportVo deleteFromExcelInternal(InputStream inputStream, String filename, DedupeTotalDataImportProgressVo progress) {
        String lowerFilename = filename == null ? "" : filename.toLowerCase();
        if (!(lowerFilename.endsWith(".xlsx") || lowerFilename.endsWith(".xls"))) {
            throw new BusinessException("仅支持 .xlsx 或 .xls 文件");
        }

        DataFormatter formatter = new DataFormatter();
        try (Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(inputStream)) {
            Sheet sheet = workbook.getSheetAt(0);
            Row headerRow = sheet.getRow(0);
            if (headerRow == null) {
                throw new BusinessException("Excel 表头为空");
            }

            Map<String, Integer> headerMap = new HashMap<>();
            for (int i = 0; i < headerRow.getLastCellNum(); i++) {
                Cell cell = headerRow.getCell(i);
                String value = normalizeExcelText(cell == null ? null : formatter.formatCellValue(cell));
                if (!value.isBlank() && !headerMap.containsKey(value)) {
                    headerMap.put(value, i);
                }
            }

            Integer asinIndex = headerMap.get("ASIN");
            if (asinIndex == null) {
                throw new BusinessException("缺少 ASIN 列");
            }

            Set<String> seenInFile = new HashSet<>();
            int totalRows = Math.max(sheet.getLastRowNum(), 0);
            int asinCount = 0;
            int deletedCount = 0;
            int skippedCount = 0;
            if (progress != null) {
                progress.setTotalRows(totalRows);
                progress.setProcessedRows(0);
                progress.setAsinCount(0);
                progress.setInsertedCount(0);
                progress.setSkippedCount(0);
                progress.setErrorMessage(null);
            }

            for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row row = sheet.getRow(rowNum);
                if (row == null) {
                    skippedCount++;
                    if (progress != null) {
                        progress.setProcessedRows(rowNum);
                        progress.setAsinCount(asinCount);
                        progress.setInsertedCount(deletedCount);
                        progress.setSkippedCount(skippedCount);
                    }
                    continue;
                }
                String asin = normalizeComparableValueOrBlank(formatter.formatCellValue(row.getCell(asinIndex)));
                if (asin.isBlank()) {
                    skippedCount++;
                    if (progress != null) {
                        progress.setProcessedRows(rowNum);
                        progress.setAsinCount(asinCount);
                        progress.setInsertedCount(deletedCount);
                        progress.setSkippedCount(skippedCount);
                    }
                    continue;
                }
                asinCount++;
                String dataValue = asin;
                if (!seenInFile.add(dataValue)) {
                    skippedCount++;
                    if (progress != null) {
                        progress.setProcessedRows(rowNum);
                        progress.setAsinCount(asinCount);
                        progress.setInsertedCount(deletedCount);
                        progress.setSkippedCount(skippedCount);
                    }
                    continue;
                }

                int deletedThisRow = deleteByDataValue(dataValue);
                if (deletedThisRow > 0) {
                    deletedCount += deletedThisRow;
                } else {
                    skippedCount++;
                }
                if (progress != null) {
                    progress.setProcessedRows(rowNum);
                    progress.setAsinCount(asinCount);
                    progress.setInsertedCount(deletedCount);
                    progress.setSkippedCount(skippedCount);
                }
            }

            DedupeTotalDataImportVo vo = new DedupeTotalDataImportVo();
            vo.setTotalRows(totalRows);
            vo.setAsinCount(asinCount);
            vo.setInsertedCount(deletedCount);
            vo.setSkippedCount(skippedCount);
            return vo;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException("删除 Excel 匹配数据失败");
        }
    }

    @Transactional
    public DedupeTotalDataItemVo update(Long id, DedupeTotalDataUpdateRequest request) {
        DedupeTotalDataEntity entity = getById(id);
        String dataValue = normalizeComparableValue(request.getDataValue());
        ensureUnique(dataValue, id);
        entity.setDataValue(dataValue);
        dedupeTotalDataMapper.updateById(entity);
        return toItemVo(getById(id));
    }

    @Transactional
    public void delete(Long id) {
        DedupeTotalDataEntity entity = getById(id);
        dedupeTotalDataMapper.deleteById(entity.getId());
    }

    private DedupeTotalDataEntity getById(Long id) {
        DedupeTotalDataEntity entity = dedupeTotalDataMapper.selectById(id);
        if (entity == null) {
            throw new BusinessException("数据不存在");
        }
        return entity;
    }

    public String normalizeComparableValue(String value) {
        String dataValue = normalizeComparableValueOrBlank(value);
        if (dataValue.isEmpty()) {
            throw new BusinessException("数据不能为空");
        }
        return dataValue;
    }

    public String normalizeComparableValueOrBlank(String value) {
        return normalizeExcelText(value);
    }

    private void ensureUnique(String dataValue, Long excludeId) {
        LambdaQueryWrapper<DedupeTotalDataEntity> query = new LambdaQueryWrapper<DedupeTotalDataEntity>()
                .eq(DedupeTotalDataEntity::getDataValue, dataValue)
                .last("LIMIT 1");
        DedupeTotalDataEntity exists = dedupeTotalDataMapper.selectOne(query);
        if (exists != null && (excludeId == null || !exists.getId().equals(excludeId))) {
            throw new BusinessException("数据已存在");
        }
    }

    private boolean existsDataValue(String dataValue) {
        LambdaQueryWrapper<DedupeTotalDataEntity> query = new LambdaQueryWrapper<DedupeTotalDataEntity>()
                .eq(DedupeTotalDataEntity::getDataValue, dataValue)
                .last("LIMIT 1");
        return dedupeTotalDataMapper.selectOne(query) != null;
    }

    private int deleteByDataValue(String dataValue) {
        return dedupeTotalDataMapper.delete(new LambdaQueryWrapper<DedupeTotalDataEntity>()
                .eq(DedupeTotalDataEntity::getDataValue, dataValue));
    }

    private String normalizeExcelText(String value) {
        if (value == null) {
            return "";
        }
        return value.replace("\ufeff", "")
                .replace("\u3000", " ")
                .replace("\r\n", " ")
                .replace("\r", " ")
                .replace("\n", " ")
                .replace("\t", " ")
                .trim()
                .replaceAll("\\s+", " ");
    }

    private DedupeTotalDataItemVo toItemVo(DedupeTotalDataEntity entity) {
        DedupeTotalDataItemVo vo = new DedupeTotalDataItemVo();
        vo.setId(entity.getId());
        vo.setDataValue(entity.getDataValue());
        vo.setCreatedAt(entity.getCreatedAt());
        return vo;
    }
}
