package com.huimv.employment.controller.mp;

import com.huimv.employment.common.exception.BizException;
import com.huimv.employment.common.exception.ErrorCode;
import com.huimv.employment.common.web.R;
import com.huimv.employment.integration.kb.SseStreamSupport;
import com.huimv.employment.security.LoginUser;
import com.huimv.employment.security.LoginUserHolder;
import com.huimv.employment.service.conversation.ConversationChatService;
import com.huimv.employment.service.conversation.ConversationMessageService;
import com.huimv.employment.service.conversation.ConversationService;
import com.huimv.employment.service.conversation.dto.ConversationChatRequest;
import com.huimv.employment.service.conversation.dto.ConversationCreateRequest;
import com.huimv.employment.service.conversation.dto.ConversationMessagePageResponse;
import com.huimv.employment.service.conversation.dto.ConversationSummaryResponse;
import com.huimv.employment.service.draft.DraftService;
import com.huimv.employment.service.draft.dto.DraftWithCostComparisonResponse;
import com.huimv.employment.service.registration.RegistrationBatchService;
import com.huimv.employment.service.registration.dto.RegistrationBatchDetailResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * 小程序 AI 对话会话接口。
 * <p>
 * 路径 {@code /api/v1/mp/conversations}，需 JWT 鉴权。
 * 从 JWT 解析 {@code userId}，仅返回当前用户自己的会话，不支持跨用户查询。
 * </p>
 * <p>
 * 推荐流程：{@code POST /conversations} 创建会话 → {@code POST /conversations/{id}/chat} 多轮对话（服务端落库）。
 * 如需直连上游网关，仍可使用 {@code POST /ai/console/chat} 纯透传接口。
 * </p>
 */
@RestController
@RequestMapping("/api/v1/mp/conversations")
@Tag(name = "AI 会话", description = "对话会话管理，需 JWT 鉴权")
@SecurityRequirement(name = "Authorization")
public class ConversationController {

    private static final String USER_TYPE_ENTERPRISE = "enterprise";
    private static final String HEADER_AGENT_ID = "X-Agent-Id";

    private final ConversationService conversationService;
    private final ConversationChatService conversationChatService;
    private final ConversationMessageService conversationMessageService;
    private final DraftService draftService;
    private final RegistrationBatchService registrationBatchService;

    public ConversationController(ConversationService conversationService,
                                  ConversationChatService conversationChatService,
                                  ConversationMessageService conversationMessageService,
                                  DraftService draftService,
                                  RegistrationBatchService registrationBatchService) {
        this.conversationService = conversationService;
        this.conversationChatService = conversationChatService;
        this.conversationMessageService = conversationMessageService;
        this.draftService = draftService;
        this.registrationBatchService = registrationBatchService;
    }

    /**
     * 查询当前登录用户的全部 AI 对话会话。
     */
    @GetMapping
    @Operation(summary = "查询当前用户的 AI 对话会话列表",
            description = "按 last_message_at 倒序返回。可选按 status、chatType 筛选。会话归属用户，不按企业筛选。")
    public R<List<ConversationSummaryResponse>> list(
            @RequestParam(required = false) String status,
            @RequestParam(required = false) String chatType) {
        LoginUser loginUser = LoginUserHolder.require();
        return R.ok(conversationService.listByUser(
                loginUser.getUserId(), status, chatType));
    }

    /**
     * 新建 AI 对话会话。
     * <p>返回的 {@code conversationNo} 即上游 AI 网关的 {@code session_id}。</p>
     */
    @PostMapping
    @Operation(summary = "新建 AI 对话会话",
            description = "创建新会话（归属当前用户，不绑定企业）。conversationNo 不传时服务端自动生成；"
                    + "chatType 默认 employment。")
    public R<ConversationSummaryResponse> create(@Validated @RequestBody(required = false) ConversationCreateRequest request) {
        LoginUser loginUser = LoginUserHolder.require();
        return R.ok(conversationService.create(loginUser.getUserId(), request));
    }

    /**
     * 查询指定会话下的消息列表（分页，时间正序）。
     */
    @GetMapping("/{id}/messages")
    @Operation(summary = "查询会话消息列表",
            description = "按 create_time 正序分页返回。仅可查询当前用户自己的会话；已归档会话仍可查看历史。")
    public R<ConversationMessagePageResponse> listMessages(
            @PathVariable("id") Long conversationId,
            @RequestParam(required = false, defaultValue = "1") Integer page,
            @RequestParam(required = false, defaultValue = "50") Integer size) {
        LoginUser loginUser = LoginUserHolder.require();
        return R.ok(conversationMessageService.listByConversation(
                loginUser.getUserId(), conversationId, page, size));
    }

    /**
     * 查询指定会话下全部草稿的成本测算（方案对比）。
     */
    @GetMapping("/{id}/drafts/cost-comparison")
    @Operation(summary = "按会话查询全部草稿及成本测算",
            description = "返回该会话下全部草稿信息；每条草稿内嵌 cost_comparison，"
                    + "结构与 GET /api/v1/mp/drafts/{id}/cost-comparison 一致，"
                    + "支持 daily/weekly/monthly 三种结算单位测算。"
                    + "无法测算时 cost_comparison 为 null。仅可查询本企业用户自己的会话。")
    public R<List<DraftWithCostComparisonResponse>> listDraftCostComparisons(
            @PathVariable("id") Long conversationId) {
        LoginUser loginUser = requireEnterprise();
        return R.ok(draftService.listCostComparisonsByConversation(
                loginUser.getUserId(), conversationId));
    }

    /**
     * 查询指定会话下全部草稿的登记进度。
     */
    @GetMapping("/{id}/drafts/registration-progress")
    @Operation(summary = "按会话查询全部草稿登记进度",
            description = "返回该会话下已确认方案并建有登记批次的草稿进度列表；"
                    + "每个元素结构与 GET /api/v1/mp/drafts/{id}/workflow-progress 一致。"
                    + "尚未确认方案、无批次的草稿自动跳过。"
                    + "仅可查询当前用户自己的会话。")
    public R<List<RegistrationBatchDetailResponse>> listDraftRegistrationProgress(
            @PathVariable("id") Long conversationId) {
        LoginUser loginUser = requireEnterprise();
        return R.ok(registrationBatchService.listDetailsByConversation(
                loginUser.getUserId(), conversationId));
    }

    /**
     * 在已有会话内发起 AI 对话（SSE 流式，流结束后落库）。
     */
    @PostMapping(value = "/{id}/chat", consumes = MediaType.APPLICATION_JSON_VALUE)
    @Operation(summary = "会话内 AI 对话（SSE 流式 + 落库）",
            description = "需先 POST /conversations 创建会话。请求体仅需 message 字段；"
                    + "服务端组装上游 console/chat 请求，流式开始前写入 user 消息；"
                    + "SSE 最后一条消息：若含 MCP 工具（MCP__generate_initial_draft / MCP__update_draft）响应"
                    + "且 draft_id 对应草稿 missing_fields 为空，则注入 next_action=show_employment_draft"
                    + "与 related_draft_id（不注入 card_payload），并同步落库。可选请求头 X-Agent-Id。")
    public void chat(@PathVariable("id") Long conversationId,
                     @Validated @RequestBody ConversationChatRequest request,
                     HttpServletResponse response,
                     @RequestHeader(value = HEADER_AGENT_ID, required = false) String agentId) throws IOException {
        LoginUser loginUser = requireEnterprise();
        SseStreamSupport.prepareSseResponse(response);
        conversationChatService.chat(
                loginUser.getUserId(), conversationId, request, agentId, response.getOutputStream());
        response.flushBuffer();
    }

    private static LoginUser requireEnterprise() {
        LoginUser loginUser = LoginUserHolder.require();
        if (!USER_TYPE_ENTERPRISE.equals(loginUser.getUserType())) {
            throw new BizException(ErrorCode.ENTERPRISE_ONLY);
        }
        return loginUser;
    }
}
