import "server-only";

import { cache } from "react";
import { and, desc, eq, inArray, like, or, sql, type SQL } from "drizzle-orm";
import { createId } from "@paralleldrive/cuid2";

import { db } from "@/shared/db";
import {
  lessonPlans,
  lessonPlanTemplates,
  textbooks,
  chapters,
  subjects,
  grades,
  users,
} from "@/shared/db/schema";
import type { DataScope } from "@/shared/types/permissions";
import { escapeLikePattern } from "@/shared/lib/action-utils";
import { SYSTEM_TEMPLATES } from "./constants";
import {
  migrateV1ToV2,
  normalizeDocument,
  buildInitialContent,
  buildDefaultSkeleton,
} from "./lib/document-migration";
import { normalizeTemplateBlocks } from "./lib/type-guards";
import { getChaptersByTextbookId, getTextbooks, findChapterById } from "@/modules/textbooks/data-access";
import type {
  LessonPlan,
  LessonPlanDocument,
  LessonPlanListItem,
  LessonPlanTemplate,
  LessonPlanStatus,
  LessonPlanVersionSummary,
  TemplateType,
  TemplateScope,
} from "./types";

// re-export 纯函数保持向后兼容
export { migrateV1ToV2, normalizeDocument, buildInitialContent, buildDefaultSkeleton };

// ---- data-access 层错误码（由 actions 层翻译为 i18n 消息）----
export class LessonPlanDataError extends Error {
  constructor(public readonly code: "NOT_FOUND" | "TEMPLATE_NOT_FOUND") {
    super(code);
    this.name = "LessonPlanDataError";
  }
}

// ---- 类型守卫：安全地将 DB string 收窄为联合类型 ----
const LESSON_PLAN_STATUSES = ["draft", "published", "archived"] as const;
function isLessonPlanStatus(v: string): v is LessonPlanStatus {
  return (LESSON_PLAN_STATUSES as readonly string[]).includes(v);
}
const TEMPLATE_TYPES = ["system", "personal"] as const;
function isTemplateType(v: string): v is TemplateType {
  return (TEMPLATE_TYPES as readonly string[]).includes(v);
}
const TEMPLATE_SCOPES = ["regular", "review", "experiment", "inquiry", "blank", "custom"] as const;
function isTemplateScope(v: string): v is TemplateScope {
  return (TEMPLATE_SCOPES as readonly string[]).includes(v);
}

// ---- 类型映射：Drizzle 行 → LessonPlan（Date → ISO string）----
function mapRowToLessonPlan(row: {
  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;
}): LessonPlan {
  return {
    id: row.id,
    title: row.title,
    textbookId: row.textbookId,
    chapterId: row.chapterId,
    coursePlanItemId: row.coursePlanItemId,
    subjectId: row.subjectId,
    gradeId: row.gradeId,
    templateId: row.templateId,
    templateName: row.templateName,
    content: normalizeDocument(row.content),
    status: isLessonPlanStatus(row.status) ? row.status : "draft",
    creatorId: row.creatorId,
    lastSavedAt: row.lastSavedAt?.toISOString() ?? null,
    createdAt: row.createdAt.toISOString(),
    updatedAt: row.updatedAt.toISOString(),
  };
}

// ---- 类型映射：Drizzle 行 → LessonPlanListItem ----
function mapRowToListItem(row: {
  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;
  textbookTitle: string | null;
  chapterTitle: string | null;
  subjectName: string | null;
  gradeName: string | null;
  creatorName: string | null;
}): LessonPlanListItem {
  return {
    ...mapRowToLessonPlan(row),
    textbookTitle: row.textbookTitle,
    chapterTitle: row.chapterTitle,
    subjectName: row.subjectName,
    gradeName: row.gradeName,
    creatorName: row.creatorName,
    versionCount: 1,
    versions: [],
  };
}

// ---- 类型映射：Drizzle 行 → LessonPlanTemplate ----
function mapRowToTemplate(row: {
  id: string;
  name: string;
  type: string;
  scope: string;
  blocks: unknown;
  creatorId: string | null;
  createdAt: Date;
  updatedAt: Date;
}): LessonPlanTemplate {
  return {
    id: row.id,
    name: row.name,
    type: isTemplateType(row.type) ? row.type : "personal",
    scope: isTemplateScope(row.scope) ? row.scope : "custom",
    // P1 修复：使用 normalizeTemplateBlocks 安全转换 DB JSON 字段，替代 as unknown as 断言
    blocks: normalizeTemplateBlocks(row.blocks),
    creatorId: row.creatorId,
    createdAt: row.createdAt.toISOString(),
    updatedAt: row.updatedAt.toISOString(),
  };
}

// ---- DataScope → 查询条件 ----
// P0-3 修复：按 scope 类型精确过滤，避免教师越权查看全校 published 课案
function buildScopeCondition(scope: DataScope, userId: string): SQL[] {
  switch (scope.type) {
    case "all":
      return [];
    case "owned":
      return [eq(lessonPlans.creatorId, userId)];
    case "class_taught": {
      // 教师：自己创建的 + published 且属于自己教授学科的
      const own = eq(lessonPlans.creatorId, userId);
      const publishedFilter = sql<boolean>`(${lessonPlans.status} = 'published')`;
      const subjectFilter =
        scope.subjectIds && scope.subjectIds.length > 0
          ? inArray(lessonPlans.subjectId, scope.subjectIds)
          : sql<boolean>`true`;
      return [
        or(
          own,
          and(publishedFilter, subjectFilter),
        )!,
      ];
    }
    case "grade_managed": {
      // 教研组长/年级主任：自己创建的 + published 且属于自己管理的年级
      const own = eq(lessonPlans.creatorId, userId);
      const publishedFilter = sql<boolean>`(${lessonPlans.status} = 'published')`;
      const gradeFilter =
        scope.gradeIds.length > 0
          ? inArray(lessonPlans.gradeId, scope.gradeIds)
          : sql<boolean>`false`;
      return [
        or(
          own,
          and(publishedFilter, gradeFilter),
        )!,
      ];
    }
    case "class_members": {
      // 学生：仅查看自己所在年级的 published 课案
      const publishedFilter = sql<boolean>`(${lessonPlans.status} = 'published')`;
      const gradeFilter =
        scope.gradeIds && scope.gradeIds.length > 0
          ? inArray(lessonPlans.gradeId, scope.gradeIds)
          : sql<boolean>`false`;
      return [and(publishedFilter, gradeFilter)!];
    }
    case "children": {
      // 家长：仅查看孩子所在年级的 published 课案
      const publishedFilter = sql<boolean>`(${lessonPlans.status} = 'published')`;
      const gradeFilter =
        scope.gradeIds && scope.gradeIds.length > 0
          ? inArray(lessonPlans.gradeId, scope.gradeIds)
          : sql<boolean>`false`;
      return [and(publishedFilter, gradeFilter)!];
    }
  }
}

// ---- 课案列表 ----
export const getLessonPlans = cache(
  async (
    params: {
      query?: string;
      textbookId?: string;
      chapterId?: string;
      subjectId?: string;
      status?: string;
    },
    scope: DataScope,
    userId: string,
  ): Promise<LessonPlanListItem[]> => {
    const conditions: SQL[] = [
      sql`${lessonPlans.status} != 'archived'`,
    ];
    conditions.push(...buildScopeCondition(scope, userId));

    if (params.query) {
      conditions.push(like(lessonPlans.title, `%${escapeLikePattern(params.query)}%`));
    }
    if (params.textbookId)
      conditions.push(eq(lessonPlans.textbookId, params.textbookId));
    if (params.chapterId)
      conditions.push(eq(lessonPlans.chapterId, params.chapterId));
    if (params.subjectId)
      conditions.push(eq(lessonPlans.subjectId, params.subjectId));
    if (params.status) conditions.push(eq(lessonPlans.status, params.status));

    const rows = await db
      .select({
        id: lessonPlans.id,
        title: lessonPlans.title,
        textbookId: lessonPlans.textbookId,
        chapterId: lessonPlans.chapterId,
        coursePlanItemId: lessonPlans.coursePlanItemId,
        subjectId: lessonPlans.subjectId,
        gradeId: lessonPlans.gradeId,
        templateId: lessonPlans.templateId,
        templateName: lessonPlans.templateName,
        content: lessonPlans.content,
        status: lessonPlans.status,
        creatorId: lessonPlans.creatorId,
        lastSavedAt: lessonPlans.lastSavedAt,
        createdAt: lessonPlans.createdAt,
        updatedAt: lessonPlans.updatedAt,
        textbookTitle: textbooks.title,
        chapterTitle: chapters.title,
        subjectName: subjects.name,
        gradeName: grades.name,
        creatorName: users.name,
      })
      .from(lessonPlans)
      .leftJoin(textbooks, eq(lessonPlans.textbookId, textbooks.id))
      .leftJoin(chapters, eq(lessonPlans.chapterId, chapters.id))
      .leftJoin(subjects, eq(lessonPlans.subjectId, subjects.id))
      .leftJoin(grades, eq(lessonPlans.gradeId, grades.id))
      .leftJoin(users, eq(lessonPlans.creatorId, users.id))
      .where(and(...conditions))
      .orderBy(desc(lessonPlans.updatedAt));

    const items = rows.map(mapRowToListItem);

    // 版本聚合：按 textbookId + chapterId + creatorId 分组（同一教师对同一章节的多个课案视为版本）
    // 无 textbookId/chapterId 的课案各自独立成组
    const groups = new Map<string, LessonPlanListItem[]>();
    for (const item of items) {
      const key =
        item.textbookId && item.chapterId
          ? `${item.textbookId}|${item.chapterId}|${item.creatorId}`
          : `__single__${item.id}`;
      const arr = groups.get(key);
      if (arr) {
        arr.push(item);
      } else {
        groups.set(key, [item]);
      }
    }

    // 每组取第一个（updatedAt 最新，因已按 updatedAt desc 排序）作为代表，附加版本信息
    const grouped: LessonPlanListItem[] = [];
    for (const groupItems of groups.values()) {
      const representative = groupItems[0];
      if (!representative) continue;
      const versions: LessonPlanVersionSummary[] = groupItems
        .map((v) => ({
          id: v.id,
          title: v.title,
          status: v.status,
          updatedAt: v.updatedAt,
          lastSavedAt: v.lastSavedAt,
        }))
        .sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1));
      grouped.push({
        ...representative,
        versionCount: groupItems.length,
        versions,
      });
    }

    return grouped;
  },
);

// ---- 单课案 ----
// 安全说明：此函数仅校验 creator 或 published 状态。
// 对于 parent/student 角色，调用方（页面层）必须额外校验 plan.gradeId
// 是否在 ctx.dataScope.gradeIds 范围内，防止跨年级信息泄露。
export const getLessonPlanById = cache(
  async (id: string, userId: string): Promise<LessonPlan | null> => {
    const rows = await db
      .select()
      .from(lessonPlans)
      .where(eq(lessonPlans.id, id))
      .limit(1);
    if (rows.length === 0) return null;
    const row = rows[0];
    // 权限：creator 可看 draft；非 creator 仅 published
    if (row.creatorId !== userId && row.status !== "published") return null;
    return mapRowToLessonPlan(row);
  },
);

// ---- 创建 ----
// V2-2 修复：接受 translateTitle 函数，将 SYSTEM_TEMPLATES 中的 i18n 键翻译为实际文本
// V3 升级：当提供 chapterId 时，使用 buildDefaultSkeleton 生成完整 v3 文档（含正文节点）
export async function createLessonPlan(input: {
  title: string;
  textbookId?: string;
  chapterId?: string;
  subjectId?: string;
  gradeId?: string;
  templateId: string;
  creatorId: string;
  translateTitle?: (key: string) => string;
}): Promise<{ planId: string }> {
  const template = await getTemplateById(input.templateId);
  if (!template) throw new LessonPlanDataError("TEMPLATE_NOT_FOUND");

  const planId = createId();

  // V3：如果提供了 chapterId，拉取章节正文并生成默认骨架
  let content: LessonPlanDocument;
  if (input.chapterId && input.textbookId) {
    const chapters = await getChaptersByTextbookId(input.textbookId);
    const chapter = findChapterById(chapters, input.chapterId);
    const chapterContent = chapter?.content ?? "";
    content = buildDefaultSkeleton(
      input.chapterId,
      chapterContent,
      input.translateTitle,
    );
  } else {
    // 无章节：使用模板生成（v3 格式，正文节点内容为空）
    const blocks = input.translateTitle
      ? template.blocks.map((b) => ({
          ...b,
          title: input.translateTitle!(b.title),
        }))
      : template.blocks;
    content = buildInitialContent(blocks);
  }

  await db.insert(lessonPlans).values({
    id: planId,
    title: input.title,
    textbookId: input.textbookId ?? null,
    chapterId: input.chapterId ?? null,
    subjectId: input.subjectId ?? null,
    gradeId: input.gradeId ?? null,
    templateId: template.id,
    templateName: template.name,
    content,
    status: "draft",
    creatorId: input.creatorId,
    lastSavedAt: new Date(),
  });

  return { planId };
}

// ---- 工具：在章节树中查找章节（含子章节）----
// P1-6 修复：使用 textbooks 模块共享的 findChapterById，消除重复代码
// （原本地函数已删除，改为从 @/modules/textbooks/data-access 导入）

// ---- 获取教材列表（供 picker 使用）----
export async function getTextbooksForPicker(): Promise<
  { id: string; title: string; subject: string; grade: string | null }[]
> {
  const textbooks = await getTextbooks();
  return textbooks.map((t) => ({
    id: t.id,
    title: t.title,
    subject: t.subject,
    grade: t.grade,
  }));
}

// ---- 获取章节列表（供 picker 使用）----
export async function getChaptersForPicker(
  textbookId: string,
): Promise<
  // P1 修复：使用 ChapterPickerOption 递归类型，替代内联的 children?: unknown[]
  import("./providers/lesson-plan-provider").ChapterPickerOption[]
> {
  const chapters = await getChaptersByTextbookId(textbookId);
  return chapters.map((c) => ({
    id: c.id,
    title: c.title,
    parentId: c.parentId,
    order: c.order,
    content: c.content,
    children: c.children,
  }));
}

// ---- 更新 content（自动保存，不生成版本）----
export async function updateLessonPlanContent(
  planId: string,
  userId: string,
  patch: { title?: string; content: LessonPlanDocument },
): Promise<void> {
  const result = await db
    .update(lessonPlans)
    .set({
      ...(patch.title ? { title: patch.title } : {}),
      content: patch.content,
      lastSavedAt: new Date(),
    })
    .where(
      and(eq(lessonPlans.id, planId), eq(lessonPlans.creatorId, userId)),
    );

  // MySQL 返回 [ResultSetHeader, FieldPacket[]]，通过 affectedRows 判断是否更新成功
  if (result[0].affectedRows === 0) {
    throw new LessonPlanDataError("NOT_FOUND");
  }
}

// ---- 软删除 ----
export async function softDeleteLessonPlan(
  planId: string,
  userId: string,
): Promise<void> {
  const result = await db
    .update(lessonPlans)
    .set({ status: "archived" })
    .where(
      and(eq(lessonPlans.id, planId), eq(lessonPlans.creatorId, userId)),
    );

  // MySQL 返回 [ResultSetHeader, FieldPacket[]]，通过 affectedRows 判断是否更新成功
  if (result[0].affectedRows === 0) {
    throw new LessonPlanDataError("NOT_FOUND");
  }
}

// ---- 发布 / 撤回发布 ----
// P0-1 修复：课案发布机制，发布后学生/家长可查看
export async function publishLessonPlan(
  planId: string,
  userId: string,
): Promise<void> {
  const result = await db
    .update(lessonPlans)
    .set({ status: "published", lastSavedAt: new Date() })
    .where(
      and(eq(lessonPlans.id, planId), eq(lessonPlans.creatorId, userId)),
    );

  if (result[0].affectedRows === 0) {
    throw new LessonPlanDataError("NOT_FOUND");
  }
}

export async function unpublishLessonPlan(
  planId: string,
  userId: string,
): Promise<void> {
  const result = await db
    .update(lessonPlans)
    .set({ status: "draft", lastSavedAt: new Date() })
    .where(
      and(
        eq(lessonPlans.id, planId),
        eq(lessonPlans.creatorId, userId),
        eq(lessonPlans.status, "published"),
      ),
    );

  if (result[0].affectedRows === 0) {
    throw new LessonPlanDataError("NOT_FOUND");
  }
}

// ---- 复制 ----
// V3 修复：duplicateSuffix 由 actions 层 i18n 翻译后传入，避免 data-access 硬编码中文
// 事务修复：读源课案 + 写副本必须在同一事务内，避免读后写之间源课案被删除/改动造成数据不一致
export async function duplicateLessonPlan(
  planId: string,
  userId: string,
  duplicateSuffix: string = " - Copy",
): Promise<{ newPlanId: string }> {
  return db.transaction(async (tx) => {
    const rows = await tx
      .select()
      .from(lessonPlans)
      .where(eq(lessonPlans.id, planId))
      .limit(1);
    if (rows.length === 0) throw new LessonPlanDataError("NOT_FOUND");
    const row = rows[0];
    // 权限：creator 可复制 draft；非 creator 仅可复制 published
    if (row.creatorId !== userId && row.status !== "published") {
      throw new LessonPlanDataError("NOT_FOUND");
    }
    const src = mapRowToLessonPlan(row);

    const newId = createId();
    await tx.insert(lessonPlans).values({
      id: newId,
      title: `${src.title}${duplicateSuffix}`,
      textbookId: src.textbookId,
      chapterId: src.chapterId,
      subjectId: src.subjectId,
      gradeId: src.gradeId,
      templateId: src.templateId,
      templateName: src.templateName,
      content: src.content,
      status: "draft",
      creatorId: userId,
      lastSavedAt: new Date(),
    });
    return { newPlanId: newId };
  });
}

// ---- 统计：各状态课案数量（供管理员看板使用，避免 app 层直查 DB）----
export interface LessonPlanStats {
  total: number;
  published: number;
  draft: number;
  archived: number;
}

export async function getLessonPlanStats(): Promise<LessonPlanStats> {
  // 统计所有课案（含 archived），按 status 分组计数
  const rows = await db
    .select({ status: lessonPlans.status, count: sql<number>`count(*)` })
    .from(lessonPlans)
    .groupBy(lessonPlans.status);
  const map = new Map(rows.map((r) => [r.status, Number(r.count)]));
  const published = map.get("published") ?? 0;
  const draft = map.get("draft") ?? 0;
  const archived = map.get("archived") ?? 0;
  return {
    total: published + draft + archived,
    published,
    draft,
    archived,
  };
}

// ---- 模板查询（内部）----
export async function getTemplateById(
  templateId: string,
): Promise<LessonPlanTemplate | null> {
  // 先查 system 固定模板
  const sysDef = SYSTEM_TEMPLATES.find((t) => t.id === templateId);
  if (sysDef) {
    return {
      id: sysDef.id,
      name: sysDef.name,
      type: "system",
      scope: sysDef.scope,
      blocks: sysDef.blocks,
      creatorId: null,
      createdAt: "",
      updatedAt: "",
    };
  }
  // 再查 DB（personal 模板）
  const rows = await db
    .select()
    .from(lessonPlanTemplates)
    .where(eq(lessonPlanTemplates.id, templateId))
    .limit(1);
  return rows.length > 0 ? mapRowToTemplate(rows[0]) : null;
}
