import "server-only";

import { and, desc, eq, inArray, max } from "drizzle-orm";
import { createId } from "@paralleldrive/cuid2";

import { db } from "@/shared/db";
import { lessonPlanVersions, lessonPlans } from "@/shared/db/schema";
import { normalizeDocument } from "./data-access";
import type { LessonPlanDocument, LessonPlanVersion } from "./types";

// ---- 类型映射：Drizzle 行 → LessonPlanVersion（Date → ISO string）----
function mapRowToVersion(row: {
  id: string;
  planId: string;
  versionNo: number;
  label: string | null;
  content: unknown;
  isAuto: boolean;
  creatorId: string;
  createdAt: Date;
}): LessonPlanVersion {
  return {
    id: row.id,
    planId: row.planId,
    versionNo: row.versionNo,
    label: row.label,
    content: normalizeDocument(row.content),
    isAuto: row.isAuto,
    creatorId: row.creatorId,
    createdAt: row.createdAt.toISOString(),
  };
}

export async function getLessonPlanVersions(
  planId: string,
  userId: string,
): Promise<LessonPlanVersion[]> {
  // 校验归属
  const plan = await db
    .select({ id: lessonPlans.id })
    .from(lessonPlans)
    .where(
      and(eq(lessonPlans.id, planId), eq(lessonPlans.creatorId, userId)),
    )
    .limit(1);
  if (plan.length === 0) return [];

  const rows = await db
    .select()
    .from(lessonPlanVersions)
    .where(eq(lessonPlanVersions.planId, planId))
    .orderBy(desc(lessonPlanVersions.versionNo));
  return rows.map(mapRowToVersion);
}

export async function createLessonPlanVersion(input: {
  planId: string;
  content: LessonPlanDocument;
  userId: string;
  isAuto: boolean;
  label?: string;
}): Promise<{ versionNo: number }> {
  // 取当前最大 versionNo
  const maxRow = await db
    .select({ maxNo: max(lessonPlanVersions.versionNo) })
    .from(lessonPlanVersions)
    .where(eq(lessonPlanVersions.planId, input.planId));
  const nextNo = (maxRow[0]?.maxNo ?? 0) + 1;

  await db.insert(lessonPlanVersions).values({
    id: createId(),
    planId: input.planId,
    versionNo: nextNo,
    label: input.label ?? null,
    content: input.content,
    isAuto: input.isAuto,
    creatorId: input.userId,
  });
  return { versionNo: nextNo };
}

export async function getVersionContent(
  planId: string,
  versionNo: number,
  userId: string,
): Promise<LessonPlanDocument | null> {
  // 校验归属
  const plan = await db
    .select({ id: lessonPlans.id })
    .from(lessonPlans)
    .where(
      and(eq(lessonPlans.id, planId), eq(lessonPlans.creatorId, userId)),
    )
    .limit(1);
  if (plan.length === 0) return null;

  const rows = await db
    .select({ content: lessonPlanVersions.content })
    .from(lessonPlanVersions)
    .where(
      and(
        eq(lessonPlanVersions.planId, planId),
        eq(lessonPlanVersions.versionNo, versionNo),
      ),
    )
    .limit(1);
  if (rows.length === 0) return null;
  return normalizeDocument(rows[0].content);
}

export async function revertToVersion(
  planId: string,
  versionNo: number,
  userId: string,
): Promise<{ newVersionNo: number } | null> {
  const content = await getVersionContent(planId, versionNo, userId);
  if (!content) return null;

  // 用该版本 content 覆盖当前 + 生成新版本
  await db
    .update(lessonPlans)
    .set({ content, lastSavedAt: new Date() })
    .where(
      and(eq(lessonPlans.id, planId), eq(lessonPlans.creatorId, userId)),
    );

  const { versionNo: newNo } = await createLessonPlanVersion({
    planId,
    content,
    userId,
    isAuto: false,
    label: `回退到 v${versionNo}`,
  });
  return { newVersionNo: newNo };
}

export async function pruneAutoVersions(
  planId: string,
  keep = 50,
): Promise<void> {
  const rows = await db
    .select({
      id: lessonPlanVersions.id,
      isAuto: lessonPlanVersions.isAuto,
      versionNo: lessonPlanVersions.versionNo,
    })
    .from(lessonPlanVersions)
    .where(eq(lessonPlanVersions.planId, planId))
    .orderBy(desc(lessonPlanVersions.versionNo));

  if (rows.length <= keep) return;
  // 保留前 keep 条；超出部分只删 isAuto=true 的
  const toDelete = rows.slice(keep).filter((r) => r.isAuto);
  if (toDelete.length === 0) return;
  await db
    .delete(lessonPlanVersions)
    .where(
      and(
        eq(lessonPlanVersions.planId, planId),
        inArray(
          lessonPlanVersions.id,
          toDelete.map((r) => r.id),
        ),
      ),
    );
}
