import { db } from "@/lib/prisma";

type RecommendedCourse = {
  courseId: string;
  slug: string;
  title: string;
  level: string;
  reason: string;
  priority: number;
};

type RecommendationPrismaClient = {
  miniGameAttempt: {
    findMany: (args: object) => Promise<{ miniGameId: string; scorePercent: number }[]>;
  };
  studyRecommendation: {
    updateMany: (args: object) => Promise<unknown>;
    createMany: (args: object) => Promise<unknown>;
    findMany: (args: object) => Promise<
      {
        courseId: string;
        reason: string;
        priority: number;
        course: { title: unknown; slug: string; level: string };
      }[]
    >;
  };
};

function getText(value: unknown): string {
  if (!value) return "";
  if (typeof value === "string") return value;
  if (typeof value === "object") {
    const record = value as Record<string, unknown>;
    if (typeof record.en === "string") return record.en;
    if (typeof record.es === "string") return record.es;
  }
  return "";
}

function targetLevelByGrade(grade: number): "BEGINNER" | "INTERMEDIATE" | "ADVANCED" {
  if (grade < 60) return "BEGINNER";
  if (grade < 80) return "INTERMEDIATE";
  return "ADVANCED";
}

export async function getMiniGameGrade(userId: string): Promise<number> {
  let attempts: { miniGameId: string; scorePercent: number }[] = [];
  const prismaAny = db as unknown as RecommendationPrismaClient;
  try {
    attempts = await prismaAny.miniGameAttempt.findMany({
      where: { userId },
      orderBy: { completedAt: "desc" },
      select: { miniGameId: true, scorePercent: true },
    });
  } catch {
    return 0;
  }

  const latestByGame = new Map<string, number>();
  for (const attempt of attempts) {
    if (!latestByGame.has(attempt.miniGameId)) {
      latestByGame.set(attempt.miniGameId, attempt.scorePercent);
    }
  }

  const latest = [...latestByGame.values()];
  if (latest.length === 0) return 0;
  return Math.round(latest.reduce((acc, value) => acc + value, 0) / latest.length);
}

export async function refreshStudyRecommendations(userId: string) {
  const grade = await getMiniGameGrade(userId);
  const targetLevel = targetLevelByGrade(grade);

  const [courses, enrollments] = await Promise.all([
    db.course.findMany({
      where: {
        status: "PUBLISHED",
      },
      include: {
        modules: {
          include: {
            lessons: {
              select: { id: true },
            },
          },
        },
      },
    }),
    db.enrollment.findMany({
      where: { userId },
      select: { courseId: true },
    }),
  ]);

  const enrolledSet = new Set(enrollments.map((enrollment) => enrollment.courseId));
  const completedProgress = await db.userProgress.findMany({
    where: {
      userId,
      isCompleted: true,
      lesson: {
        module: {
          courseId: {
            in: courses.map((course) => course.id),
          },
        },
      },
    },
    select: {
      lesson: {
        select: {
          module: {
            select: { courseId: true },
          },
        },
      },
    },
  });

  const completedByCourse = new Map<string, number>();
  for (const item of completedProgress) {
    const courseId = item.lesson.module.courseId;
    completedByCourse.set(courseId, (completedByCourse.get(courseId) ?? 0) + 1);
  }

  const recommendations: RecommendedCourse[] = [];
  for (const course of courses) {
    const totalLessons = course.modules.reduce((acc, module) => acc + module.lessons.length, 0);
    const completedLessons = completedByCourse.get(course.id) ?? 0;
    const isCompleted = totalLessons > 0 && completedLessons >= totalLessons;
    if (isCompleted) continue;

    const isEnrolled = enrolledSet.has(course.id);
    const levelMatch = course.level === targetLevel;

    let priority = 20;
    let reason = `Aligned with your current level focus (${targetLevel.toLowerCase()}).`;

    if (isEnrolled) {
      priority = 5;
      reason = "You already started this course and can keep progressing.";
    } else if (!levelMatch) {
      priority = 40;
      reason = "Useful as a secondary recommendation outside your current level target.";
    }

    recommendations.push({
      courseId: course.id,
      slug: course.slug,
      title: getText(course.title) || "Untitled course",
      level: course.level,
      reason,
      priority,
    });
  }

  const sorted = recommendations.sort((a, b) => a.priority - b.priority).slice(0, 5);

  const prismaAny = db as unknown as RecommendationPrismaClient;
  try {
    await prismaAny.studyRecommendation.updateMany({
      where: { userId, isActive: true },
      data: { isActive: false },
    });

    if (sorted.length > 0) {
      await prismaAny.studyRecommendation.createMany({
        data: sorted.map((item) => ({
          userId,
          courseId: item.courseId,
          reason: item.reason,
          priority: item.priority,
        })),
      });
    }
  } catch {
    return sorted;
  }

  return sorted;
}

export async function getActiveRecommendations(userId: string) {
  let existing:
    | {
        courseId: string;
        reason: string;
        priority: number;
        course: { title: unknown; slug: string; level: string };
      }[]
    | null = null;
  try {
    existing = await (db as unknown as RecommendationPrismaClient).studyRecommendation.findMany({
      where: { userId, isActive: true },
      include: {
        course: {
          select: {
            title: true,
            slug: true,
            level: true,
          },
        },
      },
      orderBy: { priority: "asc" },
      take: 5,
    });
  } catch {
    return refreshStudyRecommendations(userId);
  }

  if (!existing || existing.length === 0) {
    return refreshStudyRecommendations(userId);
  }

  return existing.map((item) => ({
    courseId: item.courseId,
    slug: item.course.slug,
    title: getText(item.course.title) || "Untitled course",
    level: item.course.level,
    reason: item.reason,
    priority: item.priority,
  }));
}
