import "server-only";

import { and, eq, like, sql } from "drizzle-orm";

import { db } from "@/shared/db";
import { lessonPlans } from "@/shared/db/schema";
import { escapeLikePattern } from "@/shared/lib/action-utils";
import { isRecord } from "@/shared/lib/type-guards";
import { isLessonPlanStatus } from "./lib/type-guards";
import { normalizeDocument } from "./data-access";
import type { LessonPlanListItem } from "./types";

// ---- 安全字段提取辅助（替代 as 断言，从 BlockData 联合类型收窄）----
// 类型守卫：判断 unknown 是否为 string[]（替代 as string[] 断言）
function isStringArray(v: unknown): v is string[] {
  return Array.isArray(v) && v.every((x) => typeof x === "string");
}

function getStringArray(v: unknown): string[] | undefined {
  return isStringArray(v) ? v : undefined;
}

/** 从 block.data 安全读取 knowledgePointIds 字段 */
function getKnowledgePointIds(data: unknown): string[] | undefined {
  return isRecord(data) ? getStringArray(data.knowledgePointIds) : undefined;
}

/** 从 block.data 安全读取 items 字段中的 questionId 列表 */
function getQuestionIds(data: unknown): string[] {
  if (!isRecord(data) || !Array.isArray(data.items)) return [];
  return data.items
    .filter((it): it is Record<string, unknown> => isRecord(it))
    .map((it) => it.questionId)
    .filter((id): id is string => typeof id === "string");
}

/**
 * 将 DB 行映射为 LessonPlanListItem（无 join 版本）。
 * V4 P1-15 修复：提取共享辅助函数，消除 getLessonPlansByKnowledgePoint 与
 * getLessonPlansByQuestion 中的重复 .map 实现。
 */
function mapRowToListItemWithoutJoin(r: {
  id: string;
  title: string;
  textbookId: string | null;
  chapterId: string | null;
  coursePlanItemId: string | null;
  subjectId: string | null;
  gradeId: string | null;
  templateId: string | null;
  templateName: string | null;
  content: unknown;
  status: string;
  creatorId: string;
  lastSavedAt: Date | null;
  createdAt: Date;
  updatedAt: Date;
}): LessonPlanListItem {
  return {
    id: r.id,
    title: r.title,
    textbookId: r.textbookId,
    chapterId: r.chapterId,
    coursePlanItemId: r.coursePlanItemId,
    subjectId: r.subjectId,
    gradeId: r.gradeId,
    templateId: r.templateId,
    templateName: r.templateName,
    content: normalizeDocument(r.content),
    status: isLessonPlanStatus(r.status) ? r.status : "draft",
    creatorId: r.creatorId,
    lastSavedAt: r.lastSavedAt?.toISOString() ?? null,
    createdAt: r.createdAt.toISOString(),
    updatedAt: r.updatedAt.toISOString(),
    textbookTitle: null,
    chapterTitle: null,
    subjectName: null,
    gradeName: null,
    creatorName: null,
    versionCount: 1,
    versions: [],
  };
}

// 查询关联了某知识点的课案
export async function getLessonPlansByKnowledgePoint(
  knowledgePointId: string,
  userId: string,
): Promise<LessonPlanListItem[]> {
  // content 是 JSON，用 LIKE 粗筛后内存精确过滤
  const rows = await db
    .select()
    .from(lessonPlans)
    .where(
      and(
        eq(lessonPlans.creatorId, userId),
        sql`${lessonPlans.status} != 'archived'`,
        like(lessonPlans.content, `%${escapeLikePattern(knowledgePointId)}%`),
      ),
    );
  // 类型守卫：过滤出包含该知识点的课案，并映射为 LessonPlanListItem
  return rows
    .filter((r) => {
      const doc = normalizeDocument(r.content);
      return doc.nodes.some((b) => {
        // P1 修复：使用安全字段提取替代 as unknown as 断言
        const kpIds = getKnowledgePointIds(b.data);
        return kpIds?.includes(knowledgePointId);
      });
    })
    .map(mapRowToListItemWithoutJoin);
}

// 查询使用了某题目的课案
export async function getLessonPlansByQuestion(
  questionId: string,
  userId: string,
): Promise<LessonPlanListItem[]> {
  const rows = await db
    .select()
    .from(lessonPlans)
    .where(
      and(
        eq(lessonPlans.creatorId, userId),
        sql`${lessonPlans.status} != 'archived'`,
        like(lessonPlans.content, `%${escapeLikePattern(questionId)}%`),
      ),
    );
  return rows
    .filter((r) => {
      const doc = normalizeDocument(r.content);
      return doc.nodes.some((b) => {
        if (b.type !== "exercise") return false;
        // P1 修复：使用安全字段提取替代 as unknown as 断言
        const questionIds = getQuestionIds(b.data);
        return questionIds.includes(questionId);
      });
    })
    .map(mapRowToListItemWithoutJoin);
}
