package com.infoepoch.pms.agent.tool.union.care.provider;

import com.fasterxml.jackson.databind.JsonNode;
import com.infoepoch.pms.agent.common.utils.LogHelper;
import com.infoepoch.pms.agent.config.JsonUtils;
import com.infoepoch.pms.agent.domain.care.log.CareTraceLogSupport;
import com.infoepoch.pms.agent.domain.care.model.ActivityMatchUserRule;
import com.infoepoch.pms.agent.domain.care.model.ActivitySummary;
import com.infoepoch.pms.agent.domain.care.model.PagedResult;
import com.infoepoch.pms.agent.domain.care.model.UserProfileSummary;
import com.infoepoch.pms.agent.observability.ai.ObservedRestClientFactory;
import com.infoepoch.pms.agent.properties.CareBusinessProperties;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClient;
import org.springframework.web.util.UriBuilder;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;

/**
 * 基于 HTTP 的业务数据访问
 */
@Service
public class HttpCareBusinessDataProvider implements CareBusinessDataProvider {

    private static final int SUCCESS_CODE = 1;

    private final CareBusinessProperties properties;
    private final RestClient restClient;

    public HttpCareBusinessDataProvider(CareBusinessProperties properties,
                                        ObservedRestClientFactory observedRestClientFactory) {
        this.properties = properties;
        this.restClient = observedRestClientFactory.create(
                properties.getBaseUrl(),
                observedRestClientFactory.defaultRequestFactory(),
                "careBusinessProvider"
        );
    }

    /**
     * 调用当前用户接口并裁剪成统一用户摘要。
     */
    @Override
    public UserProfileSummary getCurrentUserProfile(String traceId, String sessionId) {
        String path = requiredPath(properties.getCurrentUserPath(), "当前用户信息接口未配置");
        LogHelper.info(this, CareTraceLogSupport.format(
                traceId,
                sessionId,
                "当前用户接口请求",
                "接口路径=" + path
        ));
        try {
            JsonNode root = postForJson(path, Map.of("sessionId", sessionId), null);
            JsonNode dataNode = extractDataNode(root, path, true);
            UserProfileSummary summary = toCurrentUserProfile(dataNode, sessionId);
            LogHelper.info(this, CareTraceLogSupport.format(
                    traceId,
                    sessionId,
                    "当前用户接口响应",
                    "用户=%s，兴趣数量=%s，标签数量=%s".formatted(
                            CareTraceLogSupport.safeText(summary.userName()),
                            summary.interests().size(),
                            summary.tags().size()
                    )
            ));
            return summary;
        } catch (Exception exception) {
            logToolError(traceId, sessionId, path, "getCurrentUserInfo", exception);
            throw exception;
        }
    }

    /**
     * 调用活动接口并解析成活动列表。
     */
    @Override
    public List<ActivitySummary> searchEligibleActivities(String traceId, String sessionId) {
        String path = requiredPath(properties.getEligibleActivitiesPath(), "活动查询接口未配置");
        LogHelper.info(this, CareTraceLogSupport.format(
                traceId,
                sessionId,
                "活动接口请求",
                "接口路径=" + path
        ));
        try {
            JsonNode root = postForJson(path, Map.of("sessionId", sessionId), null);
            JsonNode dataNode = extractDataNode(root, path, false);
            if (dataNode == null || dataNode.isNull() || !dataNode.isArray()) {
                LogHelper.info(this, CareTraceLogSupport.format(
                        traceId,
                        sessionId,
                        "活动接口响应",
                        "活动数量=0"
                ));
                return List.of();
            }
            List<ActivitySummary> activities = new ArrayList<>();
            for (JsonNode item : dataNode) {
                activities.add(toActivitySummary(item));
            }
            LogHelper.info(this, CareTraceLogSupport.format(
                    traceId,
                    sessionId,
                    "活动接口响应",
                    "活动数量=" + activities.size()
            ));
            return List.copyOf(activities);
        } catch (Exception exception) {
            logToolError(traceId, sessionId, path, "getActivityInfoList", exception);
            throw exception;
        }
    }

    /**
     * 调用活动匹配用户规则接口并解析成规则列表。
     */
    @Override
    public List<ActivityMatchUserRule> listActivityMatchUserRules(String traceId, String sessionId) {
        String path = requiredPath(properties.getActivityMatchUserRulesPath(), "活动匹配用户规则接口未配置");
        LogHelper.info(this, CareTraceLogSupport.format(
                traceId,
                sessionId,
                "活动规则接口请求",
                "接口路径=" + path
        ));
        try {
            JsonNode root = postForJson(path, Map.of(), null);
            JsonNode dataNode = extractDataNode(root, path, false);
            if (dataNode == null || dataNode.isNull() || !dataNode.isArray()) {
                LogHelper.info(this, CareTraceLogSupport.format(
                        traceId,
                        sessionId,
                        "活动规则接口响应",
                        "规则数量=0"
                ));
                return List.of();
            }
            List<ActivityMatchUserRule> rules = new ArrayList<>();
            for (JsonNode item : dataNode) {
                rules.add(toActivityMatchUserRule(item));
            }
            LogHelper.info(this, CareTraceLogSupport.format(
                    traceId,
                    sessionId,
                    "活动规则接口响应",
                    "规则数量=" + rules.size()
            ));
            return List.copyOf(rules);
        } catch (Exception exception) {
            logToolError(traceId, sessionId, path, "getActivityMatchUserRules", exception);
            throw exception;
        }
    }

    /**
     * 调用用户检索接口并解析成分页用户结果。
     */
    @Override
    public PagedResult<UserProfileSummary> searchUsersByConditions(String traceId,
                                                                   String sessionId,
                                                                   Map<String, Object> conditions,
                                                                   int pageNo,
                                                                   int pageSize) {
        String path = requiredPath(properties.getUserSearchPath(), "用户检索接口未配置");
        LogHelper.info(this, CareTraceLogSupport.format(
                traceId,
                sessionId,
                "用户检索接口请求",
                "接口路径=%s，页码=%s，每页数量=%s，筛选条件=%s".formatted(
                        path,
                        pageNo,
                        pageSize,
                        CareTraceLogSupport.safeConditionsSummary(conditions)
                )
        ));
        try {
            JsonNode root = postForJson(path, Map.of(), buildUserSearchBody(sessionId, conditions, pageNo, pageSize));
            JsonNode dataNode = extractDataNode(root, path, false);
            if (dataNode == null || dataNode.isNull() || !dataNode.isObject()) {
                LogHelper.info(this, CareTraceLogSupport.format(
                        traceId,
                        sessionId,
                        "用户检索接口响应",
                        "页码=%s，返回数量=0，总数=0".formatted(pageNo)
                ));
                return new PagedResult<>(List.of(), 0, false);
            }

            long total = readLong(dataNode, "totalCount", 0);
            JsonNode dataListNode = dataNode.get("dataList");
            List<UserProfileSummary> users = new ArrayList<>();
            if (dataListNode != null && dataListNode.isArray()) {
                for (JsonNode item : dataListNode) {
                    users.add(toListedUserProfile(item));
                }
            }
            boolean hasNext = pageNo * (long) pageSize < total;
            LogHelper.info(this, CareTraceLogSupport.format(
                    traceId,
                    sessionId,
                    "用户检索接口响应",
                    "页码=%s，返回数量=%s，总数=%s，是否有下一页=%s".formatted(
                            pageNo,
                            users.size(),
                            total,
                            hasNext
                    )
            ));
            return new PagedResult<>(users, total, hasNext);
        } catch (Exception exception) {
            logToolError(traceId, sessionId, path, "getUserInfoList", exception);
            throw exception;
        }
    }

    /**
     * 执行 HTTP POST 请求并将返回值解析为 JsonNode。
     */
    private JsonNode postForJson(String path, Map<String, Object> queryParams, Object requestBody) {
        RestClient.RequestBodySpec spec = restClient.post()
                .uri(uriBuilder -> buildUri(uriBuilder, path, queryParams))
                .contentType(MediaType.APPLICATION_JSON);
        if (requestBody != null) {
            spec.body(requestBody);
        }
        String response = spec.retrieve().body(String.class);
        if (!StringUtils.hasText(response)) {
            throw new IllegalStateException("业务接口返回为空: " + path);
        }
        JsonNode jsonNode = JsonUtils.jsonToJsonNode(response);
        if (jsonNode == null) {
            throw new IllegalStateException("业务接口返回不是合法JSON: " + path);
        }
        return jsonNode;
    }

    /**
     * 从统一 AgentResponse 结构中读取 data 节点，并校验业务响应码。
     */
    private JsonNode extractDataNode(JsonNode root, String path, boolean requireData) {
        if (root == null || !root.isObject()) {
            throw new IllegalStateException("业务接口返回结构不正确: " + path);
        }
        int code = root.path("code").asInt();
        String msg = root.path("msg").asText();
        if (code != SUCCESS_CODE) {
            throw new IllegalStateException(StringUtils.hasText(msg) ? msg : "业务接口调用失败: " + path);
        }
        JsonNode dataNode = root.get("data");
        if (requireData && (dataNode == null || dataNode.isNull())) {
            throw new IllegalStateException(StringUtils.hasText(msg) ? msg : "业务接口未返回数据: " + path);
        }
        return dataNode;
    }

    /**
     * 构造带查询参数的请求 URI。
     */
    private URI buildUri(UriBuilder uriBuilder, String path, Map<String, Object> params) {
        uriBuilder.path(path);
        params.forEach((key, value) -> {
            if (value != null) {
                uriBuilder.queryParam(key, value);
            }
        });
        return uriBuilder.build();
    }

    /**
     * 校验业务接口路径是否已配置。
     */
    private String requiredPath(String value, String message) {
        if (!StringUtils.hasText(value)) {
            throw new IllegalStateException(message);
        }
        return value.trim();
    }

    /**
     * 构造用户分页检索请求体，只保留接口文档支持的字段。
     */
    private Map<String, Object> buildUserSearchBody(String sessionId,
                                                    Map<String, Object> conditions,
                                                    int pageNo,
                                                    int pageSize) {
        LinkedHashMap<String, Object> body = new LinkedHashMap<>();
        body.put("sessionId", sessionId);
        body.put("pageIndex", pageNo);
        body.put("pageSize", pageSize);
        Map<String, Object> safeConditions = conditions == null ? Map.of() : conditions;
        copyIfPresent(body, safeConditions, "sex");
        copyIfPresent(body, safeConditions, "minAge");
        copyIfPresent(body, safeConditions, "maxAge");
        copyIfPresent(body, safeConditions, "bmi");
        copyIfPresent(body, safeConditions, "hobby");
        copyIfPresent(body, safeConditions, "specialty");
        copyIfPresent(body, safeConditions, "regionName");
        copyIfPresent(body, safeConditions, "departmentName");
        return body;
    }

    /**
     * 将非空条件复制到请求体中。
     */
    private void copyIfPresent(Map<String, Object> target, Map<String, Object> source, String key) {
        Object value = source.get(key);
        if (value == null) {
            return;
        }
        if (value instanceof String text && !StringUtils.hasText(text)) {
            return;
        }
        target.put(key, value);
    }

    /**
     * 将当前用户 JSON 节点裁剪成统一用户摘要。
     */
    private UserProfileSummary toCurrentUserProfile(JsonNode node, String sessionId) {
        return new UserProfileSummary(
                "session:" + sessionId,
                readText(node, "name"),
                readInteger(node, "age"),
                readText(node, "sex"),
                splitInterestNames(node.path("interestInfoList")),
                extractNames(node.path("talentInfoList"), "talentName"),
                readText(node, "departmentName"),
                buildCurrentUserSummary(node)
        );
    }

    /**
     * 将分页用户列表中的 JSON 节点裁剪成统一用户摘要。
     */
    private UserProfileSummary toListedUserProfile(JsonNode node) {
        String mobile = readText(node, "mobile");
        String name = readText(node, "name");
        String departmentName = readText(node, "departmentName");
        return new UserProfileSummary(
                syntheticId("user", name, mobile),
                name,
                readInteger(node, "age"),
                readText(node, "sex"),
                List.of(),
                List.of(),
                departmentName,
                buildListedUserSummary(node)
        );
    }

    /**
     * 将活动 JSON 节点裁剪成统一活动摘要。
     */
    private ActivitySummary toActivitySummary(JsonNode node) {
        String activityName = readText(node, "activityName");
        String startTime = readText(node, "startTime");
        String endTime = readText(node, "endTime");
        String location = readText(node, "location");
        String activityTypeName = readText(node, "activityTypeName");
        return new ActivitySummary(
                syntheticId("activity", activityName, startTime, location),
                activityName,
                activityTypeName,
                "",
                StringUtils.hasText(activityTypeName) ? List.of(activityTypeName) : List.of(),
                location,
                joinWithDelimiter(" - ", startTime, endTime),
                buildActivitySummary(node)
        );
    }

    /**
     * 将活动规则 JSON 节点裁剪成统一规则对象。
     */
    private ActivityMatchUserRule toActivityMatchUserRule(JsonNode node) {
        return new ActivityMatchUserRule(
                readText(node, "activityKey"),
                readText(node, "sex"),
                readInteger(node, "minAge"),
                readInteger(node, "maxAge"),
                readText(node, "bmi"),
                readText(node, "hobby"),
                readText(node, "specialty")
        );
    }

    /**
     * 从数组对象中提取名称列表。
     */
    private List<String> extractNames(JsonNode arrayNode, String fieldName) {
        if (arrayNode == null || arrayNode.isNull() || !arrayNode.isArray()) {
            return List.of();
        }
        List<String> names = new ArrayList<>();
        for (JsonNode item : arrayNode) {
            String name = readText(item, fieldName);
            if (StringUtils.hasText(name)) {
                names.add(name);
            }
        }
        return List.copyOf(names);
    }

    /**
     * 从兴趣数组里提取并拆分逗号拼接的兴趣名称。
     */
    private List<String> splitInterestNames(JsonNode arrayNode) {
        if (arrayNode == null || arrayNode.isNull() || !arrayNode.isArray()) {
            return List.of();
        }
        List<String> interests = new ArrayList<>();
        for (JsonNode item : arrayNode) {
            String text = readText(item, "interestName");
            if (!StringUtils.hasText(text)) {
                continue;
            }
            for (String part : text.replace("，", ",").split(",")) {
                String trimmed = part.trim();
                if (StringUtils.hasText(trimmed)) {
                    interests.add(trimmed);
                }
            }
        }
        return List.copyOf(interests);
    }

    /**
     * 拼接当前用户摘要文本。
     */
    private String buildCurrentUserSummary(JsonNode node) {
        return joinWithDelimiter("；",
                prefixed("身高", readText(node, "height")),
                prefixed("体重", readText(node, "weight")),
                prefixed("BMI", readText(node, "bmi")),
                prefixed("收缩压", readText(node, "systolicBp")),
                prefixed("舒张压", readText(node, "diastolicBp"))
        );
    }

    /**
     * 拼接活动摘要文本。
     */
    private String buildActivitySummary(JsonNode node) {
        return joinWithDelimiter("；",
                readText(node, "activityDescribe"),
                prefixed("主办方", readText(node, "sponsor")),
                prefixed("管理员", readText(node, "activityAdmin")),
                prefixed("规模", readText(node, "scale"))
        );
    }

    /**
     * 拼接用户列表摘要文本。
     */
    private String buildListedUserSummary(JsonNode node) {
        return joinWithDelimiter("；",
                prefixed("手机号", readText(node, "mobile")),
                prefixed("身高", readText(node, "height")),
                prefixed("体重", readText(node, "weight")),
                prefixed("BMI", readText(node, "bmi")),
                prefixed("收缩压", readText(node, "systolicBp")),
                prefixed("舒张压", readText(node, "diastolicBp"))
        );
    }

    /**
     * 记录业务接口异常。
     */
    private void logToolError(String traceId, String sessionId, String path, String apiName, Exception exception) {
        LogHelper.error(this, CareTraceLogSupport.format(
                traceId,
                sessionId,
                "工具调用异常",
                "接口=%s，路径=%s，异常=%s".formatted(
                        apiName,
                        path,
                        CareTraceLogSupport.safeText(exception.getMessage())
                )
        ), exception);
    }

    /**
     * 给原始值增加标签前缀。
     */
    private String prefixed(String label, String value) {
        return StringUtils.hasText(value) ? label + "：" + value : "";
    }

    /**
     * 用指定分隔符拼接非空片段。
     */
    private String joinWithDelimiter(String delimiter, String... parts) {
        StringJoiner joiner = new StringJoiner(delimiter);
        for (String part : parts) {
            if (StringUtils.hasText(part)) {
                joiner.add(part);
            }
        }
        return joiner.toString();
    }

    /**
     * 读取文本字段。
     */
    private String readText(JsonNode node, String fieldName) {
        JsonNode value = node == null ? null : node.get(fieldName);
        if (value != null && !value.isNull() && StringUtils.hasText(value.asText())) {
            return value.asText().trim();
        }
        return "";
    }

    /**
     * 读取整数字段。
     */
    private Integer readInteger(JsonNode node, String fieldName) {
        JsonNode value = node == null ? null : node.get(fieldName);
        if (value != null && value.canConvertToInt()) {
            return value.asInt();
        }
        return null;
    }

    /**
     * 读取 long 字段。
     */
    private long readLong(JsonNode node, String fieldName, long defaultValue) {
        JsonNode value = node == null ? null : node.get(fieldName);
        if (value != null && value.canConvertToLong()) {
            return value.asLong();
        }
        return defaultValue;
    }

    /**
     * 生成稳定的合成业务 ID。
     */
    private String syntheticId(String prefix, String... values) {
        String raw = String.join("|", values);
        if (!StringUtils.hasText(raw.replace("|", "").trim())) {
            raw = prefix + "|empty";
        }
        return prefix + ":" + md5(raw);
    }

    /**
     * 计算字符串的 MD5 摘要，保持 ID 稳定且长度可控。
     */
    private String md5(String value) {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] bytes = digest.digest(value.getBytes(StandardCharsets.UTF_8));
            StringBuilder builder = new StringBuilder();
            for (byte current : bytes) {
                builder.append(String.format("%02x", current));
            }
            return builder.toString();
        } catch (NoSuchAlgorithmException exception) {
            return Integer.toHexString(value.hashCode());
        }
    }
}
