package cn.nianxx.thhotel.integrations.ai.superagent.service.impl;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import cn.nianxx.thhotel.integrations.ai.superagent.common.result.SuperAgentOpenApiResult;
import cn.nianxx.thhotel.integrations.ai.superagent.common.result.SuperAgentOpenApiTraceEvent;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;

class SuperAgentOpenApiSseParserTest {

    private final SuperAgentOpenApiSseParser parser = new SuperAgentOpenApiSseParser();

    @Test
    void shouldExtractFinalAiAnswerFromValuesEvent() {
        String sse = """
                event: metadata
                data: {"run_id":"run-debug-001","resolved_profile_id":"profile-debug"}

                event: messages
                data: {"type":"ai","content":"partial answer"}

                event: values
                data: {"messages":[{"type":"human","content":"input"},{"type":"ai","content":"{\\"ai_task_results\\":[{\\"task_type\\":\\"New Booking\\"}]}","response_metadata":{"finish_reason":"stop","model_name":"debug-model"},"usage_metadata":{"input_tokens":11,"output_tokens":7,"total_tokens":18}}]}

                event: end
                data: {}

                """;

        SuperAgentOpenApiResult result = parser.parse("session-debug-001", sse);

        assertThat(result.sessionId()).isEqualTo("session-debug-001");
        assertThat(result.runId()).isEqualTo("run-debug-001");
        assertThat(result.profileId()).isEqualTo("profile-debug");
        assertThat(result.rawAnswer()).isEqualTo("{\"ai_task_results\":[{\"task_type\":\"New Booking\"}]}");
        assertThat(result.modelName()).isEqualTo("debug-model");
        assertThat(result.inputTokens()).isEqualTo(11);
        assertThat(result.outputTokens()).isEqualTo(7);
        assertThat(result.totalTokens()).isEqualTo(18);
        assertThat(result.eventTypes()).containsExactly("metadata", "messages", "values", "end");
    }

    @Test
    void shouldExtractFinalAiAnswerFromMessagesEventWhenStopReasonPresent() {
        String sse = """
                event: metadata
                data: {"run_id":"run-debug-002","resolved_profile_id":"profile-debug"}

                event: messages
                data: {"type":"ai","content":"{\\"ai_task_results\\":[{\\"task_type\\":\\"Update Booking\\"}]}","response_metadata":{"finish_reason":"stop","model_name":"debug-model-from-message"},"usage_metadata":{"input_tokens":21,"output_tokens":13,"total_tokens":34}}

                event: end
                data: {}

                """;

        SuperAgentOpenApiResult result = parser.parse("session-debug-002", sse);

        assertThat(result.runId()).isEqualTo("run-debug-002");
        assertThat(result.rawAnswer()).isEqualTo("{\"ai_task_results\":[{\"task_type\":\"Update Booking\"}]}");
        assertThat(result.modelName()).isEqualTo("debug-model-from-message");
        assertThat(result.inputTokens()).isEqualTo(21);
        assertThat(result.outputTokens()).isEqualTo(13);
        assertThat(result.totalTokens()).isEqualTo(34);
        assertThat(result.eventTypes()).containsExactly("metadata", "messages", "end");
    }

    @Test
    void shouldFallbackToLastAiMessageWhenStreamEndedWithoutStopReason() {
        String sse = """
                event: messages
                data: {"type":"ai","content":"partial answer"}

                event: messages
                data: {"type":"ai","content":"{\\"ai_task_results\\":[{\\"task_type\\":\\"Rooming List\\"}]}","response_metadata":{"model_name":"debug-model-fallback"}}

                event: end
                data: {}

                """;

        SuperAgentOpenApiResult result = parser.parse("session-debug-003", sse);

        assertThat(result.rawAnswer()).isEqualTo("{\"ai_task_results\":[{\"task_type\":\"Rooming List\"}]}");
        assertThat(result.modelName()).isEqualTo("debug-model-fallback");
    }

    @Test
    void shouldFailWhenEndEventMissingEvenIfAiContentExists() {
        String sse = """
                event: messages
                data: {"type":"ai","content":"{\\"ai_task_results\\":[]}","response_metadata":{"finish_reason":"stop"}}

                """;

        assertThatThrownBy(() -> parser.parse("session-debug-004", sse))
                .isInstanceOf(SuperAgentOpenApiException.class)
                .hasMessageContaining("SuperAgent SSE 未收到结束事件。");
    }

    @Test
    void shouldFailWhenStreamEndedWithoutAiContent() {
        String sse = """
                event: metadata
                data: {"run_id":"run-debug-005"}

                event: values
                data: {"messages":[{"type":"human","content":"input"}]}

                event: end
                data: {}

                """;

        assertThatThrownBy(() -> parser.parse("session-debug-005", sse))
                .isInstanceOf(SuperAgentOpenApiException.class)
                .hasMessageContaining("SuperAgent SSE 未找到最终 AI 回答。");
    }

    @Test
    void shouldNotUseMessagesAfterEndAsFinalAnswer() {
        String sse = """
                event: end
                data: {}

                event: messages
                data: {"type":"ai","content":"{\\"ai_task_results\\":[]}","response_metadata":{"finish_reason":"stop"}}

                """;

        assertThatThrownBy(() -> parser.parse("session-debug-006", sse))
                .isInstanceOf(SuperAgentOpenApiException.class)
                .hasMessageContaining("SuperAgent SSE 未找到最终 AI 回答。");
    }

    @Test
    void shouldAcceptEndEventWithoutData() {
        String sse = """
                event: messages
                data: {"type":"ai","content":"{\\"ai_task_results\\":[{\\"task_type\\":\\"Voucher Payment\\"}]}","response_metadata":{"finish_reason":"stop"}}

                event: end

                """;

        SuperAgentOpenApiResult result = parser.parse("session-debug-007", sse);

        assertThat(result.rawAnswer()).isEqualTo("{\"ai_task_results\":[{\"task_type\":\"Voucher Payment\"}]}");
        assertThat(result.eventTypes()).containsExactly("messages", "end");
    }

    @Test
    void shouldSerializeObjectContentAsFinalAnswer() {
        String sse = """
                event: messages
                data: {"type":"ai","content":{"ai_task_results":[{"task_type":"Cancel Booking"}]},"response_metadata":{"finish_reason":"stop"}}

                event: end
                data: {}

                """;

        SuperAgentOpenApiResult result = parser.parse("session-debug-008", sse);

        assertThat(result.rawAnswer()).isEqualTo("{\"ai_task_results\":[{\"task_type\":\"Cancel Booking\"}]}");
    }

    @Test
    void shouldCollectPublicTraceEventsAndKeepFinalAnswer() {
        String sse = """
                event: metadata
                data: {"run_id":"run-debug-trace","resolved_profile_id":"profile-debug"}

                event: trace
                data: {"event":"reasoning.summary","run_id":"run-debug-trace","text":"正在分析邮件和附件。","ts":"2026-07-11T10:00:01Z"}

                event: trace
                data: {"event":"tool.call.started","run_id":"run-debug-trace","message_id":"ai-1","tool_calls":[{"tool_call_id":"tool-1","name":"th_hotel_query_case_context","input":{"summary":"{\\"group_code\\":\\"G001\\"}"}}],"ts":"2026-07-11T10:00:02Z"}

                event: trace
                data: {"event":"tool.call.completed","run_id":"run-debug-trace","tool_call_id":"tool-1","name":"th_hotel_query_case_context","output":{"summary":"matched 1 case"},"ts":"2026-07-11T10:00:03Z"}

                event: values
                data: {"messages":[{"type":"ai","content":"{\\"route_code\\":\\"S10\\"}","response_metadata":{"finish_reason":"stop","model_name":"debug-model"},"usage_metadata":{"input_tokens":31,"output_tokens":5,"total_tokens":36}}]}

                event: end
                data: {}

                """;
        List<SuperAgentOpenApiTraceEvent> emittedTraceEvents = new ArrayList<>();

        SuperAgentOpenApiResult result = parser.parse("session-debug-trace", sse, emittedTraceEvents::add);

        assertThat(result.rawAnswer()).isEqualTo("{\"route_code\":\"S10\"}");
        assertThat(result.eventTypes()).containsExactly("metadata", "trace", "values", "end");
        assertThat(result.traceEvents()).hasSize(3);
        assertThat(emittedTraceEvents).hasSize(3);
        assertThat(result.traceEvents().get(0).event()).isEqualTo("reasoning.summary");
        assertThat(result.traceEvents().get(0).text()).isEqualTo("正在分析邮件和附件。");
        assertThat(result.traceEvents().get(1).toolName()).isEqualTo("th_hotel_query_case_context");
        assertThat(result.traceEvents().get(1).inputSummary()).isEqualTo("{\"group_code\":\"G001\"}");
        assertThat(result.traceEvents().get(2).outputSummary()).isEqualTo("matched 1 case");
    }
}
