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 { SYSTEM_TEMPLATES } from "./constants";
import {
  migrateV1ToV2,
  normalizeDocument,
  buildInitialContent,
} from "./lib/document-migration";
import type {
  LessonPlan,
  LessonPlanDocument,
  LessonPlanListItem,
  LessonPlanTemplate,
  LessonPlanStatus,
  TemplateType,
  TemplateScope,
} from "./types";

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

// ---- 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,
  };
}

// ---- 类型映射：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",
    // 从 unknown 转换为 TemplateBlockSkeleton[]（DB JSON 字段）
    blocks: row.blocks as LessonPlanTemplate["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')`;
      return [publishedFilter];
    }
    case "children": {
      // 家长：仅查看 published 课案（同学生）
      const publishedFilter = sql<boolean>`(${lessonPlans.status} = 'published')`;
      return [publishedFilter];
    }
  }
}

// ---- 课案列表 ----
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, `%${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);
    return items;
  },
);

// ---- 单课案 ----
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 键翻译为实际文本
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();
  // 如果提供了翻译函数，将模板中的 i18n 键翻译为实际文本
  const blocks = input.translateTitle
    ? template.blocks.map((b) => ({
        ...b,
        title: input.translateTitle!(b.title),
      }))
    : template.blocks;
  const 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 };
}

// ---- 更新 content（自动保存，不生成版本）----
export async function updateLessonPlanContent(
  planId: string,
  userId: string,
  patch: { title?: string; content: LessonPlanDocument },
): Promise<void> {
  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)),
    );
}

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

// ---- 复制 ----
export async function duplicateLessonPlan(
  planId: string,
  userId: string,
): Promise<{ newPlanId: string }> {
  const src = await getLessonPlanById(planId, userId);
  if (!src) throw new LessonPlanDataError("NOT_FOUND");

  const newId = createId();
  await db.insert(lessonPlans).values({
    id: newId,
    title: `${src.title} - 副本`,
    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 };
}

// ---- 模板查询（内部）----
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;
}
