/**
 *  Copyright © 2025, Luis Andrés Lange <https://javacomm.net>
 *
 *  Previously released under Apache License 2.0; now licensed under MPL 2.0.
 *
 *  This Source Code Form is subject to the terms of the Mozilla Public
 *  License, v. 2.0. If a copy of the MPL was not distributed with this
 *  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 *  ----------------------------------------------------------------------------
 *
 *  Exhibit B - "Incompatible With Secondary Licenses" Notice
 *
 *  This Source Code Form is "Incompatible With Secondary Licenses",
 *  as defined by the Mozilla Public License, v. 2.0.
 *
 *  In short:
 *  - This file may be used, modified, and distributed under MPL 2.0 only.
 *  - It may NOT be relicensed under GPL, LGPL, AGPL, or any other Secondary License.
 *
 *  Rationale:
 *  - Ensures that the code remains MPL-2.0.
 *  - Avoids legal conflicts with GPL-licensed libraries (e.g., VideoLAN).
 *  - Maximizes usability for commercial and security-critical applications.
 *
 */
package net.javacomm.restserver;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response.Status;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TreeMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.javacomm.database.DatabaseException;
import net.javacomm.database.WebdatabaseImpl;
import net.javacomm.protocol.CONFERENCE;
import net.javacomm.transfer.TransferKonferenzraum;
import net.javacomm.transfer.TransferOrganizeConferenceCall;
import net.javacomm.transfer.TransferTelko;



@Path("/telko")
public class Telko {

  final Logger log = LogManager.getLogger(Telko.class);
  private WebdatabaseImpl database;

  public Telko() {
    database = (WebdatabaseImpl) WebdatabaseImpl.getInstance();
  }



  /**
   * Erstelle eine Telefonkonferenz.
   *
   *
   * @param userid
   *                      der Organisator der Telefonkonferenz
   * @param konferenzname
   *                      der Konferenzname
   */
  @Produces(MediaType.TEXT_PLAIN)
  @Consumes(MediaType.TEXT_PLAIN)
  @Path("/erstellen/{userid}/{konferenzname}")
  @PUT
  public boolean createTelko(@PathParam("userid") String userid,
      @PathParam("konferenzname") String konferenzname, String body) {

    net.javacomm.database.entity.Telko result;
    try {
      result = database.createTelefonkonferenz(userid, konferenzname, body);
      return result != null;
    }
    catch (DatabaseException e) {
      log.error(e.getMessage(), e);
      throw new WebApplicationException(e.getMessage());
    }
  }



  /**
   * Lies alle Telefonkonferenzen von {@code userid} ein. Die Teilnehmer sind
   * nicht in {@code TransferTelko} enthalten.
   *
   * @param userid
   *               der Benutzer oder Telkoorganisator
   *
   * @return alle Telefonkonferenzen
   */
  @Produces(MediaType.APPLICATION_JSON)
  @Path("/lesen/{userid}")
  @GET
  public List<TransferTelko> readTelko(@PathParam("userid") String userid) {

    ArrayList<TransferTelko> listTransfer = new ArrayList<>();
    List<net.javacomm.database.entity.Telko> entity;
    try {
      entity = database.readTelefonkonferenzen(userid);
      for (net.javacomm.database.entity.Telko tmp : entity) {

        TransferTelko transfer = new TransferTelko();
        transfer.setAblaufdatum(tmp.getUntil());
        transfer.setBeschreibung(tmp.getTexto());
        transfer.setKonferenzraum(tmp.getKonferenzraum());
        transfer.setOrganisator(tmp.getOrganisator());
        listTransfer.add(transfer);

      }
    }
    catch (DatabaseException e) {
      log.error(e);
      throw new WebApplicationException(e, Status.INTERNAL_SERVER_ERROR);
    }

    return listTransfer;
  }



  /**
   * Lies alle Anwender ein, die den Konferenzraum betreten könnten. Der
   * Rückgabewert sind n-Paare aus (Nickname/Userid). Der Konferenzorganisator ist
   * nicht in den Paaren enthalten, weil er immer an der Konferenz teilnimmt.
   *
   * @param konferenzraum
   *                      der Konferenzraum
   *
   * @return diese Anwender könnten eingeladen werden
   */
  @Produces(MediaType.APPLICATION_JSON)
  @Path("/lesen/users/{konferenzraum}/{organisator}")
  @GET
  public TreeMap<String, String> readRestlicheMitglieder(@PathParam("konferenzraum") String konferenzraum,
      @PathParam("organisator") String organisator) {
    TreeMap<String, String> nicknameUserid = database
        .fetchCandidateConferenceCall(konferenzraum, organisator);
    return nicknameUserid;
  }



  /**
   * Lies alle Telefonkonferenzen aus.
   *
   *
   * @param organisator
   *                    der Konferenzorganisator
   *
   * @return alle Telefonkonferenzen, die dem Anwender gehören
   */
  @Produces(MediaType.APPLICATION_JSON)
  @Path("/organize/{organisator}")
  @GET
  public List<TransferOrganizeConferenceCall> readTelefonkonferenzen(
      @PathParam("organisator") String organisator) {

    ArrayList<TransferOrganizeConferenceCall> liste = new ArrayList<>();
    List<TransferTelko> telefonkonferenzenVonUserid = readTelko(organisator);
    for (TransferTelko telko : telefonkonferenzenVonUserid) {
      TransferOrganizeConferenceCall organize = new TransferOrganizeConferenceCall();
      TreeMap<String, String> konferenzteilnehmer;
      try {
        konferenzteilnehmer = database.readKonferenzteilnehmer(telko.getKonferenzraum(), organisator);
      }
      catch (DatabaseException e) {
        log.error(e);
        throw new WebApplicationException(e, Status.INTERNAL_SERVER_ERROR);
      }
      organize.setZonedAblaufdatum(telko.getAblaufdatum());
      organize.setBeschreibung(telko.getBeschreibung());
      organize.setKonferenzname(telko.getKonferenzraum());
      organize.setTeilnehmer(konferenzteilnehmer);
      liste.add(organize);
    }
    return liste;
  }



  /**
   * Der Beschreibungstext für eine Konferenz wird gespeichert.
   *
   *
   * @param text
   *             ein Beschreibungstext
   */
  @POST
  @Path("/write/description/{userid}")
  @Produces(MediaType.TEXT_PLAIN)
  @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
  public boolean writeBeschreibung(@PathParam("userid") String userid,
      @FormParam("konferenzname") String konferenzname, @FormParam("description") String description) {

    return database.updateKonferenzbeschreibung(userid, konferenzname, description);
  }



  /**
   *
   * @param userid
   *                      der Konferenzorganisator
   * @param konferenzname
   *                      der Konferenzname
   * @param expirydate
   *                      das Ablaufdatum
   *
   * @return {@code true}, das Ablaufdatum konnte gespeichert werden
   */
  @POST
  @Path("/write/expirydate/{userid}/{konferenzname}")
  @Produces(MediaType.TEXT_PLAIN)
  @Consumes(MediaType.TEXT_PLAIN)
  public boolean writeVerfallsdatum(@PathParam("userid") String userid,
      @PathParam("konferenzname") String konferenzname, long expirydate) {

    Instant instant = Instant.ofEpochMilli(expirydate);
    ZoneId zoneId = ZoneId.systemDefault();
    LocalDateTime datetime = LocalDateTime.ofInstant(instant, zoneId);
    return database.writeVerfallsdatum(userid, konferenzname, datetime);
  }



  /**
   * Die Teilnehmerliste an der Telefonkonferenz wird gespeichert. Der
   * Konferenzorganisator ist immer ein Teilnehmer.
   *
   * @param userid
   *                      der Konferenzorganisator
   * @param konferenzname
   *                      der Konferenzname
   * @param positivliste
   *                      die Positivliste enthält die Userids aller Teilnehmer.
   *                      In der Positivliste muss der Konferenzorganisator nicht
   *                      vorkommen. Er wird dann der Positivliste intern
   *                      zugefügt.
   *
   */
  @POST
  @Path("/write/participants/{userid}/{konferenzname}")
  @Produces(MediaType.TEXT_PLAIN)
  @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
  public void writeTeilnehmerliste(@PathParam("userid") String userid,
      @PathParam("konferenzname") String konferenzname, MultivaluedMap<String, String> positivliste) {

    Iterator<List<String>> iterator = positivliste.values().iterator();
    List<String> userids = iterator.next();

    try {
      database.writeKonferenzteilnehmer(userid, konferenzname, userids);
    }
    catch (DatabaseException e) {
      log.error(e.getMessage());
      throw new WebApplicationException();
    }
  }



  /**
   * Liese alle Konferenznamen aus, an denen {@code userid} teilnimmt. Diese
   * Methode wird nicht vom Client aufgerufen.
   *
   * @param userid
   *               ein Anwender
   *
   * @return alle Konfernznamen
   */
  @GET
  @Path("/read/konferenznamen/{userid}")
  @Produces(MediaType.APPLICATION_JSON)
  public List<String> readKonferenzen(@PathParam(value = "userid") String userid) {
    List<TransferKonferenzraum> meineKonferenzen = database.meineKonferenzen(userid);
    ArrayList<String> list = new ArrayList<>();
    meineKonferenzen.forEach((konferenz) -> {
      list.add(konferenz.getKonferenzraum());
    });
    return list;
  }



  /**
   * Alle Konferenzräume werden ermittelt, in welchem der Anwender Mitglied ist.
   * Diese Methode wird vom Client aufegrufen.
   *
   *
   * @param userid
   *               ein Anwender
   *
   * @return eine Liste aller Konfernzräume
   */
  @GET
  @Path("/read/reuniones/{userid}")
  @Produces(MediaType.APPLICATION_JSON)
  public List<TransferKonferenzraum> readReuniones(@PathParam(value = "userid") String userid) {
    return database.meineKonferenzen(userid);
  }



  /**
   * Alle Konferenzteilnehmer werden mit ihren Attributen ausgelesen.
   *
   *
   * @param konferenzname
   *                      die Konferenz
   * @param organisator
   *                      der Organisator
   *
   * @return der Protokollbefehl {@code CONFERENCE}
   */
  @GET
  @Path("/read/conference/{konferenzname}/{organisator}")
  @Produces(MediaType.APPLICATION_JSON)
  public CONFERENCE readConference(@PathParam(value = "konferenzname") String konferenzname,
      @PathParam(value = "organisator") String organisator) {

    CONFERENCE conference = database.readKonferenzuser(konferenzname, organisator);
    return conference;
  }

}
