import { createYoga, type Plugin } from "graphql-yoga";
import depthLimit from "graphql-depth-limit";
import {
  createComplexityRule,
  simpleEstimator,
} from "graphql-query-complexity";
import type { Request, Response } from "express";
import type { IResolvers } from "@graphql-tools/utils";
import { buildSchema } from "./schema.js";
import { buildSession } from "./context.js";
import type { GraphqlContext, ParentSession } from "./context.js";
import { env } from "../config/env.js";
import { logger } from "../shared/observability/logger.js";
import {
  graphqlRequestsTotal,
  graphqlDurationSeconds,
} from "../shared/observability/metrics.js";
import type { DataLoaderFactory } from "../dataloader/dataloader.factory.js";

/**
 * 校验规则插件：注入深度限制 + 复杂度限制（02 §9 #5）。
 */
const validationRulesPlugin: Plugin = {
  onValidate: ({ addValidationRule }) => {
    addValidationRule(depthLimit(env.GRAPHQL_DEPTH_LIMIT));
    addValidationRule(
      createComplexityRule({
        maximumComplexity: env.GRAPHQL_COST_LIMIT,
        estimators: [simpleEstimator({ defaultComplexity: 1 })],
        onComplete: (complexity: number) => {
          logger.debug({ complexity }, "GraphQL query complexity");
        },
      }),
    );
  },
};

/**
 * 指标埋点插件：onExecute 记录开始，onExecuteDone 记录完成 + 延迟。
 */
const metricsPlugin: Plugin = {
  onExecute: ({ args }) => {
    const opName = args.operationName ?? "anonymous";
    const startedAt = Date.now();
    graphqlRequestsTotal.inc({ operation: opName, status: "started" });

    return {
      onExecuteDone: () => {
        const elapsed = (Date.now() - startedAt) / 1000;
        graphqlDurationSeconds.observe({ operation: opName }, elapsed);
        graphqlRequestsTotal.inc({
          operation: opName,
          status: "completed",
        });
      },
    };
  },
};

/**
 * 创建 GraphQL Yoga 实例（工厂模式，注入 DataLoaderFactory + Resolvers）。
 *
 * 对齐 02-architecture-design.md §1.1 + §9 #1 + §9 #5。
 *
 * 每次 GraphQL 请求构建 per-request context，包含：
 * - ParentSession（从 x-user-id 等头解析）
 * - Loaders（per-request DataLoader 实例，防 N+1）
 */
export function createYogaInstance(
  loaderFactory: DataLoaderFactory,
  resolvers: IResolvers,
) {
  const schema = buildSchema(resolvers);

  return createYoga<{
    req: Request;
    res: Response;
    session: ParentSession;
  }>({
    schema,
    graphqlEndpoint: "/graphql",
    graphiql:
      env.NODE_ENV === "development" && env.GRAPHQL_INTROSPECTION_ENABLED,

    context: ({ req }): GraphqlContext => {
      const session = buildSession(req);
      const loaders = loaderFactory.createLoaders();
      return { session, req, res: req.res as Response, loaders };
    },

    plugins: [validationRulesPlugin, metricsPlugin],
  });
}

/** Yoga 实例类型（供 GraphqlModule provider 类型推导） */
export type YogaInstance = ReturnType<typeof createYogaInstance>;
