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

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.CareConversationState;
import com.infoepoch.pms.agent.domain.care.model.CareQuery;
import com.infoepoch.pms.agent.domain.care.model.PagedResult;
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.properties.CareAgentProperties;
import com.infoepoch.pms.agent.tool.union.care.UserSearchTool;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;

/**
 * 推荐用户执行器
 */
@Component
public class UserRecommendationExecutor {

    private final UserSearchTool userSearchTool;
    private final CareConversationStateService conversationStateService;
    private final CareStreamingResponseAssembler responseAssembler;
    private final CareAgentProperties properties;

    public UserRecommendationExecutor(UserSearchTool userSearchTool,
                                      CareConversationStateService conversationStateService,
                                      CareStreamingResponseAssembler responseAssembler,
                                      CareAgentProperties properties) {
        this.userSearchTool = userSearchTool;
        this.conversationStateService = conversationStateService;
        this.responseAssembler = responseAssembler;
        this.properties = properties;
    }

    /**
     * 执行用户推荐：按页拉取、去重、累计并持续输出。
     */
    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(),
                        CareTraceLogSupport.safeConditionsSummary(query.searchConditions())
                )
        ));
        try {
            UserRecommendationPlan plan = prepareUserRecommendationPlan(traceId, sessionId, query, state);
            Duration itemDelay = Duration.ofMillis(Math.max(0, properties.getUserStreamItemDelayMillis()));
            CareConversationState currentState = state == null
                    ? CareConversationState.initial(sessionId, query)
                    : state.rewriteFromQuery(query);
            CareConversationState nextState = currentState.advance(plan.nextPageNo(), plan.deliveredIds());

            return Flux.concat(
                            Flux.just(responseAssembler.buildUserIntro(query)),
                            buildDelayedUserItemsFlux(plan.userChunks(), itemDelay),
                            buildClosingFlux(
                                    responseAssembler.buildUserClosing(plan.emittedCount(), query.requestedCount()),
                                    itemDelay,
                                    !plan.userChunks().isEmpty()
                            )
                    )
                    .doOnComplete(() -> {
                        conversationStateService.save(nextState);
                        LogHelper.info(this, CareTraceLogSupport.format(
                                traceId,
                                sessionId,
                                "用户推荐完成",
                                "输出数量=%s，累计已下发=%s，下一页=%s，结束原因=%s".formatted(
                                        plan.emittedCount(),
                                        plan.deliveredIds().size(),
                                        plan.nextPageNo(),
                                        plan.stopReason()
                                )
                        ));
                    })
                    .doOnError(error -> LogHelper.error(this, CareTraceLogSupport.format(
                            traceId,
                            sessionId,
                            "流式请求异常",
                            "用户推荐失败，异常=" + CareTraceLogSupport.safeText(error.getMessage())
                    ), error));
        } catch (Exception exception) {
            LogHelper.error(this, CareTraceLogSupport.format(
                    traceId,
                    sessionId,
                    "流式请求异常",
                    "用户推荐失败，异常=" + CareTraceLogSupport.safeText(exception.getMessage())
            ), exception);
            return Flux.error(new IllegalArgumentException("用户推荐过程中出现异常"));
        }
    }

    /**
     * 同步完成分页检索与去重，生成本轮输出计划。
     */
    private UserRecommendationPlan prepareUserRecommendationPlan(String traceId,
                                                                 String sessionId,
                                                                 CareQuery query,
                                                                 CareConversationState state) {
        int nextPageNo = state != null && query.continuation() ? state.nextPageNo() : 1;
        LinkedHashSet<String> deliveredIds = state == null
                ? new LinkedHashSet<>()
                : new LinkedHashSet<>(state.deliveredIds());
        List<String> userChunks = new ArrayList<>();
        int emittedCount = 0;
        String stopReason = "正常完成";

        while (emittedCount < query.requestedCount()) {
            PagedResult<UserProfileSummary> page = userSearchTool.execute(
                    traceId,
                    sessionId,
                    query.searchConditions(),
                    nextPageNo,
                    properties.getUserPageSize()
            );
            if (page.records().isEmpty()) {
                LogHelper.info(this, CareTraceLogSupport.format(
                        traceId,
                        sessionId,
                        "用户分页已加载",
                        "页码=%s，每页数量=%s，返回数量=0，总数=%s".formatted(
                                nextPageNo,
                                properties.getUserPageSize(),
                                page.total()
                        )
                ));
                stopReason = "空页结束";
                break;
            }

            List<UserProfileSummary> freshUsers = page.records().stream()
                    .filter(user -> !deliveredIds.contains(user.userId()))
                    .limit(query.requestedCount() - emittedCount)
                    .toList();
            LogHelper.info(this, CareTraceLogSupport.format(
                    traceId,
                    sessionId,
                    "用户分页已加载",
                    "页码=%s，每页数量=%s，返回数量=%s，去重后新增=%s，总数=%s，输出前累计=%s".formatted(
                            nextPageNo,
                            properties.getUserPageSize(),
                            page.records().size(),
                            freshUsers.size(),
                            page.total(),
                            emittedCount
                    )
            ));

            userChunks.addAll(responseAssembler.buildUserItems(freshUsers, emittedCount + 1, query));
            for (UserProfileSummary user : freshUsers) {
                deliveredIds.add(user.userId());
            }

            emittedCount += freshUsers.size();
            nextPageNo++;
            if (emittedCount >= query.requestedCount()) {
                stopReason = "达到目标数量";
                break;
            }
            if (!page.hasNext()) {
                stopReason = "无下一页";
                break;
            }
        }

        return new UserRecommendationPlan(userChunks, emittedCount, nextPageNo, deliveredIds, stopReason);
    }

    /**
     * 将用户推荐条目转换成逐条延迟输出的假流式。
     */
    private Flux<String> buildDelayedUserItemsFlux(List<String> userChunks, Duration itemDelay) {
        if (userChunks.isEmpty()) {
            return Flux.empty();
        }
        if (itemDelay.isZero() || itemDelay.isNegative()) {
            return Flux.fromIterable(userChunks);
        }
        return Flux.fromIterable(userChunks)
                .concatMap(chunk -> Mono.just(chunk).delayElement(itemDelay));
    }

    /**
     * 生成收尾文案，若已输出用户则沿用同样节奏延后显示。
     */
    private Flux<String> buildClosingFlux(String closing, Duration itemDelay, boolean hasUserChunks) {
        if (!hasUserChunks || itemDelay.isZero() || itemDelay.isNegative()) {
            return Flux.just(closing);
        }
        return Mono.just(closing)
                .delaySubscription(itemDelay)
                .flux();
    }

    private record UserRecommendationPlan(
            List<String> userChunks,
            int emittedCount,
            int nextPageNo,
            LinkedHashSet<String> deliveredIds,
            String stopReason
    ) {
    }
}

