package com.infoepoch.pms.agent.domain.care.orchestrator;

import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.streaming.OutputType;
import com.alibaba.cloud.ai.graph.streaming.StreamingOutput;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.infoepoch.pms.agent.common.utils.LogHelper;
import com.infoepoch.pms.agent.domain.care.log.CareTraceLogSupport;
import com.infoepoch.pms.agent.domain.care.model.ActivitySummary;
import com.infoepoch.pms.agent.domain.care.model.CareConversationState;
import com.infoepoch.pms.agent.domain.care.model.CareQuery;
import com.infoepoch.pms.agent.domain.care.model.UserProfileSummary;
import com.infoepoch.pms.agent.domain.care.state.CareConversationStateService;
import com.infoepoch.pms.agent.domain.care.stream.CareStreamingResponseAssembler;
import com.infoepoch.pms.agent.observability.ai.AiComponentType;
import com.infoepoch.pms.agent.observability.ai.AiObservationContext;
import com.infoepoch.pms.agent.observability.ai.AiObservationSupport;
import com.infoepoch.pms.agent.properties.CareAgentProperties;
import com.infoepoch.pms.agent.tool.union.care.CurrentUserProfileTool;
import com.infoepoch.pms.agent.tool.union.care.EligibleActivitiesTool;
import org.springframework.ai.chat.messages.AbstractMessage;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 推荐活动执行器
 */
@Component
public class ActivityRecommendationExecutor {

    private final CurrentUserProfileTool currentUserProfileTool;
    private final EligibleActivitiesTool eligibleActivitiesTool;
    private final CareConversationStateService conversationStateService;
    private final CareStreamingResponseAssembler responseAssembler;
    private final CareAgentProperties properties;
    private final ReactAgent careAgent;
    private final AiObservationSupport observationSupport;

    public ActivityRecommendationExecutor(CurrentUserProfileTool currentUserProfileTool,
                                          EligibleActivitiesTool eligibleActivitiesTool,
                                          CareConversationStateService conversationStateService,
                                          CareStreamingResponseAssembler responseAssembler,
                                          CareAgentProperties properties,
                                          @Qualifier("CareAgent") ReactAgent careAgent,
                                          AiObservationSupport observationSupport) {
        this.currentUserProfileTool = currentUserProfileTool;
        this.eligibleActivitiesTool = eligibleActivitiesTool;
        this.conversationStateService = conversationStateService;
        this.responseAssembler = responseAssembler;
        this.properties = properties;
        this.careAgent = careAgent;
        this.observationSupport = observationSupport;
    }

    /**
     * 执行活动推荐：一次性拉取活动，再在本地裁剪候选后交给模型匹配输出。
     */
    public Flux<String> execute(String traceId, String sessionId, CareQuery query, CareConversationState state) {
        LogHelper.info(this, CareTraceLogSupport.format(
                traceId,
                sessionId,
                "活动推荐开始",
                "目标数量=%s，是否续轮=%s，历史已下发=%s".formatted(
                        query.requestedCount(),
                        query.continuation(),
                        state == null ? 0 : state.deliveredIds().size()
                )
        ));
        UserProfileSummary currentUser = currentUserProfileTool.execute(traceId, sessionId);
        Set<String> deliveredIds = state == null ? Set.of() : state.deliveredIds();
        List<ActivitySummary> allActivities = eligibleActivitiesTool.execute(traceId, sessionId);
        List<ActivitySummary> candidates = new ArrayList<>();

        int candidateLimit = Math.min(
                Math.max(properties.getActivityCandidateLimit(), query.requestedCount() * 2),
                120
        );

        for (ActivitySummary activity : allActivities) {
            if (!StringUtils.hasText(activity.activityId())) {
                continue;
            }
            if (deliveredIds.contains(activity.activityId())) {
                continue;
            }
            candidates.add(activity);
            if (candidates.size() >= candidateLimit) {
                break;
            }
        }
        int deliveredCount = Math.min(candidates.size(), query.requestedCount());
        List<ActivitySummary> promptCandidates = candidates.subList(0, deliveredCount);
        Set<String> displayedActivityIds = collectActivityIds(promptCandidates);

        LogHelper.info(this, CareTraceLogSupport.format(
                traceId,
                sessionId,
                "活动候选已加载",
                "活动总数=%s，候选上限=%s，候选数量=%s，实际发送给模型=%s，用户=%s".formatted(
                        allActivities.size(),
                        candidateLimit,
                        candidates.size(),
                        promptCandidates.size(),
                        CareTraceLogSupport.safeText(currentUser.userName())
                )
        ));

        if (candidates.isEmpty()) {
            LogHelper.info(this, CareTraceLogSupport.format(
                    traceId,
                    sessionId,
                    "活动推荐完成",
                    "结束原因=无候选活动"
            ));
            return Flux.just(responseAssembler.buildNoActivityTemplate(query));
        }

        String prompt = buildPrompt(currentUser, query, promptCandidates);
        RunnableConfig runnableConfig = buildRunnableConfig(traceId, sessionId, "活动推荐");
        try {
            AtomicInteger chunkIndex = new AtomicInteger(0);
            return Flux.concat(
                            Flux.just(responseAssembler.buildActivityIntro(query)),
                            careAgent.stream(prompt, runnableConfig)
                                    .filter(StreamingOutput.class::isInstance)
                                    .map(StreamingOutput.class::cast)
                                    .filter(output -> output.getOutputType() == OutputType.AGENT_MODEL_STREAMING)
                                    .map(StreamingOutput::message)
                                    .filter(AssistantMessage.class::isInstance)
                                    .map(AssistantMessage.class::cast)
                                    .map(AbstractMessage::getText)
                                    .filter(Objects::nonNull)
                                    .doOnNext(chunk -> chunkIndex.incrementAndGet()),
                            Flux.just(responseAssembler.buildActivityClosing(deliveredCount, query.requestedCount()))
                    )
                    .doOnComplete(() -> {
                        conversationStateService.save(resolveNextState(sessionId, query, state, displayedActivityIds));
                        LogHelper.info(this, CareTraceLogSupport.format(
                                traceId,
                                sessionId,
                                "活动推荐完成",
                                "候选数量=%s，本轮新增已下发=%s，累计已下发=%s".formatted(
                                        promptCandidates.size(),
                                        displayedActivityIds.size(),
                                        deliveredIds.size() + displayedActivityIds.size()
                                )
                        ));
                    })
                    .doOnError(error -> LogHelper.error(this, CareTraceLogSupport.format(
                            traceId,
                            sessionId,
                            "流式请求异常",
                            "活动推荐失败，异常=" + CareTraceLogSupport.safeText(error.getMessage()) +
                                    "，已接收chunk数=%s".formatted(chunkIndex.get())
                    ), error));
        } catch (Exception e) {
            LogHelper.error(this, CareTraceLogSupport.format(
                    traceId,
                    sessionId,
                    "流式请求异常",
                    "活动推荐立即失败，异常=" + CareTraceLogSupport.safeText(e.getMessage())
            ), e);
            return Flux.error(new IllegalArgumentException("调用careAgent异常"));
        }
    }

    private RunnableConfig buildRunnableConfig(String traceId, String sessionId, String scene) {
        AiObservationContext currentContext = observationSupport.currentContext();
        String requestId = StringUtils.hasText(currentContext.requestId()) ? currentContext.requestId() : traceId;
        String flowId = StringUtils.hasText(currentContext.flowId()) ? currentContext.flowId() : requestId;
        AiObservationContext context = currentContext.toBuilder()
                .traceId(traceId)
                .requestId(requestId)
                .sessionId(sessionId)
                .flowId(flowId)
                .scene(scene)
                .componentType(AiComponentType.AGENT)
                .componentName("careAgent")
                .agentName("careAgent")
                .build();
        return observationSupport.buildRunnableConfig(sessionId, context);
    }

    /**
     * 生成下一轮会话状态，记录已下发活动。
     */
    private CareConversationState resolveNextState(String sessionId,
                                                   CareQuery query,
                                                   CareConversationState state,
                                                   Set<String> deliveredIds) {
        CareConversationState currentState = state == null
                ? CareConversationState.initial(sessionId, query)
                : rebuildStateForActivityRecommendation(state, query);
        return currentState.advance(1, deliveredIds);
    }

    /**
     * 为活动推荐重建下一轮状态，同任务场景下保留历史已下发活动。
     */
    private CareConversationState rebuildStateForActivityRecommendation(CareConversationState state, CareQuery query) {
        if (state.taskType() != query.taskType()) {
            return CareConversationState.initial(state.sessionId(), query);
        }
        return new CareConversationState(
                state.sessionId(),
                query.taskType(),
                query.requestedCount(),
                query.activityIntent(),
                query.preferenceSummary(),
                query.searchConditions(),
                1,
                state.deliveredIds(),
                null
        );
    }

    /**
     * 提取本轮实际视为已交付的活动 ID。
     */
    private Set<String> collectActivityIds(List<ActivitySummary> activities) {
        LinkedHashSet<String> ids = new LinkedHashSet<>();
        for (ActivitySummary activity : activities) {
            if (StringUtils.hasText(activity.activityId())) {
                ids.add(activity.activityId());
            }
        }
        return ids;
    }

    /**
     * 构造活动推荐提示词，把用户画像和活动候选压缩成模型可消费的文本。
     */
    private String buildPrompt(UserProfileSummary currentUser, CareQuery query, List<ActivitySummary> candidates) {
        StringBuilder builder = new StringBuilder();
        builder.append("""
                你正在执行精准关爱智能体的技能1：推荐合适的活动。
                请严格按照以下顺序思考：
                1. 先理解用户个人信息
                2. 再查看活动候选
                3. 从候选中挑选最匹配的活动

                只输出推荐条目，不要输出标题、开场、结尾或解释说明。
                输出必须严格使用以下 Markdown 模板，每个活动一块：
                1. **活动名称**
                   匹配理由：...
                   活动类型：...
                   时间地点：...
                   补充说明：...

                要求：
                - 只推荐真实候选活动
                - 优先依据用户年龄、兴趣、标签、区域和偏好描述进行匹配
                - 每个活动必须有一句简短、真实、明确的匹配理由
                - 活动类型、时间地点、补充说明没有合适信息时可以省略对应行
                - 不要输出整段散文，不要输出JSON，不要提技术实现

                你需要推荐的数量：""")
                .append(query.requestedCount())
                .append('\n');
        builder.append("用户偏好描述：")
                .append(StringUtils.hasText(query.preferenceSummary()) ? query.preferenceSummary() : "无额外偏好")
                .append("\n\n");
        builder.append("用户信息：\n")
                .append("- 用户ID：").append(currentUser.userId()).append('\n')
                .append("- 姓名：").append(currentUser.userName()).append('\n')
                .append("- 年龄：").append(currentUser.age() == null ? "未知" : currentUser.age()).append('\n')
                .append("- 性别：").append(defaultText(currentUser.gender(), "未知")).append('\n')
                .append("- 区域：").append(defaultText(currentUser.region(), "未知")).append('\n')
                .append("- 兴趣：").append(joinOrDefault(currentUser.interests(), "无")).append('\n')
                .append("- 标签：").append(joinOrDefault(currentUser.tags(), "无")).append('\n')
                .append("- 摘要：").append(defaultText(currentUser.summary(), "无")).append('\n');
        builder.append("\n活动候选：\n");
        int index = 1;
        for (ActivitySummary activity : candidates) {
            builder.append(index++)
                    .append(". ")
                    .append(activity.activityName())
                    .append("；活动ID=").append(activity.activityId())
                    .append("；类型=").append(defaultText(activity.activityType(), "未提供"))
                    .append("；适合人群=").append(defaultText(activity.suitableFor(), "未提供"))
                    .append("；地点=").append(defaultText(activity.location(), "未提供"))
                    .append("；时间=").append(defaultText(activity.timeWindow(), "未提供"))
                    .append("；标签=").append(joinOrDefault(activity.tags(), "无"))
                    .append("；摘要=").append(defaultText(activity.summary(), "无"))
                    .append('\n');
        }
        return builder.toString();
    }

    /**
     * 将文本字段转成默认文案，避免提示词里出现空串。
     */
    private String defaultText(String value, String defaultValue) {
        return StringUtils.hasText(value) ? value : defaultValue;
    }

    /**
     * 把列表字段拼接为中文顿号分隔文本。
     */
    private String joinOrDefault(List<String> values, String defaultValue) {
        if (CollectionUtils.isEmpty(values)) {
            return defaultValue;
        }
        return String.join("、", values);
    }
}
