package com.yumu.noveltranslator.domain.service;
import com.yumu.noveltranslator.port.dto.translation.SelectionTranslationRequest;
import com.yumu.noveltranslator.port.dto.translation.ReaderTranslateResponse;
import com.yumu.noveltranslator.port.out.TranslationClientPort;
import com.yumu.noveltranslator.domain.service.QuotaService;
import com.yumu.noveltranslator.port.out.TeamTranslationPort;
import com.yumu.noveltranslator.domain.service.TranslationPostProcessingService;
import com.yumu.noveltranslator.port.out.TranslationCachePort;
import com.yumu.noveltranslator.port.dto.translation.ReaderTranslateRequest;
import com.yumu.noveltranslator.port.dto.translation.WebpageTranslateRequest;
import com.yumu.noveltranslator.application.service.RagTranslationApplicationService;
import com.yumu.noveltranslator.port.dto.translation.SelectionTranslateResponse;
import com.yumu.noveltranslator.application.service.TranslationApplicationService;
import com.yumu.noveltranslator.domain.service.EntityConsistencyService;

import com.yumu.noveltranslator.port.dto.common.*;
import com.yumu.noveltranslator.port.dto.collab.*;
import com.yumu.noveltranslator.port.dto.entity.*;
import com.yumu.noveltranslator.port.dto.translation.*;
import com.yumu.noveltranslator.port.dto.subscription.*;
import com.yumu.noveltranslator.port.dto.auth.*;
import com.yumu.noveltranslator.domain.model.User;
import com.yumu.noveltranslator.adapter.out.security.CustomUserDetails;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

/**
 * TranslationService 补充测试
 * 覆盖现有测试未覆盖的分支：readerTranslate 段落为空、
 * readerTranslate 段落翻译失败使用原文兜底、
 * selectionTranslate 翻译返回结果为空、
 * webpageTranslateStream SSE 路径、
 * streamTextTranslate SSE 路径
 */
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@DisplayName("TranslationService 补充测试")
class TranslationServiceExtendedTest {

    @Mock
    private TranslationClientPort translationClient;
    @Mock
    private TranslationCachePort cachePort;
    @Mock
    private RagTranslationApplicationService ragTranslationService;
    @Mock
    private EntityConsistencyService entityConsistencyService;
    @Mock
    private TranslationPostProcessingService postProcessingService;
    @Mock
    private TeamTranslationPort teamTranslationPort;
    @Mock
    private QuotaService quotaService;

    private TranslationApplicationService translationService;

    @BeforeEach
    void setUp() {
        translationService = new TranslationApplicationService(
                translationClient, cachePort, ragTranslationService,
                entityConsistencyService, postProcessingService, teamTranslationPort, quotaService);
        when(postProcessingService.fixUntranslatedChinese(anyString(), anyString(), anyString(), anyString()))
                .thenAnswer(invocation -> invocation.getArgument(1));
        SecurityContextHolder.clearContext();
    }

    @AfterEach
    void tearDown() {
        SecurityContextHolder.clearContext();
    }

    private void setAuthenticatedUser(Long userId) {
        User user = new User();
        user.setId(userId);
        user.setUserLevel("free");
        CustomUserDetails userDetails = new CustomUserDetails(user);
        SecurityContextHolder.getContext().setAuthentication(
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()));
    }

    // ============ selectionTranslate 补充分支 ============

    @Nested
    @DisplayName("selectionTranslate - 补充分支")
    class SelectionTranslateExtendedTests {

        @Test
        void 翻译返回空字符串判定失败() {
            // 快速模式 (默认) 使用 executeFast，失败时返回原文
            // 测试需要验证空结果行为：直接模式走 executeFast，返回原文
            when(cachePort.getCacheByMode(anyString(), anyString())).thenReturn(Optional.empty());
            when(translationClient.translate(anyString(), anyString(), anyString(), anyBoolean(), anyBoolean(), anyList(), anyString(), anyString()))
                    .thenReturn("{\"code\":200,\"data\":\"\"}");

            SelectionTranslationRequest req = new SelectionTranslationRequest();
            req.setText("Hello");
            req.setSourceLang("en");
            req.setTargetLang("zh");
            SelectionTranslateResponse resp = translationService.selectionTranslate(1L, req);

            // executeFast 返回原文 "Hello"（非空），所以 success=true
            assertTrue(resp.getSuccess());
            assertEquals("Hello", resp.getTranslation());
        }

        @Test
        void 翻译返回null判定失败() {
            when(cachePort.getCacheByMode(anyString(), anyString())).thenReturn(Optional.empty());
            when(translationClient.translate(anyString(), anyString(), anyString(), anyBoolean(), anyBoolean(), anyList(), anyString(), anyString()))
                    .thenReturn(null);

            SelectionTranslationRequest req = new SelectionTranslationRequest();
            req.setText("Hello");
            req.setSourceLang("en");
            req.setTargetLang("zh");
            SelectionTranslateResponse resp = translationService.selectionTranslate(1L, req);

            // executeFast 返回原文 "Hello"
            assertTrue(resp.getSuccess());
            assertEquals("Hello", resp.getTranslation());
        }

        @Test
        void 缓存返回翻译结果() {
            when(cachePort.getCacheByMode(anyString(), anyString())).thenReturn(Optional.of("context-cached"));

            SelectionTranslationRequest req = new SelectionTranslationRequest();
            req.setText("test-text");
            req.setSourceLang("en");
            req.setTargetLang("zh");
            SelectionTranslateResponse resp = translationService.selectionTranslate(1L, req);

            assertTrue(resp.getSuccess());
            assertEquals("context-cached", resp.getTranslation());
        }
    }

    // ============ readerTranslate 补充分支 ============

    @Nested
    @DisplayName("readerTranslate - 补充分支")
    class ReaderTranslateExtendedTests {

        @Test
        void 段落翻译失败使用原文兜底() {
            setAuthenticatedUser(1L);
            User user = new User();
            user.setId(1L);
            user.setUserLevel("free");

            // 缓存全部未命中
            when(cachePort.getCacheByMode(anyString(), anyString())).thenReturn(Optional.empty());
            // 翻译抛出异常 → 管线捕获异常并返回原文
            when(translationClient.translate(anyString(), anyString(), anyString(), anyBoolean(), anyBoolean(), anyList(), anyString(), anyString()))
                    .thenThrow(new RuntimeException("translation error"));

            ReaderTranslateRequest req = new ReaderTranslateRequest();
            req.setContent("Hello world.");
            req.setTargetLang("zh");
            ReaderTranslateResponse resp = translationService.readerTranslate(1L, req);

            // readerTranslate 不应抛出异常，即使翻译失败也应返回原文
            assertNotNull(resp);
            // 验证未调用翻译成功路径（由于抛出异常）
            // 注意：实际行为取决于虚拟线程是否完成，此处仅验证不崩溃
        }

        @Test
        void 阅读器翻译使用指定引擎() {
            setAuthenticatedUser(1L);

            when(cachePort.getCacheByMode(anyString(), anyString())).thenReturn(Optional.empty());
            when(translationClient.translate(anyString(), anyString(), anyString(), anyBoolean(), anyBoolean(), anyList(), anyString(), anyString()))
                    .thenReturn("{\"code\":200,\"data\":\"translated\"}");

            ReaderTranslateRequest req = new ReaderTranslateRequest();
            req.setContent("Hello.");
            req.setTargetLang("ja");
            req.setEngine("deepl");
            ReaderTranslateResponse resp = translationService.readerTranslate(1L, req);

            assertNotNull(resp);
            assertEquals("expert", resp.getEngine());
        }
    }

    // ============ webpageTranslateStream SSE ============

    @Nested
    @DisplayName("webpageTranslateStream - SSE 流式")
    class WebpageTranslateStreamTests {

        @Test
        void 配额不足返回错误SSE() {
            setAuthenticatedUser(1L);
            when(quotaService.tryConsumeChars(anyLong(), anyString(), anyInt(), anyString())).thenReturn(false);

            WebpageTranslateRequest req = new WebpageTranslateRequest();
            req.setTextRegistry(List.of());

            translationService.webpageTranslateStream(1L, req, (event) -> {});

            // SSE 异步发送，无法直接断言内容，但不应抛出异常
        }

        @Test
        void 用户不存在跳过配额检查() {
            setAuthenticatedUser(999L);

            WebpageTranslateRequest req = new WebpageTranslateRequest();
            req.setTextRegistry(List.of());

            translationService.webpageTranslateStream(1L, req, (event) -> {});

        }

        @Test
        void 空textRegistry直接完成() {
            WebpageTranslateRequest req = new WebpageTranslateRequest();
            req.setTextRegistry(new ArrayList<>());

            translationService.webpageTranslateStream(1L, req, (event) -> {});

        }
    }

    // ============ streamTextTranslate SSE ============

    @Nested
    @DisplayName("streamTextTranslate - SSE 流式")
    class StreamTextTranslateTests {

        @Test
        void 空文本直接返回completed() {
            SelectionTranslationRequest req = new SelectionTranslationRequest();
            req.setText("");

            translationService.streamTextTranslate(1L, req, (event) -> {});

        }

        @Test
        void null文本直接返回completed() {
            SelectionTranslationRequest req = new SelectionTranslationRequest();
            req.setText(null);

            translationService.streamTextTranslate(1L, req, (event) -> {});

        }

        @Test
        void 配额不足返回错误SSE() {
            setAuthenticatedUser(1L);
            when(quotaService.tryConsumeChars(anyLong(), anyString(), anyInt(), anyString())).thenReturn(false);

            SelectionTranslationRequest req = new SelectionTranslationRequest();
            req.setText("Hello World");

            translationService.streamTextTranslate(1L, req, (event) -> {});

        }

        @Test
        void 使用指定mode参数() {
            setAuthenticatedUser(1L);
            when(quotaService.tryConsumeChars(anyLong(), anyString(), anyInt(), eq("expert"))).thenReturn(true);

            SelectionTranslationRequest req = new SelectionTranslationRequest();
            req.setText("Hello");
            req.setMode("expert");

            translationService.streamTextTranslate(1L, req, (event) -> {});

        }
    }
}
