import type { Request, Response } from "express";
import { prisma } from "../config/db.ts";
import fs from "fs";
import path from "path";
import { getHost, toPublicPath } from "../utils/utils.ts";
import { toIST } from "../utils/time.ts";
import { sendNotification } from "../config/firebase.ts";
import asyncHandler from "express-async-handler";

const deleteFile = (filePath: string) => {
    if (!filePath) return;
    try {
        const fileName = path.basename(filePath);
        const absolutePath = path.join(process.cwd(), "uploads", fileName);
        if (fs.existsSync(absolutePath)) fs.unlinkSync(absolutePath);
    } catch (error) {
        console.error("Error deleting file:", error);
    }
};

const getStudentCurrentExamId = async (req: Request) => {
    const user = req.user as any;
    if (user?.role !== "STUDENT") return null;
    if (!user?.studentInstitutionId) return null;

    const studentInstitution = await prisma.studentInstitution.findFirst({
        where: { id: user.studentInstitutionId, deletedAt: null },
        select: { examsId: true },
    });

    return studentInstitution?.examsId ?? null;
};

export const createSyllabus = asyncHandler(async (req: Request, res: Response) => {
    const user = req.user;
    const { exam, description } = req.body;
    const files = req.files as Express.Multer.File[];
    const syllabusFiles = files ? files.map(file => toPublicPath(file.path)) : [];

    if (!exam) {
        syllabusFiles.forEach(file => deleteFile(file));
        res.status(400).json({ message: "Exam is required" });
        return;
    }

    const newSyllabus = await prisma.syllabus.create({
        data: {
            examId: exam,
            syllabusFiles,
            description,
            institutionId: user.institutionId,
        },
        include: { exam: { select: { examName: true } } }
    });

    try {
        const studentInstitutions = await prisma.studentInstitution.findMany({ where: { institutionId: user.institutionId, examsId: exam }, include: { student: { select: { fcmToken: true } } } });
        if (studentInstitutions.length > 0) {
            const tokens = studentInstitutions.map(s => s?.student?.fcmToken).filter((token): token is string => !!token);
            if (tokens.length > 0) {
                await sendNotification(
                    tokens,
                    newSyllabus.exam.examName,
                    description || "New Syllabus Added",
                    { type: "syllabus" }
                );
            }
        }
    } catch (notifyError) {
        console.error("Failed to send push notifications:", notifyError);
    }

    res.status(201).json({
        message: "Syllabus created successfully",
        data: {
            ...newSyllabus,
            createdAt: toIST(newSyllabus.createdAt),
            updatedAt: toIST(newSyllabus.updatedAt),
            syllabusFiles: (newSyllabus.syllabusFiles as string[]).map(file => `${getHost()}${file}`)
        },
    });
});

export const getAllSyllabus = asyncHandler(async (req: Request, res: Response) => {
    const user = req.user;
    const { search = "", page = "1", limit = "10", exam } = req.query;
    const pageNum = Number(page);
    const limitNum = Number(limit);
    const skip = (pageNum - 1) * limitNum;

    const where: any = {};
    if (search) {
        where.OR = [
            { description: { contains: search as string } },
            { exam: { examName: { contains: search as string } } },
        ];
    }

    const currentExamId = exam ? null : await getStudentCurrentExamId(req);
    if (exam) {
        where.examId = exam as string;
    } else if (currentExamId && user.role === "STUDENT") {
        where.examId = currentExamId;
    }

    const whereClause: any = {
        institutionId: user.institutionId,
        ...where
    };

    const [total, syllabuses] = await Promise.all([
        prisma.syllabus.count({ where: whereClause }),
        prisma.syllabus.findMany({
            where: whereClause,
            include: { exam: { select: { id: true, examName: true } } },
            orderBy: { createdAt: 'desc' },
            skip,
            take: limitNum,
        })
    ]);

    const processedData = syllabuses.map((item) => ({
        ...item,
        createdAt: toIST(item.createdAt),
        updatedAt: toIST(item.updatedAt),
        exam: item.examId,
        examName: item.exam.examName,
        syllabusFiles: ((item.syllabusFiles as string[]) || []).map(f => `${getHost()}${f}`),
    }));

    res.status(200).json({
        data: processedData,
        meta: {
            total,
            page: pageNum,
            limit: limitNum,
            totalPages: Math.ceil(total / limitNum),
        },
    });
});

export const getSyllabusById = asyncHandler(async (req: Request, res: Response) => {
    const { id } = req.params;
    const user = req.user;
    const currentExamId = await getStudentCurrentExamId(req);

    const syllabus = await prisma.syllabus.findUnique({
        where: {
            id,
            institutionId: user.institutionId,
            ...(user.role === "STUDENT" && currentExamId ? { examId: currentExamId } : {}),
        },
        include: { exam: { select: { examName: true } } }
    });

    if (!syllabus) {
        res.status(404).json({ message: "Syllabus not found" });
        return;
    }

    res.status(200).json({
        data: {
            ...syllabus,
            createdAt: toIST(syllabus.createdAt),
            updatedAt: toIST(syllabus.updatedAt),
            syllabusFiles: ((syllabus.syllabusFiles as string[]) || []).map(file => `${getHost()}${file}`)
        }
    });
});

export const updateSyllabus = asyncHandler(async (req: Request, res: Response) => {
    const { id } = req.params;
    const { exam, description, existingFiles } = req.body;
    const user = req.user;

    const currentSyllabus = await prisma.syllabus.findUnique({ where: { id, institutionId: user.institutionId } });
    if (!currentSyllabus) {
        res.status(404).json({ message: "Syllabus not found" });
        return;
    }

    const updates: any = {};
    if (description) updates.description = description;
    if (exam) updates.examId = exam;

    let finalFiles: string[] = [];
    if (existingFiles) {
        const paths = Array.isArray(existingFiles) ? existingFiles : [existingFiles];
        finalFiles = paths.map(p => p.includes(getHost()) ? p.replace(getHost(), "") : p);
    }

    if (req.files && (req.files as Express.Multer.File[]).length > 0) {
        const newFiles = (req.files as Express.Multer.File[]).map(file => toPublicPath(file.path));
        finalFiles = [...finalFiles, ...newFiles];
    }

    const filesToDelete = (currentSyllabus.syllabusFiles as string[]).filter(f => !finalFiles.includes(f));
    filesToDelete.forEach(file => deleteFile(file));

    updates.syllabusFiles = finalFiles;

    const updatedSyllabus = await prisma.syllabus.update({
        where: { id },
        data: updates,
        include: { exam: { select: { examName: true } } }
    });

    res.status(200).json({
        message: "Syllabus updated successfully",
        data: {
            ...updatedSyllabus,
            createdAt: toIST(updatedSyllabus.createdAt),
            updatedAt: toIST(updatedSyllabus.updatedAt),
            syllabusFiles: (updatedSyllabus.syllabusFiles as string[]).map(file => `${getHost()}${file}`)
        },
    });
});

export const deleteSyllabus = asyncHandler(async (req: Request, res: Response) => {
    const { id } = req.params;
    const user = req.user;
    const syllabus = await prisma.syllabus.delete({ where: { id, institutionId: user.institutionId } });

    if (syllabus.syllabusFiles && (syllabus.syllabusFiles as string[]).length > 0) {
        (syllabus.syllabusFiles as string[]).forEach(file => deleteFile(file));
    }

    res.status(200).json({ message: "Syllabus deleted successfully" });
});
