// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type {NextApiRequest, NextApiResponse} from "next";
import {app} from "@/firebase";
import {getFirestore, collection, getDocs, query, where, setDoc, doc} from "firebase/firestore";
import {withIronSessionApiRoute} from "iron-session/next";
import {sessionOptions} from "@/lib/session";
import {uuidv4} from "@firebase/util";
import { Module } from "@/interfaces";
import { getExams } from "@/utils/exams.be";
import { Exam } from "@/interfaces/exam";
import { flatten } from "lodash";

const db = getFirestore(app);

export default withIronSessionApiRoute(handler, sessionOptions);

async function handler(req: NextApiRequest, res: NextApiResponse) {
	if (!req.session.user) {
		res.status(401).json({ok: false});
		return;
	}

	if (req.method === "GET") return GET(req, res);
	if (req.method === "POST") return POST(req, res);

	res.status(404).json({ok: false});
}

async function GET(req: NextApiRequest, res: NextApiResponse) {
	const q = query(collection(db, "assignments"));
	const snapshot = await getDocs(q);

	const docs = snapshot.docs.map((doc) => ({
		id: doc.id,
		...doc.data(),
	}));

	res.status(200).json(docs);
}

interface ExamWithUser {
  module: Module;
  id: string;
  assignee: string;
}

function getRandomIndex(arr: any[]): number {
  const randomIndex = Math.floor(Math.random() * arr.length);
  return randomIndex;
}

const generateExams = async (
  generateMultiple: Boolean,
  selectedModules: Module[],
  assignees: string[]
): Promise<ExamWithUser[]> => {
  if (generateMultiple) {
    // for optimization purposes, it would be better to create a new endpoint that returned the answers for all users at once
    const allExams = await assignees.map(async (assignee) => {
      const selectedModulePromises = await selectedModules.map(
        async (module: Module) => {
          try {
            const exams: Exam[] = await getExams(db, module, "true", assignee);

            const exam = exams[getRandomIndex(exams)];
            if (exam) {
              return { module: exam.module, id: exam.id, assignee };
            }
            return null;
          } catch (e) {
            console.error(e);
            return null;
          }
        },
        []
      );
      const newModules = await Promise.all(selectedModulePromises);

      return newModules;
    }, []);

    const exams = flatten(await Promise.all(allExams)).filter(
      (x) => x !== null
    ) as ExamWithUser[];
    return exams;
  }

  const selectedModulePromises = await selectedModules.map(
    async (module: Module) => {
      const exams: Exam[] = await getExams(db, module, "false", undefined);
      const exam = exams[getRandomIndex(exams)];

      if (exam) {
        return { module: exam.module, id: exam.id };
      }
      return null;
    }
  );

  const exams = await Promise.all(selectedModulePromises);
  const examesFiltered = exams.filter((x) => x !== null) as ExamWithUser[];
  return flatten(
    assignees.map((assignee) =>
      examesFiltered.map((exam) => ({ ...exam, assignee }))
    )
  );
};

async function POST(req: NextApiRequest, res: NextApiResponse) {
  const {
    selectedModules,
    assignees,
    // Generarte multiple true would generate an unique exam for eacah user
    // false would generate the same exam for all usersa
    generateMultiple = false,
    ...body
  } = req.body as {
    selectedModules: Module[];
    assignees: string[];
    generateMultiple: Boolean;
  };

  const exams: ExamWithUser[] = await generateExams(
    generateMultiple,
    selectedModules,
    assignees
  );
  if (exams.length === 0) {
    res
      .status(400)
      .json({ ok: false, error: "No exams found for the selected modules" });
    return;
  }

  await setDoc(doc(db, "assignments", uuidv4()), {
    assigner: req.session.user?.id,
    assignees,
    results: [],
    exams,
    ...body,
  });

  res.status(200).json({ ok: true });
}
