/* eslint-disable @next/next/no-img-element */
import Table from "@/components/High/Table";
import Checkbox from "@/components/Low/Checkbox";
import Separator from "@/components/Low/Separator";
import { useAllowedEntities } from "@/hooks/useEntityPermissions";
import { Session } from "@/hooks/useSessions";
import { Entity, EntityWithRoles } from "@/interfaces/entity";
import { Exam } from "@/interfaces/exam";
import { Assignment, AssignmentResult } from "@/interfaces/results";
import { StudentUser, User } from "@/interfaces/user";
import { sessionOptions } from "@/lib/session";
import { filterBy, findBy, mapBy, redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api";
import { getEntitiesAssignments } from "@/utils/assignments.be";
import { getEntitiesWithRoles } from "@/utils/entities.be";
import { getExamsByIds } from "@/utils/exams.be";
import { findAllowedEntities } from "@/utils/permissions";
import { getSessionsByAssignments } from "@/utils/sessions.be";
import { isAdmin } from "@/utils/users";
import { getEntitiesUsers } from "@/utils/users.be";
import { createColumnHelper } from "@tanstack/react-table";
import axios from "axios";
import { clsx } from "clsx";
import { withIronSessionSsr } from "iron-session/next";
import { capitalize, orderBy } from "lodash";
import moment from "moment";
import Head from "next/head";
import Link from "next/link";
import { useEffect, useMemo, useState } from "react";
import ReactDatePicker from "react-datepicker";
import { BsBank, BsChevronLeft, BsX } from "react-icons/bs";

interface Props {
  user: User;
  students: StudentUser[];
  entities: EntityWithRoles[];
  assignments: Assignment[];
  sessions: Session[];
  exams: Exam[];
}

export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
  const user = await requestUser(req, res);
  if (!user) return redirect("/login");

  const entityIDS = mapBy(user.entities, "id") || [];
  const entities = await getEntitiesWithRoles(
    isAdmin(user) ? undefined : entityIDS
  );
  const allowedEntities = findAllowedEntities(
    user,
    entities,
    "view_entity_statistics"
  );

  if (allowedEntities.length === 0) return redirect("/");

  const studentsAllowedEntities = findAllowedEntities(
    user,
    entities,
    "view_students"
	);
	
  const [students, assignments] = await Promise.all([
    getEntitiesUsers(mapBy(studentsAllowedEntities, "id"), { type: "student" }),
    getEntitiesAssignments(mapBy(entities, "id")),
  ]);
	
  const [sessions, exams] = await Promise.all([
    getSessionsByAssignments(mapBy(assignments, "id")),
    getExamsByIds(assignments.flatMap((a) => a.exams)),
  ]);

  return {
    props: serialize({
      user,
      students,
      entities: allowedEntities,
      assignments,
      sessions,
      exams,
    }),
  };
}, sessionOptions);

interface Item {
  student: StudentUser;
  result?: AssignmentResult;
  assignment: Assignment;
  exams: Exam[];
  entity: Entity;
  session?: Session;
}

const columnHelper = createColumnHelper<Item>();

export default function Statistical({
  user,
  students,
  entities,
  assignments,
  sessions,
  exams,
}: Props) {
  const [startDate, setStartDate] = useState<Date>(new Date());
  const [endDate, setEndDate] = useState<Date | null>(
    moment().add(1, "month").toDate()
  );
  const [selectedEntities, setSelectedEntities] = useState<string[]>([]);
  const [isDownloading, setIsDownloading] = useState(false);

  const entitiesAllowDownload = useAllowedEntities(
    user,
    entities,
    "download_statistics_report"
  );

  const resetDateRange = () => {
    const orderedAssignments = orderBy(assignments, ["startDate"], ["asc"]);
    setStartDate(
      moment(
        orderedAssignments.shift()?.startDate || "2024-01-01T00:00:01.986Z"
      ).toDate()
    );
    setEndDate(moment().add(1, "month").toDate());
  };

  useEffect(resetDateRange, [assignments]);

  const updateDateRange = (dates: [Date, Date | null]) => {
    const [start, end] = dates;
    setStartDate(start!);
    setEndDate(end);
  };

  const toggleEntity = (id: string) =>
    setSelectedEntities((prev) =>
      prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
    );

  const renderAssignmentResolution = (entityID: string) => {
    const entityAssignments = filterBy(assignments, "entity", entityID);
    const total = entityAssignments.reduce(
      (acc, curr) => acc + curr.assignees.length,
      0
    );
    const results = entityAssignments.reduce(
      (acc, curr) => acc + curr.results.length,
      0
    );

    return `${results}/${total}`;
  };

  const totalAssignmentResolution = useMemo(() => {
    const total = assignments.reduce(
      (acc, curr) => acc + curr.assignees.length,
      0
    );
    const results = assignments.reduce(
      (acc, curr) => acc + curr.results.length,
      0
    );

    return { results, total };
  }, [assignments]);

  const filteredAssignments = useMemo(() => {
    if (!startDate && !endDate) return assignments;
    const startDateFiltered = startDate
      ? assignments.filter((a) =>
          moment(a.startDate).isSameOrAfter(moment(startDate))
        )
      : assignments;
    return endDate
      ? startDateFiltered.filter((a) =>
          moment(a.endDate).isSameOrBefore(moment(endDate))
        )
      : startDateFiltered;
  }, [startDate, endDate, assignments]);

  const data: Item[] = useMemo(
    () =>
      filteredAssignments
        .filter((a) => selectedEntities.includes(a.entity || ""))
        .flatMap((a) =>
          a.assignees.map((x) => {
            const result = findBy(a.results, "user", x);
            const student = findBy(students, "id", x);
            const entity = findBy(entities, "id", a.entity);
            const assignmentExams = exams.filter((e) =>
              a.exams
                .map((x) => `${x.id}_${x.module}`)
                .includes(`${e.id}_${e.module}`)
            );
            const session = sessions.find(
              (s) => s.assignment?.id === a.id && s.user === x
            );

            if (!student) return undefined;
            return {
              student,
              result,
              assignment: a,
              exams: assignmentExams,
              session,
              entity,
            };
          })
        )
        .filter((x) => !!x) as Item[],
    [students, selectedEntities, filteredAssignments, exams, sessions, entities]
  );

  const sortedData: Item[] = useMemo(
    () =>
      data.sort((a, b) => {
        const aTotalScore =
          a.result?.stats
            .filter((x) => !x.isPractice)
            .reduce((acc, curr) => acc + curr.score.correct, 0) || 0;
        const bTotalScore =
          b.result?.stats
            .filter((x) => !x.isPractice)
            .reduce((acc, curr) => acc + curr.score.correct, 0) || 0;

        return bTotalScore - aTotalScore;
      }),
    [data]
  );

  const downloadExcel = async () => {
    setIsDownloading(true);

    const request = await axios.post(
      "/api/statistical",
      {
        entities: entities.filter((e) => selectedEntities.includes(e.id)),
        items: data,
        assignments: filteredAssignments,
        startDate,
        endDate,
      },
      {
        responseType: "blob",
      }
    );

    const href = URL.createObjectURL(request.data);
    const link = document.createElement("a");
    link.href = href;
    link.setAttribute(
      "download",
      `statistical_${new Date().toISOString()}.xlsx`
    );
    document.body.appendChild(link);
    link.click();

    document.body.removeChild(link);
    URL.revokeObjectURL(href);

    setIsDownloading(false);
  };

  const columns = [
    columnHelper.accessor("student.name", {
      header: "Student",
      cell: (info) => info.getValue(),
    }),
    columnHelper.accessor("student.studentID", {
      header: "Student ID",
      cell: (info) => info.getValue(),
    }),
    columnHelper.accessor("student.email", {
      header: "E-mail",
      cell: (info) => info.getValue(),
    }),
    columnHelper.accessor("entity.label", {
      header: "Entity",
      cell: (info) => info.getValue(),
    }),
    columnHelper.accessor("assignment.name", {
      header: "Assignment",
      cell: (info) => info.getValue(),
    }),
    columnHelper.accessor("assignment.startDate", {
      header: "Date",
      cell: (info) => moment(info.getValue()).format("DD/MM/YYYY"),
    }),
    columnHelper.accessor("result", {
      header: "Progress",
      cell: (info) => {
        const student = info.row.original.student;
        const session = info.row.original.session;

        if (!student.lastLogin)
          return <span className="text-mti-red-dark">Never logged in</span>;
        if (info.getValue())
          return (
            <span className="text-mti-green font-semibold">Submitted</span>
          );
        if (!session) return <span className="text-mti-rose">Not started</span>;

        return (
          <span className="font-semibold">
            {capitalize(session.exam?.module || "")} Module, Part{" "}
            {session.partIndex + 1}, Exercise {session.exerciseIndex + 1}
          </span>
        );
      },
    }),
  ];

  return (
    <>
      <Head>
        <title>Statistical | EnCoach</title>
        <meta
          name="description"
          content="A training platform for the IELTS exam provided by the Muscat Training Institute and developed by eCrop."
        />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <>
        <div className="flex flex-col gap-4">
          <div className="flex items-center justify-between w-full">
            <div className="flex items-center gap-2">
              <Link
                href="/dashboard"
                className="text-mti-purple hover:text-mti-purple-dark transition ease-in-out duration-300 text-xl"
              >
                <BsChevronLeft />
              </Link>
              <h2 className="font-bold text-2xl">Statistical</h2>
            </div>
            <Checkbox
              onChange={(value) =>
                setSelectedEntities(value ? mapBy(entities, "id") : [])
              }
              isChecked={selectedEntities.length === entities.length}
            >
              Select All
            </Checkbox>
          </div>
          <Separator />
        </div>

        <section className="flex flex-col gap-3">
          <div className="w-full flex items-center justify-between gap-4 flex-wrap">
            {entities.map((entity) => (
              <button
                onClick={() => toggleEntity(entity.id)}
                className={clsx(
                  "flex flex-col items-center justify-between gap-3 border-2 drop-shadow rounded-xl bg-white p-8 px-2 w-48 h-52",
                  "transition ease-in-out duration-300 hover:shadow-xl hover:border-mti-purple",
                  selectedEntities.includes(entity.id) &&
                    "border-mti-purple text-mti-purple"
                )}
                key={entity.id}
              >
                <BsBank size={48} />
                <div className="flex flex-col gap-1">
                  <span>{entity.label}</span>
                  <span className={clsx("font-semibold")}>
                    {renderAssignmentResolution(entity.id)}
                  </span>
                </div>
              </button>
            ))}
          </div>

          <div className="flex items-center justify-between">
            <div className="flex items-center gap-2">
              <ReactDatePicker
                className={clsx(
                  "p-6 px-12 w-full flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer",
                  "hover:border-mti-purple tooltip",
                  "transition duration-300 ease-in-out"
                )}
                dateFormat="dd/MM/yyyy"
                selectsRange
                selected={startDate}
                onChange={updateDateRange}
                startDate={startDate}
                endDate={endDate}
              />
              {startDate !== null && endDate !== null && (
                <button
                  onClick={resetDateRange}
                  className="transition ease-in-out duration-300 rounded-full p-2 hover:bg-mti-gray-cool/10"
                >
                  <BsX size={24} />
                </button>
              )}
            </div>
            <span className="font-semibold text-lg pr-1">
              Total: {totalAssignmentResolution.results} /{" "}
              {totalAssignmentResolution.total}
            </span>
          </div>
        </section>

        {selectedEntities.length > 0 && (
          <Table
            columns={columns}
            data={sortedData}
            searchFields={[
              ["student", "name"],
              ["student", "email"],
              ["student", "studentID"],
              ["exams", "id"],
              ["assignment", "name"],
            ]}
            searchPlaceholder="Search by student, assignment or exam..."
            onDownload={
              entitiesAllowDownload.length > 0 ? downloadExcel : undefined
            }
            isDownloadLoading={isDownloading}
          />
        )}
      </>
    </>
  );
}
