package com.njcn.gather.icd.mapping.service.impl;

import com.njcn.gather.icd.mapping.component.DefaultTemplateLoader;
import com.njcn.gather.icd.mapping.component.FileStorageService;
import com.njcn.gather.icd.mapping.component.IcdParserService;
import com.njcn.gather.icd.mapping.component.IndexAnalysisService;
import com.njcn.gather.icd.mapping.component.IndexValidationService;
import com.njcn.gather.icd.mapping.component.MappingDocumentSerializer;
import com.njcn.gather.icd.mapping.component.MappingGenerationService;
import com.njcn.gather.icd.mapping.pojo.bo.GenerateMappingResult;
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis;
import com.njcn.gather.icd.mapping.pojo.bo.analysis.ValidationResult;
import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
import com.njcn.gather.icd.mapping.pojo.bo.template.DefaultTemplate;
import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
import com.njcn.gather.icd.mapping.pojo.dto.IndexSelectionGroupCommand;
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
import com.njcn.gather.icd.mapping.service.MappingTaskService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * ICD 映射任务编排服务实现。
 *
 * 按固定链路组织 ICD 解析、索引分析、校验和映射生成，避免 Controller 关注业务编排细节。
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MappingTaskServiceImpl implements MappingTaskService {

    /** ICD 解析阶段名称。 */
    private static final String ICD_PARSE_TASK_NAME = "ICD 解析";
    /** 正式映射生成阶段名称。 */
    private static final String MAPPING_GENERATE_TASK_NAME = "映射生成";
    /** 候选分析成功提示。 */
    private static final String INDEX_SELECTION_SUCCESS_MESSAGE = "ICD 解析成功，请确认解析结果并完成索引绑定后提交";
    /** 索引绑定缺失提示。 */
    private static final String INDEX_SELECTION_MISSING_MESSAGE = "索引配置缺失，请根据候选信息完成标签与数字索引的绑定后重新提交";
    /** 索引绑定非法提示。 */
    private static final String INDEX_SELECTION_INVALID_MESSAGE = "索引配置不合法，请根据候选信息完成标签与数字索引的绑定后重新提交";
    /** 映射生成成功提示。 */
    private static final String MAPPING_GENERATE_SUCCESS_MESSAGE = "映射生成成功";

    /** ICD/SCL 文件解析服务。 */
    private final IcdParserService icdParserService;
    /** DefaultCfg.txt 默认模板加载器。 */
    private final DefaultTemplateLoader defaultTemplateLoader;
    /** 根据 ICD 和模板生成前端可选索引候选。 */
    private final IndexAnalysisService indexAnalysisService;
    /** 校验前端回传的标签与 lnInst 绑定关系。 */
    private final IndexValidationService indexValidationService;
    /** 根据有效绑定关系生成最终 MappingDocument。 */
    private final MappingGenerationService mappingGenerationService;
    /** 将 MappingDocument 序列化为 JSON。 */
    private final MappingDocumentSerializer mappingDocumentSerializer;
    /** 按需把生成结果保存到本地磁盘。 */
    private final FileStorageService fileStorageService;

    @Override
    public GenerateMappingResult getICD(GenerateFromIcdCommand command) {
        return executeTask(ICD_PARSE_TASK_NAME, result -> {
            parseTaskContext(command, result);
            result.setStatus(GenerateStatus.NEED_INDEX_SELECTION);
            result.setMessage(INDEX_SELECTION_SUCCESS_MESSAGE);
        });
    }

    @Override
    public GenerateMappingResult getIcdMmsJson(GenerateFromIcdCommand command) {
        GenerateMappingResult icdResult = getICD(command);
        if (icdResult.getStatus() == GenerateStatus.FAILED || icdResult.getIcdDocument() == null) {
            return icdResult;
        }

        command.setIcdDocument(icdResult.getIcdDocument());
        return getMmsJson(command);
    }

    @Override
    public GenerateMappingResult getMmsJson(GenerateFromIcdCommand command) {
        return executeTask(MAPPING_GENERATE_TASK_NAME, result -> {
            MappingTaskContext context = buildTaskContext(requireIcdDocument(command), result);
            if (isIndexSelectionEmpty(command.getIndexSelection())) {
                markNeedIndexSelection(result, INDEX_SELECTION_MISSING_MESSAGE);
                return;
            }

            ValidationResult validationResult = indexValidationService.validate(context.indexAnalysis, command.getIndexSelection());
            if (!validationResult.isValid()) {
                markNeedIndexSelection(result, INDEX_SELECTION_INVALID_MESSAGE);
                result.getProblems().addAll(validationResult.getProblems());
                return;
            }

            MappingDocument mappingDocument = mappingGenerationService.generate(
                    context.icdDocument,
                    context.template,
                    context.indexAnalysis,
                    command.getIndexSelection(),
                    command.getVersion(),
                    command.getAuthor()
            );
            result.setMappingDocument(mappingDocument);

            String mappingJson = serializeMappingDocument(mappingDocument, command.isPrettyJson());
            result.setMappingJson(mappingJson);

            if (command.isSaveToDisk()) {
                String fileName = buildOutputFileName(context.icdDocument, command.isPrettyJson());
                String savedPath = fileStorageService.save(fileName, mappingJson, command.getOutputDir());
                result.setSavedPath(savedPath);
            }

            result.setStatus(GenerateStatus.SUCCESS);
            result.setMessage(MAPPING_GENERATE_SUCCESS_MESSAGE);
        });
    }

    /**
     * 统一包装任务执行结果，避免每个入口重复编写异常兜底逻辑。
     */
    private GenerateMappingResult executeTask(String taskName, MappingTaskAction action) {
        GenerateMappingResult result = new GenerateMappingResult();
        try {
            action.execute(result);
        } catch (Exception ex) {
            handleTaskFailure(result, taskName, ex);
        }
        return result;
    }

    /**
     * 解析上传的 ICD 文件，并补齐候选分析上下文。
     */
    private MappingTaskContext parseTaskContext(GenerateFromIcdCommand command, GenerateMappingResult result) {
        IcdDocument icdDocument = icdParserService.parse(command.getFileBytes(), command.getFileName());
        return buildTaskContext(icdDocument, result);
    }

    /**
     * 基于 ICD 解析结果补齐模板和候选分析信息，供后续提交流程复用。
     */
    private MappingTaskContext buildTaskContext(IcdDocument icdDocument, GenerateMappingResult result) {
        fillIcdSummary(result, icdDocument);

        DefaultTemplate template = loadTemplate(result);
        IndexAnalysis indexAnalysis = analyzeIndexCandidates(icdDocument, template, result);
        result.setIndexAnalysis(indexAnalysis);
        return new MappingTaskContext(icdDocument, template, indexAnalysis);
    }

    /**
     * 提交流程必须带上前端确认后的 ICD 解析结果。
     */
    private IcdDocument requireIcdDocument(GenerateFromIcdCommand command) {
        if (command.getIcdDocument() == null) {
            throw new IllegalArgumentException("ICD 解析结果不能为空");
        }
        return command.getIcdDocument();
    }

    /**
     * 加载并校验默认模板。
     */
    private DefaultTemplate loadTemplate(GenerateMappingResult result) {
        DefaultTemplate template = defaultTemplateLoader.load();
        result.getProblems().addAll(template.verify());
        return template;
    }

    /**
     * 分析 ICD 对应的索引候选。
     */
    private IndexAnalysis analyzeIndexCandidates(IcdDocument icdDocument,
                                                 DefaultTemplate template,
                                                 GenerateMappingResult result) {
        IndexAnalysis indexAnalysis = indexAnalysisService.analyze(icdDocument, template);
        result.getProblems().addAll(indexAnalysis.getProblems());
        return indexAnalysis;
    }

    /**
     * 回填响应公共信息。
     */
    private void fillIcdSummary(GenerateMappingResult result, IcdDocument icdDocument) {
        result.setIedName(icdDocument.getIedName());
        result.setLdInst(icdDocument.getLdInst());
        result.setIcdDocument(icdDocument);
    }

    /**
     * 标记当前仍需用户重新确认索引绑定。
     */
    private void markNeedIndexSelection(GenerateMappingResult result, String message) {
        result.setStatus(GenerateStatus.NEED_INDEX_SELECTION);
        result.setMessage(message);
    }

    /**
     * 根据输出模式选择紧凑或美化 JSON。
     */
    private String serializeMappingDocument(MappingDocument mappingDocument, boolean prettyJson) {
        return prettyJson
                ? mappingDocumentSerializer.toPrettyJson(mappingDocument)
                : mappingDocumentSerializer.toCompactJson(mappingDocument);
    }

    /**
     * 构造落盘文件名。
     *
     * 文件名优先使用 IED 名称；如 IED 名称为空或清理后为空，则回退使用 mapping。
     */
    private String buildOutputFileName(IcdDocument icdDocument, boolean prettyJson) {
        String baseName = icdDocument.getIedName() == null ? "mapping" : icdDocument.getIedName();
        String safeBaseName = baseName.replaceAll("[\\\\/:*?\"<>|]+", "_").trim();
        if (safeBaseName.isEmpty()) {
            safeBaseName = "mapping";
        }
        return safeBaseName + (prettyJson ? "-mapping-pretty.json" : "-mapping.json");
    }

    /**
     * 统一处理任务异常，避免控制层再补失败分支。
     */
    private void handleTaskFailure(GenerateMappingResult result, String taskName, Exception ex) {
        log.error("{}失败", taskName, ex);
        String errorMessage = resolveErrorMessage(ex);
        result.setStatus(GenerateStatus.FAILED);
        result.setMessage(taskName + "失败：" + errorMessage);
        result.getProblems().add(errorMessage);
    }

    private boolean isIndexSelectionEmpty(List<IndexSelectionGroupCommand> indexSelection) {
        return indexSelection == null || indexSelection.isEmpty();
    }

    private String resolveErrorMessage(Exception ex) {
        if (ex.getMessage() == null || ex.getMessage().trim().isEmpty()) {
            return ex.getClass().getSimpleName();
        }
        return ex.getMessage();
    }

    @FunctionalInterface
    private interface MappingTaskAction {
        void execute(GenerateMappingResult result) throws Exception;
    }

    /**
     * 供多个任务阶段复用的编排上下文。
     */
    private static class MappingTaskContext {

        private final IcdDocument icdDocument;

        private final DefaultTemplate template;

        private final IndexAnalysis indexAnalysis;

        private MappingTaskContext(IcdDocument icdDocument, DefaultTemplate template, IndexAnalysis indexAnalysis) {
            this.icdDocument = icdDocument;
            this.template = template;
            this.indexAnalysis = indexAnalysis;
        }
    }
}
