import { Request, Response } from "express";
import ProtocolService from "../../service/protocolService";
import ProtocolFactory from "../../factory/admin/protocol";
import ProtocolAgendaService from "../../service/protocolAgendaService";
import ProtocolAgendaFactory from "../../factory/admin/protocolAgenda";
import ProtocolDecisionService from "../../service/protocolDecisionService";
import ProtocolDecisionFactory from "../../factory/admin/protocolDecision";
import ProtocolPresenceService from "../../service/protocolPrecenseService";
import ProtocolPresenceFactory from "../../factory/admin/protocolPresence";
import ProtocolVotingService from "../../service/protocolVotingService";
import ProtocolVotingFactory from "../../factory/admin/protocolVoting";
import { CreateProtocolCommand, SynchronizeProtocolCommand } from "../../command/protocolCommand";
import ProtocolCommandHandler from "../../command/protocolCommandHandler";
import { SynchronizeProtocolAgendaCommand } from "../../command/protocolAgendaCommand";
import ProtocolAgendaCommandHandler from "../../command/protocolAgendaCommandHandler";
import { ProtocolAgendaViewModel } from "../../viewmodel/admin/protocolAgenda.models";
import ProtocolDecisionCommandHandler from "../../command/protocolDecisionCommandHandler";
import { ProtocolDecisionViewModel } from "../../viewmodel/admin/protocolDecision.models";
import ProtocolPresenceCommandHandler from "../../command/protocolPresenceCommandHandler";
import { SynchronizeProtocolPresenceCommand } from "../../command/protocolPresenceCommand";
import { SynchronizeProtocolDecisionCommand } from "../../command/protocolDecisionCommand";
import { SynchronizeProtocolVotingCommand } from "../../command/protocolVotingCommand";
import { ProtocolVotingViewModel } from "../../viewmodel/admin/protocolVoting.models";
import ProtocolVotingCommandHandler from "../../command/protocolVotingCommandHandler";
import { PdfExport } from "../../helpers/pdfExport";
import ProtocolPrintoutService from "../../service/protocolPrintoutService";
import ProtocolPrintoutFactory from "../../factory/admin/protocolPrintout";
import { CreateProtocolPrintoutCommand } from "../../command/protocolPrintoutCommand";
import ProtocolPrintoutCommandHandler from "../../command/protocolPrintoutCommandHandler";
import { FileSystemHelper } from "../../helpers/fileSystemHelper";
import { ProtocolPresenceViewModel } from "../../viewmodel/admin/protocolPresence.models";

/**
 * @description get all protocols
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getAllProtocols(req: Request, res: Response): Promise<any> {
  let offset = parseInt((req.query.offset as string) ?? "0");
  let count = parseInt((req.query.count as string) ?? "25");
  let [protocols, total] = await ProtocolService.getAll(offset, count);

  res.json({
    protocols: ProtocolFactory.mapToBase(protocols),
    total: total,
    offset: offset,
    count: count,
  });
}

/**
 * @description get protocol by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getProtocolById(req: Request, res: Response): Promise<any> {
  let id = parseInt(req.params.id);
  let protocol = await ProtocolService.getById(id);

  res.json(ProtocolFactory.mapToSingle(protocol));
}

/**
 * @description get protocol agenda by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getProtocolAgendaById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);

  let agenda = await ProtocolAgendaService.getAll(protocolId);

  res.json(ProtocolAgendaFactory.mapToBase(agenda));
}

/**
 * @description get protocol decisions by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getProtocolDecisonsById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);

  let decisions = await ProtocolDecisionService.getAll(protocolId);

  res.json(ProtocolDecisionFactory.mapToBase(decisions));
}

/**
 * @description get protocol precense by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getProtocolPrecenseById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);

  let presence = await ProtocolPresenceService.getAll(protocolId);

  res.json(ProtocolPresenceFactory.mapToBase(presence));
}

/**
 * @description get protocol votings by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getProtocolVotingsById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);

  let votings = await ProtocolVotingService.getAll(protocolId);

  res.json(ProtocolVotingFactory.mapToBase(votings));
}

/**
 * @description get protocol printouts by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getProtocolPrintoutsById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);

  let printouts = await ProtocolPrintoutService.getAll(protocolId);

  res.json(ProtocolPrintoutFactory.mapToBase(printouts));
}

/**
 * @description get protocol printout by id and print
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getProtocolPrintoutByIdAndPrint(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);
  let printoutId = parseInt(req.params.printoutId);

  let printout = await ProtocolPrintoutService.getById(printoutId, protocolId);

  let filepath = FileSystemHelper.formatPath("protocol", printout.filename);

  res.sendFile(filepath, {
    headers: {
      "Content-Type": "application/pdf",
    },
  });
}

/**
 * @description create protocol
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function createProtocol(req: Request, res: Response): Promise<any> {
  let title = req.body.title;
  let date = req.body.date;

  let createProtocol: CreateProtocolCommand = {
    title,
    date,
  };
  let id = await ProtocolCommandHandler.create(createProtocol);

  res.send(id);
}

/**
 * @description create protocol agenda by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function createProtocolAgendaById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);

  let agenda = await ProtocolAgendaCommandHandler.create(protocolId);

  res.send(agenda);
}

/**
 * @description create protocol decisions by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function createProtocolDecisonsById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);

  let decision = await ProtocolDecisionCommandHandler.create(protocolId);

  res.send(decision);
}

/**
 * @description create protocol votings by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function createProtocolVotingsById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);

  let voting = await ProtocolVotingCommandHandler.create(protocolId);

  res.send(voting);
}

/**
 * @description create protocol printout by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function createProtocolPrintoutById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);
  let protocol = await ProtocolService.getById(protocolId);
  let agenda = await ProtocolAgendaService.getAll(protocolId);
  let decisions = await ProtocolDecisionService.getAll(protocolId);
  let presence = await ProtocolPresenceService.getAll(protocolId);
  let votings = await ProtocolVotingService.getAll(protocolId);
  let iteration = await ProtocolPrintoutService.getCount(protocolId);

  let title = `${protocol.title} - ${new Date(protocol.date).toLocaleDateString("de-DE", {
    day: "2-digit",
    month: "long",
    year: "numeric",
  })}`;

  let filename = `${new Date().toISOString().split("T")[0]}_${iteration + 1}_Protokoll_${protocol.title.replace(
    /[^a-zA-Z0-9]/g,
    ""
  )}`;

  await PdfExport.renderFile({
    template: "protocol",
    title,
    filename,
    folder: "protocol",
    data: {
      title: protocol.title,
      summary: protocol.summary,
      iteration: iteration + 1,
      date: new Date(protocol.date).toLocaleDateString("de-DE", {
        weekday: "long",
        day: "2-digit",
        month: "2-digit",
        year: "numeric",
      }),
      start: protocol.starttime,
      end: protocol.endtime,
      agenda,
      decisions,
      presence: presence.filter((p) => !p.absent).map((p) => p.member),
      absent: presence.filter((p) => p.absent).map((p) => p.member),
      votings,
    },
  });

  let printout: CreateProtocolPrintoutCommand = {
    title,
    iteration: iteration + 1,
    filename: `${filename}.pdf`,
    protocolId,
  };
  await ProtocolPrintoutCommandHandler.create(printout);

  res.sendStatus(204);
}

/**
 * @description synchronize protocol by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function synchronizeProtocolById(req: Request, res: Response): Promise<any> {
  let id = parseInt(req.params.id);
  let title = req.body.title;
  let date = req.body.date;
  let starttime = req.body.starttime;
  let endtime = req.body.endtime;
  let summary = req.body.summary;

  let syncProtocol: SynchronizeProtocolCommand = {
    id,
    title,
    date,
    starttime,
    endtime,
    summary,
  };
  await ProtocolCommandHandler.sync(syncProtocol);

  res.sendStatus(204);
}

/**
 * @description synchronize protocol agenda by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function synchronizeProtocolAgendaById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);
  let agenda = req.body.agenda as Array<ProtocolAgendaViewModel>;

  let syncAgenda: Array<SynchronizeProtocolAgendaCommand> = agenda.map(
    (a: ProtocolAgendaViewModel): SynchronizeProtocolAgendaCommand => ({
      id: a.id ?? null,
      topic: a.topic,
      context: a.context,
      protocolId,
    })
  );
  await ProtocolAgendaCommandHandler.sync(syncAgenda);

  res.sendStatus(204);
}

/**
 * @description synchronize protocol decisions by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function synchronizeProtocolDecisonsById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);
  let decisions = req.body.decisions as Array<ProtocolDecisionViewModel>;

  let syncDecision: Array<SynchronizeProtocolDecisionCommand> = decisions.map(
    (d: ProtocolDecisionViewModel): SynchronizeProtocolDecisionCommand => ({
      id: d.id ?? null,
      topic: d.topic,
      context: d.context,
      protocolId,
    })
  );
  await ProtocolDecisionCommandHandler.sync(syncDecision);

  res.sendStatus(204);
}

/**
 * @description synchronize protocol votings by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function synchronizeProtocolVotingsById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);
  let votings = req.body.votings as Array<ProtocolVotingViewModel>;

  let syncVoting: Array<SynchronizeProtocolVotingCommand> = votings.map(
    (d: ProtocolVotingViewModel): SynchronizeProtocolVotingCommand => ({
      id: d.id ?? null,
      topic: d.topic,
      context: d.context,
      favour: d.favour,
      abstain: d.abstain,
      against: d.abstain,
      protocolId,
    })
  );
  await ProtocolVotingCommandHandler.sync(syncVoting);

  res.sendStatus(204);
}

/**
 * @description synchronize protocol precense by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function synchronizeProtocolPrecenseById(req: Request, res: Response): Promise<any> {
  let protocolId = parseInt(req.params.protocolId);
  let presence = req.body.presence as Array<ProtocolPresenceViewModel>;

  let syncPresence: SynchronizeProtocolPresenceCommand = {
    members: presence.map((p) => ({
      memberId: p.memberId,
      absent: p.absent,
    })),
    protocolId,
  };
  await ProtocolPresenceCommandHandler.sync(syncPresence);

  res.sendStatus(204);
}