package com.zhengmeng.ocrplatform.form;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhengmeng.ocrplatform.task.TaskNotFoundException;
import com.zhengmeng.ocrplatform.task.OcrTaskEntity;
import com.zhengmeng.ocrplatform.task.OcrTaskRepository;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Service
public class FormTemplateService {
    private final FormTemplateRepository templateRepository;
    private final FormFieldMappingRepository mappingRepository;
    private final OcrTaskRepository taskRepository;
    private final ObjectMapper objectMapper;

    public FormTemplateService(FormTemplateRepository templateRepository,
                              FormFieldMappingRepository mappingRepository,
                              OcrTaskRepository taskRepository,
                              ObjectMapper objectMapper) {
        this.templateRepository = templateRepository;
        this.mappingRepository = mappingRepository;
        this.taskRepository = taskRepository;
        this.objectMapper = objectMapper;
    }

    public List<FormTemplateRecord> listLatestTemplates() {
        List<FormTemplateEntity> templates = templateRepository.findByOrderByTemplateCodeAscVersionNoDesc();
        LinkedHashMap<String, FormTemplateRecord> latestByCode = new LinkedHashMap<>();

        for (FormTemplateEntity template : templates) {
            if (!latestByCode.containsKey(template.getTemplateCode())) {
                latestByCode.put(template.getTemplateCode(), FormTemplateRecord.from(template));
            }
        }

        return new ArrayList<>(latestByCode.values());
    }

    public List<FormTemplateRecord> listVersions(String templateCode) {
        return templateRepository.findByTemplateCodeOrderByVersionNoDesc(templateCode).stream()
                .map(FormTemplateRecord::from)
                .toList();
    }

    public FormTemplateRecord findTemplateOrLatest(String templateCode, Integer versionNo) {
        FormTemplateEntity template = (versionNo == null)
                ? templateRepository.findFirstByTemplateCodeAndEnabledTrueOrderByVersionNoDesc(templateCode)
                .orElseThrow(() -> new FormTemplateNotFoundException("Template not found: " + templateCode))
                : templateRepository.findByTemplateCodeAndVersionNo(templateCode, versionNo)
                .orElseThrow(() -> new FormTemplateNotFoundException("Template version not found: " + templateCode + "/" + versionNo));
        return FormTemplateRecord.from(template);
    }

    @Transactional
    public FormTemplateRecord upsertTemplate(FormTemplateUpsertRequest request) {
        validateFormJson(request.formJson());

        LocalDateTime now = LocalDateTime.now();
        String templateCode = request.templateCode().trim();
        int versionNo = resolveVersion(templateCode, request.versionNo());

        FormTemplateEntity entity = templateRepository.findByTemplateCodeAndVersionNo(templateCode, versionNo)
                .orElseGet(FormTemplateEntity::new);

        entity.setTemplateCode(templateCode);
        entity.setTemplateName(request.templateName().trim());
        entity.setBusinessType(request.businessType().trim());
        entity.setVersionNo(versionNo);
        entity.setFormJson(request.formJson().trim());
        entity.setEnabled(request.enabled() == null ? true : request.enabled());
        entity.setUpdatedAt(now);
        if (entity.getCreatedAt() == null) {
            entity.setCreatedAt(now);
        }

        return FormTemplateRecord.from(templateRepository.save(entity));
    }

    public List<FormFieldMappingRecord> listMappings(String templateCode) {
        return mappingRepository.findByTemplateCodeOrderByIdAsc(templateCode).stream()
                .map(FormFieldMappingRecord::from)
                .toList();
    }

    @Transactional
    public List<FormFieldMappingRecord> replaceMappings(String templateCode, List<FormFieldMappingUpsertRequest> mappings) {
        templateRepository.findFirstByTemplateCodeAndEnabledTrueOrderByVersionNoDesc(templateCode)
                .orElseThrow(() -> new FormTemplateNotFoundException("Template not found: " + templateCode));

        LocalDateTime now = LocalDateTime.now();
        mappingRepository.deleteByTemplateCode(templateCode);
        mappingRepository.flush();

        Map<String, FormFieldMappingUpsertRequest> uniqueMappings = new LinkedHashMap<>();
        for (FormFieldMappingUpsertRequest item : mappings) {
            uniqueMappings.put(item.formFieldKey().trim(), item);
        }

        List<FormFieldMappingRecord> savedMappings = uniqueMappings.values().stream().map(item -> {
            FormFieldMappingEntity entity = new FormFieldMappingEntity();
            entity.setTemplateCode(templateCode);
            entity.setFormFieldKey(item.formFieldKey().trim());
            String inputName = item.formInputName() == null || item.formInputName().isBlank()
                    ? item.formFieldKey().trim()
                    : item.formInputName().trim();
            entity.setFormInputName(inputName);
            entity.setOcrFieldKey(item.ocrFieldKey().trim());
            entity.setFieldLabel(item.fieldLabel().trim());
            entity.setValueType(item.valueType() == null ? "TEXT" : item.valueType().trim());
            entity.setRequired(Boolean.TRUE.equals(item.required()));
            entity.setTransformRuleJson(item.transformRuleJson());
            entity.setCreatedAt(now);
            entity.setUpdatedAt(now);
            return FormFieldMappingRecord.from(mappingRepository.save(entity));
        }).toList();

        return savedMappings;
    }

    public FormTemplateRecord resolveTemplateForTask(String taskId, String templateCodeOverride) {
        if (templateCodeOverride != null && !templateCodeOverride.isBlank()) {
            return findTemplateOrLatest(templateCodeOverride.trim(), null);
        }

        OcrTaskEntity task = taskRepository.findByTaskId(taskId)
                .orElseThrow(() -> new TaskNotFoundException(taskId));

        if (task.getTemplateCode() == null || task.getTemplateCode().isBlank()) {
            throw new IllegalArgumentException("Task has no templateCode and no templateCode query parameter provided.");
        }

        return findTemplateOrLatest(task.getTemplateCode(), null);
    }

    private int resolveVersion(String templateCode, Integer versionNo) {
        if (versionNo != null) {
            return versionNo;
        }
        Integer existedCount = templateRepository.countByTemplateCode(templateCode);
        if (existedCount == null || existedCount == 0) {
            return 1;
        }

        Integer latestVersion = templateRepository.findByTemplateCodeOrderByVersionNoDesc(templateCode)
                .stream()
                .findFirst()
                .map(FormTemplateEntity::getVersionNo)
                .orElse(0);

        return latestVersion == null ? 1 : latestVersion + 1;
    }

    private void validateFormJson(String formJson) {
        try {
            objectMapper.readTree(formJson);
        } catch (IOException ex) {
            throw new IllegalArgumentException("Invalid formJson: " + ex.getMessage());
        }
    }
}
