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

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.config.StorageProperties;
import com.nanri.aiimage.modules.convert.model.dto.ConvertRunRequest;
import com.nanri.aiimage.modules.convert.model.dto.UploadedSourceFileDto;
import com.nanri.aiimage.modules.convert.model.entity.ConvertTemplateEntity;
import com.nanri.aiimage.modules.convert.model.vo.ConvertResultItemVo;
import com.nanri.aiimage.modules.convert.model.vo.ConvertRunVo;
import com.nanri.aiimage.modules.file.service.oss.OssStorageService;
import com.nanri.aiimage.modules.task.mapper.FileResultMapper;
import com.nanri.aiimage.modules.task.mapper.FileTaskMapper;
import com.nanri.aiimage.modules.task.model.entity.FileResultEntity;
import com.nanri.aiimage.modules.task.model.entity.FileTaskEntity;
import lombok.RequiredArgsConstructor;
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 java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

@Service
@RequiredArgsConstructor
public class ConvertRunService {

    private final FileTaskMapper fileTaskMapper;
    private final FileResultMapper fileResultMapper;
    private final StorageProperties storageProperties;
    private final ConvertTemplateService convertTemplateService;
    private final ObjectMapper objectMapper;
    private final OssStorageService ossStorageService;

    public ConvertRunVo run(ConvertRunRequest request) {
        ConvertTemplateEntity template = convertTemplateService.getByCode(request.getTemplateId());

        FileTaskEntity task = new FileTaskEntity();
        task.setTaskNo("CONVERT-" + IdUtil.getSnowflakeNextIdStr());
        task.setModuleType("CONVERT");
        task.setTaskMode("IMMEDIATE");
        task.setStatus("SUCCESS");
        task.setSourceFileCount(request.getFiles().size());
        task.setCreatedBy("system");
        task.setRequestJson(JSONUtil.toJsonStr(request));
        task.setCreatedAt(LocalDateTime.now());
        task.setUpdatedAt(LocalDateTime.now());
        task.setFinishedAt(LocalDateTime.now());
        fileTaskMapper.insert(task);

        List<ConvertResultItemVo> items = new ArrayList<>();
        int successCount = 0;
        int failedCount = 0;

        for (UploadedSourceFileDto sourceFile : request.getFiles()) {
            ConvertResultItemVo item = new ConvertResultItemVo();
            item.setSourceFilename(sourceFile.getOriginalFilename());
            try {
                File inputFile = findLocalSourceFile(sourceFile.getFileKey());
                if (inputFile == null || !inputFile.exists()) {
                    throw new BusinessException("上传文件不存在，请重新上传");
                }

                String inputName = sourceFile.getOriginalFilename() == null ? inputFile.getName() : sourceFile.getOriginalFilename();
                String outputFilename = template.getOutputFilename() == null || template.getOutputFilename().isBlank()
                        ? "结果.txt"
                        : template.getOutputFilename();
                File outputDir = FileUtil.mkdir(FileUtil.file(storageProperties.getLocalTempDir(), "convert-result"));
                File outputFile = buildNamedOutputFile(outputDir, outputFilename);

                writeTxtByLegacyRules(inputFile, outputFile, template);

                String ossObjectKey = ossStorageService.uploadResultFile(outputFile, "CONVERT");
                String downloadUrl = ossStorageService.generateDownloadUrl(ossObjectKey);

                item.setSuccess(true);
                item.setOutputFilename(outputFilename);
                item.setDownloadUrl(downloadUrl);
                successCount++;

                FileResultEntity resultEntity = new FileResultEntity();
                resultEntity.setTaskId(task.getId());
                resultEntity.setModuleType("CONVERT");
                resultEntity.setSourceFilename(inputName);
                resultEntity.setResultFilename(outputFilename);
                resultEntity.setResultFileUrl(downloadUrl);
                resultEntity.setResultFileSize(outputFile.length());
                resultEntity.setResultContentType("text/plain");
                resultEntity.setSuccess(1);
                resultEntity.setCreatedAt(LocalDateTime.now());
                fileResultMapper.insert(resultEntity);
                item.setResultId(resultEntity.getId());
            } catch (Exception ex) {
                item.setSuccess(false);
                item.setError(ex.getMessage());
                failedCount++;

                FileResultEntity resultEntity = new FileResultEntity();
                resultEntity.setTaskId(task.getId());
                resultEntity.setModuleType("CONVERT");
                resultEntity.setSourceFilename(sourceFile.getOriginalFilename());
                resultEntity.setSuccess(0);
                resultEntity.setErrorMessage(ex.getMessage());
                resultEntity.setCreatedAt(LocalDateTime.now());
                fileResultMapper.insert(resultEntity);
            }
            items.add(item);
        }

        task.setSuccessFileCount(successCount);
        task.setFailedFileCount(failedCount);
        task.setResultJson(JSONUtil.toJsonStr(items));
        task.setUpdatedAt(LocalDateTime.now());
        fileTaskMapper.updateById(task);

        ConvertRunVo vo = new ConvertRunVo();
        vo.setTotal(request.getFiles().size());
        vo.setSuccessCount(successCount);
        vo.setFailedCount(failedCount);
        vo.setItems(items);
        return vo;
    }

    public List<ConvertResultItemVo> listHistory() {
        return fileResultMapper.selectList(new LambdaQueryWrapper<FileResultEntity>()
                        .eq(FileResultEntity::getModuleType, "CONVERT")
                        .eq(FileResultEntity::getSuccess, 1)
                        .orderByDesc(FileResultEntity::getCreatedAt)
                        .last("limit 50"))
                .stream()
                .map(entity -> {
                    ConvertResultItemVo vo = new ConvertResultItemVo();
                    vo.setResultId(entity.getId());
                    vo.setSourceFilename(entity.getCreatedAt() == null ? "" : entity.getCreatedAt().toLocalDate().toString().replace("-", ""));
                    vo.setOutputFilename(entity.getResultFilename());
                    vo.setDownloadUrl(entity.getResultFileUrl());
                    vo.setSuccess(true);
                    return vo;
                })
                .toList();
    }

    public void deleteHistory(Long resultId) {
        FileResultEntity entity = fileResultMapper.selectById(resultId);
        if (entity == null || !"CONVERT".equals(entity.getModuleType())) {
            throw new BusinessException("记录不存在");
        }
        fileResultMapper.deleteById(resultId);
    }

    public File getResultFile(Long resultId) {
        FileResultEntity entity = fileResultMapper.selectById(resultId);
        if (entity == null || entity.getResultFileUrl() == null || !"CONVERT".equals(entity.getModuleType())) {
            throw new BusinessException("结果文件不存在");
        }
        File file = new File(entity.getResultFileUrl());
        if (!file.exists()) {
            throw new BusinessException("结果文件不存在");
        }
        return file;
    }

    private void writeTxtByLegacyRules(File inputFile, File outputFile, ConvertTemplateEntity templateEntity) throws IOException {
        List<String> requiredColumns = readStringList(templateEntity.getRequiredSourceColumnsJson());
        List<String> headerColumns = readStringList(templateEntity.getHeaderColumnsJson());
        Map<String, String> fieldMapping = readStringMap(templateEntity.getFieldMappingJson());
        Map<String, String> defaults = readStringMap(templateEntity.getDefaultsJson());
        List<String> preambleLines = readStringList(templateEntity.getPreambleLinesJson());
        int blankHeaderRowsAfterSchema = templateEntity.getBlankHeaderRowsAfterSchema() == null ? 0 : templateEntity.getBlankHeaderRowsAfterSchema();

        DataFormatter formatter = new DataFormatter();
        try (FileInputStream fis = new FileInputStream(inputFile); Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(fis)) {
            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++) {
                String value = normalizeCellText(formatter.formatCellValue(headerRow.getCell(i)));
                if (!value.isBlank() && !headerMap.containsKey(value)) {
                    headerMap.put(value, i);
                }
            }

            List<String> missing = requiredColumns.stream().filter(column -> !headerMap.containsKey(column)).toList();
            if (!missing.isEmpty()) {
                throw new BusinessException("缺少列：" + String.join("、", missing));
            }

            List<String> rows = new ArrayList<>();
            rows.addAll(preambleLines);
            rows.add(String.join("\t", headerColumns));
            for (int i = 0; i < blankHeaderRowsAfterSchema; i++) {
                rows.add("\t".repeat(Math.max(0, headerColumns.size() - 1)));
            }

            long batchId = System.currentTimeMillis();
            int rowIndex = 1;
            int asinIndex = headerMap.getOrDefault("ASIN", -1);
            for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row row = sheet.getRow(rowNum);
                if (row == null) {
                    continue;
                }
                String asin = asinIndex >= 0 ? normalizeCellText(formatter.formatCellValue(row.getCell(asinIndex))) : "";
                if (asin.isBlank()) {
                    continue;
                }

                List<String> lineValues = new ArrayList<>();
                for (String column : headerColumns) {
                    if ("sku".equals(column)) {
                        lineValues.add(batchId + "-" + rowIndex);
                        continue;
                    }
                    if (fieldMapping.containsKey(column)) {
                        String sourceColumn = fieldMapping.get(column);
                        Integer sourceIndex = headerMap.get(sourceColumn);
                        String value = sourceIndex == null ? "" : normalizeCellText(formatter.formatCellValue(row.getCell(sourceIndex)));
                        lineValues.add(value);
                        continue;
                    }
                    if (defaults.containsKey(column)) {
                        lineValues.add(defaults.get(column));
                        continue;
                    }
                    lineValues.add("");
                }
                rows.add(String.join("\t", lineValues));
                rowIndex++;
            }
            FileUtil.writeLines(rows, outputFile, StandardCharsets.UTF_8);
        }
    }

    private List<String> readStringList(String json) {
        try {
            if (json == null || json.isBlank()) {
                return new ArrayList<>();
            }
            return objectMapper.readValue(json, new TypeReference<List<String>>() {});
        } catch (Exception ex) {
            throw new BusinessException("模板配置解析失败");
        }
    }

    private Map<String, String> readStringMap(String json) {
        try {
            if (json == null || json.isBlank()) {
                return new HashMap<>();
            }
            return objectMapper.readValue(json, new TypeReference<Map<String, String>>() {});
        } catch (Exception ex) {
            throw new BusinessException("模板配置解析失败");
        }
    }

    private File findLocalSourceFile(String fileKey) {
        File baseDir = FileUtil.file(storageProperties.getLocalTempDir());
        if (!baseDir.exists()) {
            return null;
        }
        List<File> matchedFiles = FileUtil.loopFiles(baseDir, pathname -> pathname.isFile() && pathname.getName().startsWith(fileKey));
        return matchedFiles.isEmpty() ? null : matchedFiles.getFirst();
    }

    private File buildNamedOutputFile(File outputDir, String filename) {
        File candidate = FileUtil.file(outputDir, filename);
        if (!candidate.exists()) {
            return candidate;
        }
        String mainName = FileUtil.mainName(filename);
        String extName = FileUtil.extName(filename);
        int index = 2;
        while (true) {
            String nextFilename = extName == null || extName.isBlank()
                    ? mainName + "_" + index
                    : mainName + "_" + index + "." + extName;
            File nextCandidate = FileUtil.file(outputDir, nextFilename);
            if (!nextCandidate.exists()) {
                return nextCandidate;
            }
            index++;
        }
    }

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