/**
 * AI Stream Resolver - SSE 流式 AI 答疑 (P5).
 *
 * 仲裁依据:
 *   - coord-final-decisions §2 B1 (GraphQL Yoga 原生支持 SSE Subscription)
 *   - coord-final-decisions §2 B2 (gRPC 调用 ai.StreamChat)
 *   - coord-final-decisions §2 B4 (强制自我越权防御)
 *   - workline §5.2 (P5.2 ai gRPC client chat/streamChat SSE)
 *
 * 实现: GraphQL Subscription → AsyncIterator, 透传 ai.StreamChat gRPC 流.
 * GraphQL Yoga 通过 SSE 传输协议将 Subscription 事件推给客户端.
 *
 * 客户端使用:
 *   subscription AiStreamChat($input: AIStreamChatInput!) {
 *     aiStreamChat(input: $input) { content done model usage }
 *   }
 *
 * 传输协议: SSE (text/event-stream), Yoga 自动处理.
 */
import { z } from "zod";
import type { StudentBffContext } from "../../shared/graphql/yoga.js";
import {
  UnauthorizedError,
  ValidationError,
} from "../../shared/errors/application-error.js";

const AIStreamChatInputSchema = z.object({
  messages: z
    .array(
      z.object({
        role: z.enum(["user", "assistant"]),
        content: z.string().min(1).max(8000),
      }),
    )
    .min(1)
    .max(20),
  model: z
    .enum(["gpt-4o-mini", "baichuan-53b", "local-qwen-7b"])
    .default("gpt-4o-mini"),
  context: z
    .object({
      subject: z.string().optional(),
      knowledgePointId: z.string().optional(),
    })
    .optional(),
});

/**
 * AI 流式响应 chunk 结构 (对齐 ai.StreamChat gRPC stream).
 */
export interface AIStreamChunk {
  content: string;
  done: boolean;
  model?: string;
  usage?: {
    promptTokens: number;
    completionTokens: number;
    totalTokens: number;
  };
}

export const aiStreamResolvers = {
  Subscription: {
    /**
     * aiStreamChat: AI 答疑流式响应.
     *
     * 通过 GraphQL Subscription + SSE 传输,
     * 透传 ai.StreamChat gRPC server-streaming RPC.
     *
     * @permission STUDENT_AI_CHAT
     * @dataScope OWN
     */
    aiStreamChat: {
      subscribe(
        _parent: unknown,
        args: { input: unknown },
        ctx: StudentBffContext,
      ): AsyncIterable<{ aiStreamChat: AIStreamChunk }> {
        if (!ctx.userId) {
          throw new UnauthorizedError();
        }

        const parseResult = AIStreamChatInputSchema.safeParse(args.input);
        if (!parseResult.success) {
          throw new ValidationError(
            "Invalid aiStreamChat input",
            parseResult.error.flatten(),
          );
        }
        const input = parseResult.data;
        const userId = ctx.userId;

        return (async function* (): AsyncGenerator<{
          aiStreamChat: AIStreamChunk;
        }> {
          try {
            // 调用 ai.StreamChat (gRPC server-streaming)
            // DownstreamClient.callStream 返回 AsyncIterable
            const stream = ctx.downstream.callStream(
              "ai",
              "StreamChat",
              {
                userId,
                messages: input.messages,
                model: input.model,
                context: input.context,
              },
              {
                traceId: ctx.traceId,
                metadata: { "x-user-id": userId },
                timeoutMs: 60000, // 流式调用 60s 超时
              },
            );

            for await (const chunk of stream) {
              const typed = chunk as {
                content?: string;
                done?: boolean;
                model?: string;
                usage?: {
                  promptTokens: number;
                  completionTokens: number;
                  totalTokens: number;
                };
              };

              yield {
                aiStreamChat: {
                  content: typed.content ?? "",
                  done: typed.done ?? false,
                  model: typed.model,
                  usage: typed.usage,
                },
              };

              if (typed.done) {
                break;
              }
            }
          } catch (err) {
            // 流式错误: 发送一个 done=true 的错误 chunk, 客户端关闭流
            yield {
              aiStreamChat: {
                content: `[stream error] ${(err as Error).message}`,
                done: true,
              },
            };
          }
        })();
      },
    },
  },
};
