import { Router, Request, Response, NextFunction } from "express";
import { InvalidParameterError } from "../errors/invalidParameterError";
import { titreBase } from "../utils/define";
import { DevoirControleur } from "../controleurs/devoirControleur";
import { LoginControlleur } from "../controleurs/loginControleur";
import { coursController } from "../locator";
import { Cours } from "../classes/cours";
import { COOKIE_TOKEN } from "../utils/constant";
import { getSystemErrorMap } from "util";
import { Enseignant } from "../classes/enseignant";
import { Devoir } from "../classes/devoir";
import url from "url";

import { validateDateStartBeforeEnd } from "../utils/date-utils";
import { generateGrade } from "../utils/generate-grade";

export class DevoirRouter {
  private _router: Router;
  private _devoirControleur: DevoirControleur;
  private _loginControleur: LoginControlleur;

  get router() {
    return this._router;
  }

  constructor() {
    this._router = Router();
    this._devoirControleur = new DevoirControleur();
    this._loginControleur = new LoginControlleur();
    this.init();
  }

  private _errorCode500(
    error: any,
    req: Request,
    res: Response<any, Record<string, any>>
  ) {
    req.flash("error", error.message);
    res.status(error.code).json({ error: error.toString() });
  }

  public async recupererListeCours(
    req: Request,
    res: Response,
    next: NextFunction
  ) {
    try {
      const user = (await this._loginControleur.getUtilisateur(
        req.cookies[COOKIE_TOKEN]
      )) as Enseignant;

      const coursActifs: Cours[] = [];
      (coursController.recupererCours(user) as Cours[]).forEach((cours) => {
        if (cours.getEnseignantId() === user.id && cours.actif)
          coursActifs.push(cours);
      });

      const devoirCounterMap = new Map<string, number>();
      coursActifs.forEach((cours) => {
        console.log(cours.liste_travaux);
        devoirCounterMap.set(cours.id, 0);
        cours.liste_travaux.forEach((travail) => {
          if (travail.type === "DEVOIR") {
            devoirCounterMap.set(cours.id, devoirCounterMap.get(cours.id) + 1);
          }
        });
      });
      console.log("map", devoirCounterMap);

      res.render("devoir/cours-actifs", {
        title: `${titreBase}`,
        user,
        coursActifs,
        devoirCounterMap,
      });
    } catch (error) {
      this._errorCode500(error, req, res);
    }
  }

  public async recupererListeDevoirs(
    req: Request,
    res: Response,
    next: NextFunction
  ) {
    try {
      const code = req.params.code;
      const user = await this._loginControleur.getUtilisateur(
        req.cookies[COOKIE_TOKEN]
      );
      if (user) {
        const cours = coursController
          .recupererCours(user)
          .find((cours) => cours.id === code);

        const devoirs = this._devoirControleur.recupererListeDevoir(cours);

        res.render("devoir/liste-devoirs", {
          title: `${titreBase}`,
          user,
          cours,
          devoirs,
        });
      }
    } catch (error) {
      this._errorCode500(error, req, res);
    }
  }

  public async formulaireDevoir(
    req: Request,
    res: Response,
    next: NextFunction
  ) {
    try {
      const user = await this._loginControleur.getUtilisateur(
        req.cookies[COOKIE_TOKEN]
      );
      const code = req.params.code;
      const { nom, description, date_debut, date_fin, note_max, actif, error } =
        req.query;
      if (user && code) {
        res.render("devoir/formuler-devoir", {
          title: `${titreBase}`,
          user,
          code,
          devoir: {
            nom: nom,
            description: description,
            date_debut: date_debut,
            date_fin: date_fin,
            note_max: note_max,
            actif: actif,
          },
          messages: { error: error },
        });
      }
    } catch (error) {
      this._errorCode500(error, req, res);
    }
  }

  public async ajouterDevoir(req: Request, res: Response, next: NextFunction) {
    try {
      const user = await this._loginControleur.getUtilisateur(
        req.cookies[COOKIE_TOKEN]
      );
      const {
        cours_id,
        nom,
        description,
        date_debut,
        date_fin,
        note_max,
        actif,
      } = req.body;

      let path = `/devoir/devoirs/${cours_id}`;

      if (!validateDateStartBeforeEnd(date_debut, date_fin)) {
        console.log("date validation failed");
        if (user && cours_id) {
          console.log("date redirect");
          path = url.format({
            pathname: `/devoir/nouveau-devoir/${cours_id}`,
            query: {
              nom: nom,
              description: description,
              date_debut: date_debut,
              date_fin: date_fin,
              note_max: note_max,
              actif: actif,
              error:
                "Date invalide, la date de fin est avant la date de début.",
            },
          });
        }
      } else {
        if (user && cours_id) {
          const cours = coursController
            .recupererCours(user)
            .find((c) => c.id === cours_id);

          this._devoirControleur.ajouterDevoir(
            cours,
            new Devoir(nom, description, date_debut, date_fin, note_max, actif)
          );
        }
      }
      res.redirect(path);
    } catch (error) {
      console.log(error.message);
      this._errorCode500(error, req, res);
    }
  }

  public async devoir(req: Request, res: Response, next: NextFunction) {
    try {
      const user = await this._loginControleur.getUtilisateur(
        req.cookies[COOKIE_TOKEN]
      );
      const code = req.params.code;
      const id = req.params.id;
      if (user && id) {
        const cours = coursController
          .recupererCours(user)
          .find((c) => c.id === code);

        const devoir =
          this._devoirControleur.recupererDevoir(cours, id) || undefined;

        let grades = generateGrade(cours, devoir as Devoir);

        const { sort } = req.query;
        if (sort) {
          if (sort === "alpha") {
            grades = grades.sort((g1, g2) => {
              const nameA = g1.student.prenom.toUpperCase();
              const nameB = g2.student.prenom.toUpperCase();
              if (nameA < nameB) {
                return -1;
              }
              if (nameA > nameB) {
                return 1;
              }
              return 0;
            });
          } else if (sort === "note") {
            grades = grades.sort((g1, g2) => g1.note - g2.note);
          }
        }

        res.render("devoir/devoir", {
          title: `${titreBase}`,
          user,
          devoir,
          cours,
          code,
          grades,
        });
      }
    } catch (error) {
      this._errorCode500(error, req, res);
    }
  }

  public async modifierDevoir(req: Request, res: Response, next: NextFunction) {
    try {
      const user = await this._loginControleur.getUtilisateur(
        req.cookies[COOKIE_TOKEN]
      );
      const {
        devoir_id,
        cours_id,
        nom,
        description,
        date_debut,
        date_fin,
        note_max,
        actif,
      } = req.body;

      let path = `/devoir/devoir/${cours_id}/${devoir_id}`;

      if (!validateDateStartBeforeEnd(date_debut, date_fin)) {
        if (user && cours_id) {
          path = url.format({
            pathname: `/devoir/reformuler-devoir/${cours_id}/${devoir_id}`,
            query: {
              description: description,
              date_debut: date_debut,
              date_fin: date_fin,
              note_max: note_max,
              actif: actif,
              error:
                "Date invalide, la date de fin est avant la date de début.",
            },
          });
        }
      } else {
        if (user && cours_id) {
          const cours = coursController
            .recupererCours(user)
            .find((c) => c.id === cours_id);

          this._devoirControleur.modifierDevoir(
            cours,
            new Devoir(
              nom,
              description,
              date_debut,
              date_fin,
              note_max,
              actif,
              devoir_id
            ),
            devoir_id
          );
        }
      }

      res.redirect(path);
    } catch (error) {
      console.log(error.message);
      this._errorCode500(error, req, res);
    }
  }

  public async retirerDevoir(req: Request, res: Response, next: NextFunction) {
    try {
      const user = await this._loginControleur.getUtilisateur(
        req.cookies[COOKIE_TOKEN]
      );
      const devoir_id = req.body.devoir_id;
      const cours_id = req.body.cours_id;

      console.log("test");
      console.log(devoir_id);

      if (user && devoir_id) {
        const cours = coursController
          .recupererCours(user)
          .find((c) => c.id === cours_id);

        this._devoirControleur.retirerDevoir(cours, devoir_id);

        res.redirect(`/devoir/devoirs/${cours_id}`);
      }
    } catch (error) {
      console.log(error.message);
      this._errorCode500(error, req, res);
    }
  }

  public async visualiserTousDevoir(
    req: Request,
    res: Response,
    next: NextFunction
  ) {
    try {
      const user = await this._loginControleur.getUtilisateur(
        req.cookies[COOKIE_TOKEN]
      );
      if (user) {
        const _cours = coursController.recupererCours(user);
        const devoirMap: Map<Devoir, Cours> =
          this._devoirControleur.recupererTousDevoir(_cours);

        let devoirArr = [];
        devoirMap.forEach((val, key) => {
          devoirArr.push({
            nom: key.nom,
            id: key.id,
            description: key.description,
            cours: val.id,
          });
        });
        res.render("devoir/tous-devoirs", {
          title: `${titreBase}`,
          user: user,
          devoirs: devoirArr,
        });
      }
    } catch (error) {
      this._errorCode500(error, req, res);
    }
  }

  public async reformulerDevoir(
    req: Request,
    res: Response,
    next: NextFunction
  ) {
    try {
      const user = await this._loginControleur.getUtilisateur(
        req.cookies[COOKIE_TOKEN]
      );

      if (user) {
        const devoir_id = req.params.id;
        const cours_id = req.params.cours;
        const cours = coursController
          .recupererCours(user)
          .find((c) => c.id === cours_id);
        const devoir = this._devoirControleur.recupererDevoir(cours, devoir_id);
        const { description, date_debut, date_fin, note_max, actif, error } =
          req.query;
        if (description && date_debut && date_fin && note_max && actif) {
          (devoir as Devoir).edit(
            description,
            date_debut,
            date_fin,
            note_max,
            actif
          );
        }

        res.render("devoir/reformuler-devoir", {
          title: `${titreBase}`,
          user: user,
          devoir: devoir,
          cours: cours,
          messages: { error: error },
        });
      }
    } catch (error) {
      this._errorCode500(error, req, res);
    }
  }

  private init() {
    this._router.get("/", this.recupererListeCours.bind(this));
    this._router.get("/tous-devoirs", this.visualiserTousDevoir.bind(this));
    this._router.get("/devoirs/:code", this.recupererListeDevoirs.bind(this));
    this._router.get("/nouveau-devoir/:code", this.formulaireDevoir.bind(this));
    this._router.post("/nouveau-devoir/", this.ajouterDevoir.bind(this));
    this._router.get("/devoir/:code/:id", this.devoir.bind(this));
    this._router.post("/devoir/:code/:id", this.modifierDevoir.bind(this));
    this._router.delete("/devoir/:code/:id", this.retirerDevoir.bind(this));
    this._router.get(
      "/reformuler-devoir/:cours/:id",
      this.reformulerDevoir.bind(this)
    );
  }
}

export const devoirRouter = new DevoirRouter();
