/**
 * Content Resolver - textbooks / chapters / learningPath Queries (P4 扩展).
 *
 * 仲裁依据:
 *   - student-bff.schema.graphql Query.textbooks / chapters / learningPath
 *   - coord-final-decisions §2 B2 (gRPC 调用 content)
 *   - president-final-rulings §2.3 (跨阶段扩展例外: 新增下游 gRPC 调用允许)
 *   - coord-final-decisions §2 B6 (Redis 5-30s 短缓存)
 *
 * P3 时下游 content 未就绪, env.MOCK_UPSTREAM=true 返回 mock; 上游就绪后切换真实调用.
 */
import type { StudentBffContext } from "../../shared/graphql/yoga.js";
import { ok, fail } from "../../shared/action-state.js";
import { CacheTTL } from "../../shared/cache/cache.module.js";
import { UnauthorizedError } from "../../shared/errors/application-error.js";

export const contentResolvers = {
  Query: {
    /**
     * textbooks: 教材列表.
     * @permission STUDENT_CONTENT_READ
     * @dataScope OWN
     */
    async textbooks(
      _parent: unknown,
      args: {
        gradeId?: string;
        subjectId?: string;
        page?: number;
        pageSize?: number;
      },
      ctx: StudentBffContext,
    ): Promise<unknown> {
      if (!ctx.userId) {
        throw new UnauthorizedError();
      }

      const cacheKey = `${args.gradeId ?? "all"}:${args.subjectId ?? "all"}`;
      const cached = await ctx.cache.get<unknown>("textbooks", cacheKey);
      if (cached) {
        return ok(cached, {
          traceId: ctx.traceId,
          cachedAt: new Date().toISOString(),
        });
      }

      try {
        const result = await ctx.downstream.call(
          "content",
          "ListTextbooks",
          {
            gradeId: args.gradeId,
            subjectId: args.subjectId,
            page: args.page ?? 1,
            pageSize: Math.min(args.pageSize ?? 20, 50),
          },
          {
            traceId: ctx.traceId,
            metadata: { "x-user-id": ctx.userId },
          },
        );

        const data = result as { textbooks: unknown[] };
        await ctx.cache.set("textbooks", data, CacheTTL.TEXTBOOKS, cacheKey);
        return ok(data, { traceId: ctx.traceId });
      } catch (err) {
        return fail(
          "BFF_STUDENT_BAD_GATEWAY",
          `Failed to fetch textbooks: ${(err as Error).message}`,
          { i18nKey: "error.bffStudent.bad_gateway", traceId: ctx.traceId },
        );
      }
    },

    /**
     * chapters: 教材章节树.
     * @permission STUDENT_CONTENT_READ
     */
    async chapters(
      _parent: unknown,
      args: { textbookId: string },
      ctx: StudentBffContext,
    ): Promise<unknown> {
      if (!ctx.userId) {
        throw new UnauthorizedError();
      }

      const cached = await ctx.cache.get<unknown>("chapters", args.textbookId);
      if (cached) {
        return ok(cached, {
          traceId: ctx.traceId,
          cachedAt: new Date().toISOString(),
        });
      }

      try {
        const result = await ctx.downstream.call(
          "content",
          "ListChapters",
          {
            textbookId: args.textbookId,
          },
          {
            traceId: ctx.traceId,
            metadata: { "x-user-id": ctx.userId },
          },
        );

        const data = result as { chapters: unknown[] };
        await ctx.cache.set(
          "chapters",
          data,
          CacheTTL.CHAPTERS,
          args.textbookId,
        );
        return ok(data, { traceId: ctx.traceId });
      } catch (err) {
        return fail(
          "BFF_STUDENT_BAD_GATEWAY",
          `Failed to fetch chapters: ${(err as Error).message}`,
          { i18nKey: "error.bffStudent.bad_gateway", traceId: ctx.traceId },
        );
      }
    },

    /**
     * learningPath: 个性化学习路径 (基于学情诊断).
     * @permission STUDENT_CONTENT_READ
     * @dataScope OWN
     */
    async learningPath(
      _parent: unknown,
      args: { knowledgePointId: string },
      ctx: StudentBffContext,
    ): Promise<unknown> {
      if (!ctx.userId) {
        throw new UnauthorizedError();
      }

      try {
        const result = await ctx.downstream.call(
          "content",
          "GetLearningPath",
          {
            studentId: ctx.userId,
            knowledgePointId: args.knowledgePointId,
          },
          {
            traceId: ctx.traceId,
            metadata: { "x-user-id": ctx.userId },
          },
        );

        return ok(result, { traceId: ctx.traceId });
      } catch (err) {
        return fail(
          "BFF_STUDENT_BAD_GATEWAY",
          `Failed to fetch learning path: ${(err as Error).message}`,
          { i18nKey: "error.bffStudent.bad_gateway", traceId: ctx.traceId },
        );
      }
    },
  },
};
