package com.zcloud.personnel.positioning.command;

import com.alibaba.cola.exception.BizException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zcloud.personnel.positioning.integration.finds.FindsOpenApiClient;
import com.zcloud.personnel.positioning.persistence.dataobject.AlarmRuleDO;
import com.zcloud.personnel.positioning.persistence.dataobject.FenceDO;
import com.zcloud.personnel.positioning.persistence.dataobject.OpenapiConfigDO;
import com.zcloud.personnel.positioning.persistence.dataobject.TerminalDO;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.LinkedHashMap;
import java.util.Map;

@Component
public class FindsEntityLookupService {
    private static final String TERMINAL_LIST_API = "finds.terminal.list";
    private static final String FENCE_LIST_API = "finds.fence.list.v2";
    private static final String RULE_LIST_API = "finds.alarm.rule.list";
    private static final int PAGE_SIZE = 100;
    private static final int MAX_FENCE_PAGES = 20;
    private static final int MAX_RULE_PAGES = 20;

    private final FindsOpenApiClient findsOpenApiClient;
    private final ObjectMapper objectMapper;

    public FindsEntityLookupService(FindsOpenApiClient findsOpenApiClient, ObjectMapper objectMapper) {
        this.findsOpenApiClient = findsOpenApiClient;
        this.objectMapper = objectMapper;
    }

    public Long findTerminalId(OpenapiConfigDO config, TerminalDO terminal) {
        if (terminal == null || !StringUtils.hasText(terminal.getTerminalNo())) {
            return null;
        }
        Map<String, Object> request = new LinkedHashMap<>();
        request.put("terminalNo", terminal.getTerminalNo());
        request.put("pageNo", 1);
        request.put("pageSize", PAGE_SIZE);
        JsonNode rows = listRows(config, TERMINAL_LIST_API, request);
        Long onlyMatch = null;
        int matchCount = 0;
        for (JsonNode row : rows) {
            String terminalNo = firstText(row, "terminalNo", "no", "terminalCode", "imei");
            if (!terminal.getTerminalNo().equals(terminalNo)) {
                continue;
            }
            Long id = firstLong(row, "id", "terminalId", "findsTerminalId");
            if (id != null) {
                onlyMatch = id;
                matchCount++;
            }
        }
        return matchCount == 1 ? onlyMatch : null;
    }

    public Long findFenceId(OpenapiConfigDO config, FenceDO fence) {
        if (fence == null || !StringUtils.hasText(fence.getFenceName())) {
            return null;
        }
        Long onlyMatch = null;
        for (int pageNo = 1; pageNo <= MAX_FENCE_PAGES; pageNo++) {
            Map<String, Object> request = new LinkedHashMap<>();
            request.put("nameLike", fence.getFenceName());
            request.put("pageNo", pageNo);
            request.put("pageSize", PAGE_SIZE);
            JsonNode rows = listRows(config, FENCE_LIST_API, request);
            for (JsonNode row : rows) {
                if (!fence.getFenceName().equals(text(row.get("name")))) {
                    continue;
                }
                Long id = firstLong(row, "id", "fenceId", "findsFenceId");
                if (id != null && onlyMatch != null && !onlyMatch.equals(id)) {
                    return null;
                }
                if (id != null) {
                    onlyMatch = id;
                }
            }
            if (rows.size() < PAGE_SIZE) {
                return onlyMatch;
            }
        }
        return null;
    }

    public Long findAlarmRuleId(OpenapiConfigDO config, AlarmRuleDO rule) {
        if (rule == null || !StringUtils.hasText(rule.getRuleName())) {
            return null;
        }
        Long onlyMatch = null;
        for (int pageNo = 1; pageNo <= MAX_RULE_PAGES; pageNo++) {
            Map<String, Object> request = new LinkedHashMap<>();
            request.put("pageNo", pageNo);
            request.put("pageSize", PAGE_SIZE);
            if (StringUtils.hasText(rule.getAlarmCode())) {
                request.put("alarmCode", rule.getAlarmCode());
            }
            JsonNode rows = listRows(config, RULE_LIST_API, request);
            for (JsonNode row : rows) {
                String name = text(row.get("name"));
                String alarmCode = text(row.get("alarmCode"));
                if (rule.getRuleName().equals(name)
                        && (!StringUtils.hasText(rule.getAlarmCode()) || rule.getAlarmCode().equals(alarmCode))) {
                    Long id = firstLong(row, "id", "ruleId", "findsRuleId");
                    if (id != null && onlyMatch != null && !onlyMatch.equals(id)) {
                        return null;
                    }
                    if (id != null) {
                        onlyMatch = id;
                    }
                }
            }
            if (rows.size() < PAGE_SIZE) {
                return onlyMatch;
            }
        }
        return null;
    }

    public boolean isNotFound(JsonNode response) {
        if (response == null) {
            return false;
        }
        String code = response.path("code").asText("");
        if ("404".equals(code) || "40400".equals(code) || "40004".equals(code)) {
            return true;
        }
        return containsNotFound(response.path("msg").asText(""))
                || containsNotFound(response.path("detail").asText(""));
    }

    public boolean isNotFound(Throwable error) {
        Throwable current = error;
        while (current != null) {
            if (containsNotFound(current.getMessage())) {
                return true;
            }
            current = current.getCause();
        }
        return false;
    }

    private JsonNode listRows(OpenapiConfigDO config, String apiName, Map<String, Object> request) {
        JsonNode response = findsOpenApiClient.post(config, apiName, request);
        assertSuccess(response, apiName);
        return extractRows(response.path("data"));
    }

    private JsonNode extractRows(JsonNode data) {
        if (data == null || data.isMissingNode() || data.isNull()) {
            return objectMapper.createArrayNode();
        }
        if (data.isArray()) {
            return data;
        }
        for (String field : new String[]{"data", "rows", "records", "list"}) {
            JsonNode rows = data.get(field);
            if (rows != null && rows.isArray()) {
                return rows;
            }
        }
        JsonNode nested = data.get("data");
        if (nested != null && nested.isObject()) {
            return extractRows(nested);
        }
        return objectMapper.createArrayNode();
    }

    private void assertSuccess(JsonNode response, String apiName) {
        int code = response == null ? -1 : response.path("code").asInt(-1);
        if (code != 200 && code != 0) {
            String message = response == null ? "" : response.path("msg").asText("");
            throw new BizException("FindS查询接口调用失败：" + apiName + "，" + message);
        }
    }

    private String firstText(JsonNode node, String... fields) {
        for (String field : fields) {
            String value = text(node.get(field));
            if (StringUtils.hasText(value)) {
                return value;
            }
        }
        return null;
    }

    private Long firstLong(JsonNode node, String... fields) {
        for (String field : fields) {
            JsonNode value = node.get(field);
            if (value == null || value.isNull()) {
                continue;
            }
            if (value.canConvertToLong()) {
                return value.asLong();
            }
            try {
                return Long.valueOf(value.asText());
            } catch (NumberFormatException ignored) {
                // 当前字段不兼容时继续尝试下一个候选字段。
            }
        }
        return null;
    }

    private String text(JsonNode node) {
        if (node == null || node.isNull() || node.isMissingNode()) {
            return null;
        }
        String value = node.asText();
        return StringUtils.hasText(value) ? value : null;
    }

    private boolean containsNotFound(String message) {
        if (!StringUtils.hasText(message)) {
            return false;
        }
        String lower = message.toLowerCase();
        return lower.contains("not found")
                || lower.contains("not exist")
                || lower.contains("no data")
                || message.contains("不存在")
                || message.contains("未找到")
                || message.contains("无此数据");
    }
}
