import type { Prisma } from "@prisma/client";
import { db } from "@/lib/prisma";
import {
  ACADEMIC_STAGE_CONFIG,
  ACADEMIC_STAGE_ORDER,
  PUBLIC_UPCOMING_TAGS,
  type AcademicStageConfig,
  type AcademicStageId,
  formatDuration,
  formatMinutes,
  getCourseDescriptions,
  getLocalizedText,
  getProficiencyLabel,
  resolveAcademicStage,
  resolveAvailability,
  resolveThumbnailUrl,
  truncateText,
} from "@/lib/courses/presentation";
import { isFinalExam, parseLessonDescriptionMeta } from "@/lib/courses/lessonContent";

type CatalogCourseRow = Prisma.CourseGetPayload<{
  include: {
    author: {
      select: {
        fullName: true;
      };
    };
    modules: {
      orderBy: {
        orderIndex: "asc";
      };
      select: {
        lessons: {
          orderBy: {
            orderIndex: "asc";
          };
          select: {
            id: true;
            estimatedDuration: true;
            isFreePreview: true;
            youtubeUrl: true;
            videoUrl: true;
          };
        };
      };
    };
    _count: {
      select: {
        enrollments: true;
      };
    };
  };
}>;

type DetailCourseRow = Prisma.CourseGetPayload<{
  include: {
    author: {
      select: {
        fullName: true;
      };
    };
    modules: {
      orderBy: {
        orderIndex: "asc";
      };
      select: {
        id: true;
        title: true;
        orderIndex: true;
        lessons: {
          orderBy: {
            orderIndex: "asc";
          };
          select: {
            id: true;
            title: true;
            description: true;
            orderIndex: true;
            estimatedDuration: true;
            isFreePreview: true;
            youtubeUrl: true;
            videoUrl: true;
            _count: {
              select: {
                resources: true;
                exercises: true;
              };
            };
          };
        };
      };
    };
    _count: {
      select: {
        enrollments: true;
      };
    };
  };
}>;

export type CatalogCourseCardView = {
  id: string;
  slug: string;
  title: string;
  shortDescription: string;
  longDescription: string;
  thumbnailUrl: string | null;
  stageId: AcademicStageId;
  stageLabel: string;
  proficiencyLabel: string;
  durationLabel: string;
  durationMinutes: number;
  lessonCount: number;
  instructor: string;
  availabilityLabel: string;
  availabilityState: "published" | "upcoming" | "draft";
  progressPercent: number;
  completedLessons: number;
  totalLessons: number;
  studentsCount: number;
  isEnrolled: boolean;
  hasPreview: boolean;
};

export type CatalogSectionView = {
  id: AcademicStageId;
  anchorId: string;
  tabLabel: string;
  sectionTitle: string;
  sectionDescription: string;
  courses: CatalogCourseCardView[];
};

export type CourseCatalogViewModel = {
  sections: CatalogSectionView[];
  totals: {
    totalCourses: number;
    totalLessons: number;
    instructorCount: number;
  };
};

type ProgramBadge = "Video" | "Lectura" | "Actividad" | "Evaluación";

export type CourseProgramItemView = {
  id: string;
  order: number;
  title: string;
  subtitle: string;
  durationLabel: string | null;
  badges: ProgramBadge[];
  isPreview: boolean;
  isFinalExam: boolean;
  isLocked: boolean;
  isCompleted: boolean;
  isUpcoming: boolean;
};

export type CourseProgramModuleView = {
  id: string;
  title: string;
  order: number;
  items: CourseProgramItemView[];
};

export type CourseDetailViewModel = {
  id: string;
  slug: string;
  title: string;
  shortDescription: string;
  longDescription: string;
  thumbnailUrl: string | null;
  stage: AcademicStageConfig;
  proficiencyLabel: string;
  instructor: string;
  availabilityLabel: string;
  availabilityState: "published" | "upcoming" | "draft";
  studentsCount: number;
  lessonCount: number;
  durationLabel: string;
  durationMinutes: number;
  moduleCount: number;
  modules: CourseProgramModuleView[];
  progressPercent: number;
  completedLessons: number;
  totalLessons: number;
  isEnrolled: boolean;
  firstPreviewLessonId: string | null;
  price: number;
};

function toMinutes(totalSeconds: number): number {
  if (!Number.isFinite(totalSeconds) || totalSeconds <= 0) return 0;
  return Math.max(1, Math.ceil(totalSeconds / 60));
}

function getPrimaryMedia(lessons: Array<{ youtubeUrl: string | null; videoUrl: string | null }>) {
  const withYoutube = lessons.find((lesson) => Boolean(lesson.youtubeUrl?.trim()));
  if (withYoutube) return { youtubeUrl: withYoutube.youtubeUrl, videoUrl: withYoutube.videoUrl };

  const withVideo = lessons.find((lesson) => Boolean(lesson.videoUrl?.trim()));
  if (withVideo) return { youtubeUrl: withVideo.youtubeUrl, videoUrl: withVideo.videoUrl };

  return { youtubeUrl: null, videoUrl: null };
}

function getProgramBadges(lesson: DetailCourseRow["modules"][number]["lessons"][number]): ProgramBadge[] {
  const lessonMeta = parseLessonDescriptionMeta(lesson.description);
  const badges: ProgramBadge[] = [];

  if (lessonMeta.contentType === "VIDEO") badges.push("Video");
  if (lessonMeta.contentType === "LECTURE") badges.push("Lectura");
  if (lessonMeta.contentType === "ACTIVITY") badges.push("Actividad");
  if (lessonMeta.contentType === "QUIZ" || lessonMeta.contentType === "FINAL_EXAM") badges.push("Evaluación");
  if (lesson.youtubeUrl || lesson.videoUrl) {
    if (!badges.includes("Video")) badges.push("Video");
  }
  if (lesson._count.resources > 0 && !badges.includes("Lectura")) badges.push("Lectura");
  if (lesson._count.exercises > 0 && !badges.includes("Evaluación")) badges.push("Evaluación");
  if (badges.length === 0) badges.push("Actividad");

  return badges;
}

function getPublicCatalogWhere() {
  return {
    OR: [
      { status: "PUBLISHED" as const },
      {
        status: "DRAFT" as const,
        tags: {
          hasSome: PUBLIC_UPCOMING_TAGS,
        },
      },
    ],
  };
}

export async function getCourseCatalogViewModel(userId: string | null): Promise<CourseCatalogViewModel> {
  const courses = await db.course
    .findMany({
      where: getPublicCatalogWhere(),
      include: {
        author: {
          select: {
            fullName: true,
          },
        },
        modules: {
          orderBy: {
            orderIndex: "asc",
          },
          select: {
            lessons: {
              orderBy: {
                orderIndex: "asc",
              },
              select: {
                id: true,
                estimatedDuration: true,
                isFreePreview: true,
                youtubeUrl: true,
                videoUrl: true,
              },
            },
          },
        },
        _count: {
          select: {
            enrollments: true,
          },
        },
      },
      orderBy: [
        {
          updatedAt: "desc",
        },
      ],
    })
    .catch((error) => {
      console.error("Failed to load public course catalog.", error);
      return [] as CatalogCourseRow[];
    });

  const courseIds = courses.map((course) => course.id);
  const [enrollments, completedProgress] =
    userId && courseIds.length > 0
      ? await Promise.all([
          db.enrollment
            .findMany({
              where: {
                userId,
                courseId: {
                  in: courseIds,
                },
              },
              select: {
                courseId: true,
              },
            })
            .catch(() => []),
          db.userProgress
            .findMany({
              where: {
                userId,
                isCompleted: true,
                lesson: {
                  module: {
                    courseId: {
                      in: courseIds,
                    },
                  },
                },
              },
              select: {
                lesson: {
                  select: {
                    module: {
                      select: {
                        courseId: true,
                      },
                    },
                  },
                },
              },
            })
            .catch(() => []),
        ])
      : [[], []];

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

  const grouped: Record<AcademicStageId, CatalogCourseCardView[]> = {
    base: [],
    consolidacion: [],
    especializacion: [],
  };

  for (const course of courses) {
    const allLessons = course.modules.flatMap((module) => module.lessons);
    const totalSeconds = allLessons.reduce((sum, lesson) => sum + lesson.estimatedDuration, 0);
    const totalLessons = allLessons.length;
    const completedLessons = completedByCourse.get(course.id) ?? 0;
    const isEnrolled = enrollmentSet.has(course.id);
    const progressPercent = totalLessons > 0 ? Math.round((completedLessons / totalLessons) * 100) : 0;
    const stage = resolveAcademicStage(course.level, course.tags);
    const availability = resolveAvailability(course.status, course.tags);
    const descriptions = getCourseDescriptions(course.description);
    const media = getPrimaryMedia(allLessons);
    const thumbnailUrl =
      resolveThumbnailUrl({
        description: course.description,
        tags: course.tags,
        youtubeUrl: media.youtubeUrl,
        videoUrl: media.videoUrl,
      }) || null;

    grouped[stage.id].push({
      id: course.id,
      slug: course.slug,
      title: getLocalizedText(course.title) || "Programa académico ACVE",
      shortDescription: descriptions.short,
      longDescription: descriptions.long,
      thumbnailUrl,
      stageId: stage.id,
      stageLabel: stage.levelLabel,
      proficiencyLabel: getProficiencyLabel(course.level),
      durationLabel: formatDuration(totalSeconds),
      durationMinutes: toMinutes(totalSeconds),
      lessonCount: totalLessons,
      instructor: course.author.fullName || "Equipo académico ACVE",
      availabilityLabel: availability.label,
      availabilityState: availability.state,
      progressPercent,
      completedLessons,
      totalLessons,
      studentsCount: course._count.enrollments,
      isEnrolled,
      hasPreview: allLessons.some((lesson) => lesson.isFreePreview),
    });
  }

  const sections: CatalogSectionView[] = ACADEMIC_STAGE_ORDER.map((stageId) => {
    const config = ACADEMIC_STAGE_CONFIG[stageId];
    return {
      id: stageId,
      anchorId: `programas-${stageId}`,
      tabLabel: config.tabLabel,
      sectionTitle: config.sectionTitle,
      sectionDescription: config.sectionDescription,
      courses: grouped[stageId],
    };
  });

  const totalLessons = sections.reduce(
    (sum, section) => sum + section.courses.reduce((sectionSum, course) => sectionSum + course.lessonCount, 0),
    0,
  );
  const instructorCount = new Set(sections.flatMap((section) => section.courses.map((course) => course.instructor))).size;
  const totalCourses = sections.reduce((sum, section) => sum + section.courses.length, 0);

  return {
    sections,
    totals: {
      totalCourses,
      totalLessons,
      instructorCount,
    },
  };
}

function toNumber(value: Prisma.Decimal): number {
  const asNumber = Number(value);
  return Number.isFinite(asNumber) ? asNumber : 0;
}

export async function getCourseDetailViewModel(
  slug: string,
  userId: string | null,
): Promise<CourseDetailViewModel | null> {
  const course = await db.course.findFirst({
    where: {
      slug,
      ...getPublicCatalogWhere(),
    },
    include: {
      author: {
        select: {
          fullName: true,
        },
      },
      modules: {
        orderBy: {
          orderIndex: "asc",
        },
        select: {
          id: true,
          title: true,
          orderIndex: true,
          lessons: {
            orderBy: {
              orderIndex: "asc",
            },
            select: {
              id: true,
              title: true,
              description: true,
              orderIndex: true,
              estimatedDuration: true,
              isFreePreview: true,
              youtubeUrl: true,
              videoUrl: true,
              _count: {
                select: {
                  resources: true,
                  exercises: true,
                },
              },
            },
          },
        },
      },
      _count: {
        select: {
          enrollments: true,
        },
      },
    },
  });

  if (!course) return null;

  const allLessons = course.modules.flatMap((module) => module.lessons);
  const lessonIds = allLessons.map((lesson) => lesson.id);
  const [enrollment, completedProgress] = userId
    ? await Promise.all([
        db.enrollment.findUnique({
          where: {
            userId_courseId: {
              userId,
              courseId: course.id,
            },
          },
          select: {
            id: true,
          },
        }),
        lessonIds.length > 0
          ? db.userProgress.findMany({
              where: {
                userId,
                isCompleted: true,
                lessonId: {
                  in: lessonIds,
                },
              },
              select: {
                lessonId: true,
              },
            })
          : Promise.resolve([] as { lessonId: string }[]),
      ])
    : [null, [] as { lessonId: string }[]];

  const completedSet = new Set(completedProgress.map((entry) => entry.lessonId));
  const isEnrolled = Boolean(enrollment);
  const totalLessons = allLessons.length;
  const completedLessons = completedSet.size;
  const progressPercent = totalLessons > 0 ? Math.round((completedLessons / totalLessons) * 100) : 0;
  const totalSeconds = allLessons.reduce((sum, lesson) => sum + lesson.estimatedDuration, 0);
  const stage = resolveAcademicStage(course.level, course.tags);
  const availability = resolveAvailability(course.status, course.tags);
  const descriptions = getCourseDescriptions(course.description);
  const media = getPrimaryMedia(allLessons);
  const thumbnailUrl =
    resolveThumbnailUrl({
      description: course.description,
      tags: course.tags,
      youtubeUrl: media.youtubeUrl,
      videoUrl: media.videoUrl,
    }) || null;

  let runningOrder = 0;
  const modules: CourseProgramModuleView[] = course.modules.map((module) => ({
    id: module.id,
    title: getLocalizedText(module.title) || `Módulo ${module.orderIndex + 1}`,
    order: module.orderIndex + 1,
    items: module.lessons.map((lesson) => {
      runningOrder += 1;
      const lessonMeta = parseLessonDescriptionMeta(lesson.description);
      const subtitleRaw = lessonMeta.text || getLocalizedText(lesson.description);
      const subtitle = subtitleRaw ? truncateText(subtitleRaw, 130) : "";
      const isUpcoming = availability.state !== "published";
      const isLocked = !isUpcoming && !isEnrolled && !lesson.isFreePreview;
      return {
        id: lesson.id,
        order: runningOrder,
        title: getLocalizedText(lesson.title) || `Lección ${runningOrder}`,
        subtitle,
        durationLabel: lesson.estimatedDuration > 0 ? formatMinutes(toMinutes(lesson.estimatedDuration)) : null,
        badges: getProgramBadges(lesson),
        isPreview: lesson.isFreePreview,
        isFinalExam: isFinalExam(lessonMeta.contentType),
        isLocked,
        isCompleted: completedSet.has(lesson.id),
        isUpcoming,
      };
    }),
  }));

  return {
    id: course.id,
    slug: course.slug,
    title: getLocalizedText(course.title) || "Programa académico ACVE",
    shortDescription: descriptions.short,
    longDescription: descriptions.long,
    thumbnailUrl,
    stage,
    proficiencyLabel: getProficiencyLabel(course.level),
    instructor: course.author.fullName || "Equipo académico ACVE",
    availabilityLabel: availability.label,
    availabilityState: availability.state,
    studentsCount: course._count.enrollments,
    lessonCount: totalLessons,
    durationLabel: formatDuration(totalSeconds),
    durationMinutes: toMinutes(totalSeconds),
    moduleCount: modules.length,
    modules,
    progressPercent,
    completedLessons,
    totalLessons,
    isEnrolled,
    firstPreviewLessonId: allLessons.find((lesson) => lesson.isFreePreview)?.id ?? null,
    price: toNumber(course.price),
  };
}
