package com.ruoyi.ccdi.project.service.impl;

import com.ruoyi.ccdi.project.domain.dto.CcdiRelationGraphQueryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiRelationGraphSuspectedEnterpriseQueryDTO;
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphEdgeVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphNodeVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphSuspectedEnterpriseItemVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphSuspectedEnterpriseVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiRelationGraphVO;
import com.ruoyi.ccdi.project.mapper.CcdiRelationGraphMapper;
import com.ruoyi.ccdi.project.service.ICcdiRelationGraphService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 关系图谱Service实现
 */
@Service
public class CcdiRelationGraphServiceImpl implements ICcdiRelationGraphService {

    private static final int DEFAULT_LIMIT = 80;
    private static final int MAX_LIMIT = 200;
    private static final int DEFAULT_SUSPECTED_LIMIT = 10;
    private static final int MAX_SUSPECTED_LIMIT = 20;
    private static final int SAME_NAME_BLOCK_THRESHOLD = 20;
    private static final String NODE_PREFIX = "rel_node/";
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Resource
    private CcdiRelationGraphMapper relationGraphMapper;

    @Override
    public List<CcdiRelationGraphNodeVO> searchSubjects(CcdiRelationGraphQueryDTO queryDTO) {
        CcdiRelationGraphQueryDTO query = normalizeGraphQuery(queryDTO);
        if (isBlank(query.getKeyword()) && isBlank(query.getObjectKey())) {
            return Collections.emptyList();
        }
        return relationGraphMapper.selectRelationGraphSubjects(query);
    }

    @Override
    public CcdiRelationGraphVO getRelationGraph(CcdiRelationGraphQueryDTO queryDTO) {
        CcdiRelationGraphQueryDTO query = normalizeGraphQuery(queryDTO);
        CcdiRelationGraphNodeVO centerNode = resolveCenterNode(query);
        if (centerNode == null || isBlank(centerNode.getObjectKey())) {
            return new CcdiRelationGraphVO();
        }

        query.setObjectKey(centerNode.getObjectKey());
        List<CcdiRelationGraphEdgeVO> edges = relationGraphMapper.selectRelationGraphEdges(query);
        if (edges == null) {
            edges = Collections.emptyList();
        }

        List<CcdiRelationGraphNodeVO> nodes = buildNodes(centerNode, edges);
        CcdiRelationGraphVO graph = new CcdiRelationGraphVO();
        graph.setCenterNode(centerNode);
        graph.setNodes(nodes);
        graph.setEdges(edges);
        graph.setEdgeCount((long) edges.size());
        graph.setMaxDepth(1);
        return graph;
    }

    @Override
    public CcdiRelationGraphSuspectedEnterpriseVO getSuspectedEnterprises(CcdiRelationGraphSuspectedEnterpriseQueryDTO queryDTO) {
        CcdiRelationGraphSuspectedEnterpriseVO result = new CcdiRelationGraphSuspectedEnterpriseVO();
        CcdiRelationGraphSuspectedEnterpriseQueryDTO query = normalizeSuspectedEnterpriseQuery(queryDTO);
        if (isBlank(query.getPersonName())) {
            result.setMessage("缺少人员姓名，无法按工商同名主体召回");
            return result;
        }

        int sameNameKeyNoCount = relationGraphMapper.countSuspectedEnterpriseKeyNos(query.getPersonName());
        result.setSameNameKeyNoCount(sameNameKeyNoCount);
        if (sameNameKeyNoCount > SAME_NAME_BLOCK_THRESHOLD) {
            result.setBlocked(true);
            result.setMessage("同名工商主体过多，请结合交易对手企业名称或其他线索进一步筛选");
            return result;
        }

        LocalDate birthDate = resolveBirthDate(query);
        List<CcdiRelationGraphSuspectedEnterpriseItemVO> rows =
                relationGraphMapper.selectSuspectedEnterprises(query.getPersonName(), MAX_SUSPECTED_LIMIT);
        List<CcdiRelationGraphSuspectedEnterpriseItemVO> filteredRows = new ArrayList<>();
        if (rows != null) {
            for (CcdiRelationGraphSuspectedEnterpriseItemVO row : rows) {
                if (row == null) {
                    continue;
                }
                row.setPersonName(query.getPersonName());
                if (applyAgeRule(row, birthDate)) {
                    filteredRows.add(row);
                    if (filteredRows.size() >= query.getLimit()) {
                        break;
                    }
                }
            }
        }

        result.setRows(filteredRows);
        if (filteredRows.isEmpty()) {
            result.setMessage("未发现可展示的疑似同名企业");
        }
        return result;
    }

    private CcdiRelationGraphNodeVO resolveCenterNode(CcdiRelationGraphQueryDTO query) {
        List<CcdiRelationGraphNodeVO> subjects = relationGraphMapper.selectRelationGraphSubjects(query);
        if (subjects == null || subjects.isEmpty()) {
            return null;
        }
        return subjects.get(0);
    }

    private List<CcdiRelationGraphNodeVO> buildNodes(CcdiRelationGraphNodeVO centerNode, List<CcdiRelationGraphEdgeVO> edges) {
        Set<String> objectKeys = new LinkedHashSet<>();
        objectKeys.add(centerNode.getObjectKey());
        for (CcdiRelationGraphEdgeVO edge : edges) {
            edge.setFromObjectKey(toObjectKey(edge.getFromKey()));
            edge.setToObjectKey(toObjectKey(edge.getToKey()));
            if (!isBlank(edge.getFromObjectKey())) {
                objectKeys.add(edge.getFromObjectKey());
            }
            if (!isBlank(edge.getToObjectKey())) {
                objectKeys.add(edge.getToObjectKey());
            }
        }

        List<CcdiRelationGraphNodeVO> rawNodes = relationGraphMapper.selectRelationGraphNodesByKeys(new ArrayList<>(objectKeys));
        Map<String, CcdiRelationGraphNodeVO> nodeMap = new LinkedHashMap<>();
        if (rawNodes != null) {
            for (CcdiRelationGraphNodeVO node : rawNodes) {
                enrichNode(node, centerNode);
                nodeMap.put(node.getObjectKey(), node);
            }
        }
        if (!nodeMap.containsKey(centerNode.getObjectKey())) {
            enrichNode(centerNode, centerNode);
            nodeMap.put(centerNode.getObjectKey(), centerNode);
        }

        for (CcdiRelationGraphEdgeVO edge : edges) {
            CcdiRelationGraphNodeVO fromNode = nodeMap.get(edge.getFromObjectKey());
            CcdiRelationGraphNodeVO toNode = nodeMap.get(edge.getToObjectKey());
            if (fromNode != null) {
                edge.setFromName(fromNode.getNodeName());
            }
            if (toNode != null) {
                edge.setToName(toNode.getNodeName());
            }
        }
        return List.copyOf(nodeMap.values());
    }

    private void enrichNode(CcdiRelationGraphNodeVO node, CcdiRelationGraphNodeVO centerNode) {
        if (node == null) {
            return;
        }
        node.setNodeKey(NODE_PREFIX + node.getObjectKey());
        node.setCanExpand(true);
        node.setDepth(node.getObjectKey() != null && node.getObjectKey().equals(centerNode.getObjectKey()) ? 0 : 1);
    }

    private CcdiRelationGraphQueryDTO normalizeGraphQuery(CcdiRelationGraphQueryDTO queryDTO) {
        CcdiRelationGraphQueryDTO query = queryDTO == null ? new CcdiRelationGraphQueryDTO() : queryDTO;
        query.setKeyword(normalizeText(query.getKeyword()));
        query.setObjectKey(normalizeText(query.getObjectKey()));
        query.setLimit(normalizeLimit(query.getLimit()));
        query.setDepth(1);
        return query;
    }

    private CcdiRelationGraphSuspectedEnterpriseQueryDTO normalizeSuspectedEnterpriseQuery(CcdiRelationGraphSuspectedEnterpriseQueryDTO queryDTO) {
        CcdiRelationGraphSuspectedEnterpriseQueryDTO query =
                queryDTO == null ? new CcdiRelationGraphSuspectedEnterpriseQueryDTO() : queryDTO;
        query.setPersonName(normalizeText(query.getPersonName()));
        query.setCertNo(normalizeText(query.getCertNo()));
        query.setBirthDate(normalizeText(query.getBirthDate()));
        query.setLimit(normalizeSuspectedLimit(query.getLimit()));
        return query;
    }

    private Integer normalizeSuspectedLimit(Integer limit) {
        if (limit == null || limit <= 0) {
            return DEFAULT_SUSPECTED_LIMIT;
        }
        return Math.min(limit, MAX_SUSPECTED_LIMIT);
    }

    private LocalDate resolveBirthDate(CcdiRelationGraphSuspectedEnterpriseQueryDTO query) {
        LocalDate explicitBirthDate = parseDate(query.getBirthDate());
        if (explicitBirthDate != null) {
            return explicitBirthDate;
        }
        return parseBirthDateFromCertNo(query.getCertNo());
    }

    private boolean applyAgeRule(CcdiRelationGraphSuspectedEnterpriseItemVO row, LocalDate birthDate) {
        LocalDate establishDate = parseDate(row.getEstablishDate());
        if (birthDate == null || establishDate == null) {
            row.setMatchReason("姓名一致；企业成立日期或出生日期缺失，年龄无法判断");
            return true;
        }
        int age = Period.between(birthDate, establishDate).getYears();
        row.setAgeAtEstablish(age);
        if (age < 18) {
            return false;
        }
        row.setMatchReason("姓名一致；成立时年龄" + age + "岁");
        return true;
    }

    private LocalDate parseBirthDateFromCertNo(String certNo) {
        if (isBlank(certNo)) {
            return null;
        }
        String value = certNo.trim();
        if (value.matches("^\\d{17}[0-9Xx]$")) {
            return parseCompactDate(value.substring(6, 14));
        }
        if (value.matches("^\\d{15}$")) {
            return parseCompactDate("19" + value.substring(6, 12));
        }
        return null;
    }

    private LocalDate parseCompactDate(String value) {
        try {
            return LocalDate.parse(value, DateTimeFormatter.BASIC_ISO_DATE);
        } catch (DateTimeParseException ignored) {
            return null;
        }
    }

    private LocalDate parseDate(String value) {
        if (isBlank(value)) {
            return null;
        }
        try {
            return LocalDate.parse(value.trim(), DATE_FORMATTER);
        } catch (DateTimeParseException ignored) {
            return null;
        }
    }

    private Integer normalizeLimit(Integer limit) {
        if (limit == null || limit <= 0) {
            return DEFAULT_LIMIT;
        }
        return Math.min(limit, MAX_LIMIT);
    }

    private String toObjectKey(String nodeKey) {
        if (isBlank(nodeKey)) {
            return null;
        }
        return nodeKey.startsWith(NODE_PREFIX) ? nodeKey.substring(NODE_PREFIX.length()) : nodeKey;
    }

    private String normalizeText(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }

    private boolean isBlank(String value) {
        return value == null || value.trim().isEmpty();
    }
}
