/**
 *  Copyright © 2025, Luis Andrés Lange <https://javacomm.net>
 *
 *  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.
 *
 */
package net.javacomm.client.base;

import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.GenericType;
import jakarta.ws.rs.core.MediaType;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import net.javacomm.restserver.MessageBodyNickname;
import net.javacomm.restserver.MessageBodyOutdated;
import net.javacomm.restserver.MessageBodyTransferBenutzerkonto;
import net.javacomm.restserver.MessageBodyTransferConfig;
import net.javacomm.restserver.MessageBodyTransferRoomfilter;
import net.javacomm.restserver.MessageBodyTransferUser;
import net.javacomm.share.Constants;
import net.javacomm.transfer.TransferBenutzerkonto;
import net.javacomm.transfer.TransferConfig;
import net.javacomm.transfer.TransferRoomfilter;
import net.javacomm.transfer.TransferUser;
import net.javacomm.window.manager.Control;



/**
 * Vom Server werden alle Administrationsdaten abgerufen.
 */
public final class AdministratorWorker extends SwingWorker<Void, Void> {

  private final static Logger log = LogManager.getLogger(AdministratorWorker.class);
  public final static String SUCCESS = "success";

  private String userid;
  private String password;
  private CompletableFuture<ArrayList<TransferBenutzerkonto>> accounts;
  private CompletableFuture<TransferConfig> domain;
  private CompletableFuture<List<String>> nicknames;
  private CompletableFuture<List<String>> programmversionen;
  private CompletableFuture<ArrayList<TransferRoomfilter>> roomfilter;
  private CompletableFuture<ArrayList<TransferUser>> userRegistration;

  /**
   * Webservice initialisieren.
   * 
   * @param userid
   *                 diese userid gehört einem Admin
   * @param password
   *                 dieses Passwort
   */
  public AdministratorWorker(String userid, String password) {
    this.userid = userid;
    this.password = password;
  }



  @Override
  protected Void doInBackground() {
    accounts = CompletableFuture.supplyAsync(() -> {
      try {
        return readAccounts(userid, password);
      }
      catch (IOException e) {
        throw new RuntimeException(e);
      }
    });
    domain = CompletableFuture.supplyAsync(() -> {
      try {
        return readDomain(userid, password);
      }
      catch (IOException e) {
        throw new RuntimeException(e);
      }
    });
    nicknames = CompletableFuture.supplyAsync(this::readNicknamesWorker);
    programmversionen = CompletableFuture.supplyAsync(this::readProgrammversionen);

    roomfilter = CompletableFuture.supplyAsync(() -> {
      try {
        return readRoomfilter(userid, password);
      }
      catch (IOException e) {
        throw new RuntimeException(e);
      }
    });

    userRegistration = CompletableFuture.supplyAsync(() -> {
      try {
        return readUserRegistration(userid, password);
      }
      catch (IOException e) {
        throw new RuntimeException(e);
      }
    });

    // Rendezvous
    CompletableFuture.allOf(accounts, domain, nicknames, programmversionen, roomfilter, userRegistration)
        .join();

    return null;
  }



  @Override
  public void done() {
    firePropertyChange(SUCCESS, Control.NULL, Control.ACCOUNTS);
    firePropertyChange(SUCCESS, Control.NULL, Control.DOMAIN);
    firePropertyChange(SUCCESS, Control.NULL, Control.NICKNAMES);
    firePropertyChange(SUCCESS, Control.NULL, Control.PROGRAMMVERSIONEN);
    firePropertyChange(SUCCESS, Control.NULL, Control.ROOMFILTER);
    firePropertyChange(SUCCESS, Control.NULL, Control.USER_REGISTRATION);
  }



  /**
   * Alle Benutzerkonten abholen.
   * 
   * @return alle Benutzerkonten
   * 
   * @throws Exception
   *                   Lesefehler
   */
  public ArrayList<TransferBenutzerkonto> getAccounts() throws Exception {
    try {
      return accounts.get();
    }
    catch (InterruptedException | ExecutionException e) {
      throw e;
    }
  }



  /**
   * Die Konfigurationsdaten dür die Domäne werden gelesen.
   * 
   * @return diese Konfigurationsdaten
   * @throws Exception
   *                   Lesefehler
   */
  public TransferConfig getDomain() throws Exception {
    try {
      return domain.get();
    }
    catch (InterruptedException | ExecutionException e) {
      throw e;
    }
  }



  /**
   * Die verbotenen Nicknames werden geholt.
   * 
   * @return alle verbotenen Nicknames
   * @throws Exception
   *                   Lesefehler
   */
  public List<String> getNicknames() throws Exception {
    try {
      return nicknames.get();
    }
    catch (InterruptedException | ExecutionException e) {
      throw e;
    }
  }



  /**
   * Die veralteten Programmversionen werden abgeholt.
   * 
   * @return alle veralteten Programmversionen
   * @throws Exception
   *                   Lesefehler
   */
  public List<String> getProgrammversionen() throws Exception {
    try {
      return programmversionen.get();
    }
    catch (InterruptedException | ExecutionException e) {
      throw e;
    }
  }



  /**
   * Die zurückgegebenen Filter enthalten verbotene Raumnamen.
   * 
   * @return alle verbotenen Raumnamen
   * @throws Exception
   *                   Lesefehler
   */
  public ArrayList<TransferRoomfilter> getRoomfilter() throws Exception {
    try {
      return roomfilter.get();
    }
    catch (InterruptedException | ExecutionException e) {
      throw e;
    }
  }



  /**
   * Die Benutzeranträge für JavaComm werden zurückgegeben.
   * 
   * @return alle offenen Benutzeranträge
   * @throws Exception
   *                   Lesefehler
   */
  public ArrayList<TransferUser> getUserRegistration() throws Exception {
    try {
      return userRegistration.get();
    }
    catch (InterruptedException | ExecutionException e) {
      throw e;
    }
  }



  /**
   * Alle Benutzerkonten werden gelesen.
   * 
   * @param userid
   *                 dieser Admin
   * @param password
   *                 dieses Passwort
   * @return alle Benutzerkonten
   * 
   * @throws IOException
   *                     Konten konnten nicht gelesen werden
   */
  private ArrayList<TransferBenutzerkonto> readAccounts(String userid, String password) throws IOException {
    ArrayList<TransferBenutzerkonto> result = null;
    try(Client client = ClientBuilder.newClient()) {
      client.register(MessageBodyTransferBenutzerkonto.class);
      client.register(MultiPartFeature.class);
      StringBuilder url = new StringBuilder(Constants.PROTOCOL).append(Constants.DOMAIN)
          .append("/javacommserver/administrator/read/accounts");
      WebTarget webtarget = client.target(url.toString());
      try(FormDataMultiPart multipart = new FormDataMultiPart()) {
        multipart.field("admin", userid);
        multipart.field("password", password);
        GenericType<ArrayList<TransferBenutzerkonto>> configType = new GenericType<>() {};
        result = webtarget.request(MediaType.APPLICATION_JSON)
            .post(Entity.entity(multipart, multipart.getMediaType()), configType);
        return result;
      }
    }
  }



  /**
   * Die Konfigurationsdaten dür die Domäne werden gelesen.
   * 
   * @return diese Konfigurationsadten
   * 
   * @throws IOException
   *                     Konfigurationsdaten konnten nicht gelesen werden
   */
  private TransferConfig readDomain(String userid, String password) throws IOException {
    try(
      Client client = ClientBuilder.newClient();
      FormDataMultiPart multipart = new FormDataMultiPart()
    ) {
      client.register(MultiPartFeature.class);
      client.register(MessageBodyTransferConfig.class);
      WebTarget webtarget = client
          .target(Constants.PROTOCOL + Constants.DOMAIN + "/javacommserver/administrator/read/config");
      multipart.field("userid", userid);
      multipart.field("password", password);
      return webtarget.request(MediaType.APPLICATION_JSON)
          .post(Entity.entity(multipart, multipart.getMediaType()), TransferConfig.class);

    }
  }



  /**
   * Lies alle Nicknames aus, die auf der Verbotsliste stehen.
   * 
   * @return diese Nicknames
   */
  private List<String> readNicknamesWorker() {
    ArrayList<String> result = new ArrayList<>();
    try(Client client = ClientBuilder.newClient()) {
      StringBuilder url = new StringBuilder(Constants.PROTOCOL).append(Constants.DOMAIN)
          .append("/javacommserver/administrator/read/nicknames");

      client.register(MessageBodyNickname.class);
      WebTarget webtarget = client.target(url.toString());
      GenericType<ArrayList<String>> configType = new GenericType<>() {};
      result = webtarget.request(MediaType.APPLICATION_JSON_TYPE).get(configType);
      return result;
    }
  }



  private List<String> readProgrammversionen() {
    try(Client client = ClientBuilder.newClient()) {
      ArrayList<String> result = new ArrayList<>();
      client.register(MessageBodyOutdated.class);
      StringBuilder url = new StringBuilder(Constants.PROTOCOL).append(Constants.DOMAIN)
          .append("/javacommserver/administrator/read/outdated");
      WebTarget webtarget = client.target(url.toString());
      GenericType<ArrayList<String>> configType = new GenericType<>() {};
      result = webtarget.request(MediaType.APPLICATION_JSON_TYPE).get(configType);
      return result;
    }
  }



  /**
   * Reads all blocked room filters.
   *
   * @return the blocked room filters
   *
   * @throws IOException
   *                     if the room filters could not be read
   */
  private ArrayList<TransferRoomfilter> readRoomfilter(String userid, String password) throws IOException {

    GenericType<ArrayList<TransferRoomfilter>> configType = new GenericType<>() {};
    ArrayList<TransferRoomfilter> result = null;
    try(
      Client client = ClientBuilder.newClient();
      FormDataMultiPart multipart = new FormDataMultiPart()
    ) {
      client.register(MessageBodyTransferRoomfilter.class);
      client.register(MultiPartFeature.class);
      multipart.field("userid", userid);
      multipart.field("password", password);

      WebTarget webtarget = client
          .target(Constants.PROTOCOL + Constants.DOMAIN + "/javacommserver/administrator/read/roomfilter");

      result = webtarget.request(MediaType.APPLICATION_JSON)
          .post(Entity.entity(multipart, multipart.getMediaType()), configType);
      return result;
    }
  }



  /**
   * Lies alle Benutzeranträge aus.
   * 
   * @param userid
   *                 dieser User ist ein Admin
   * @param password
   *                 dieses Passwort
   * 
   * @return alle Benutzeranträge
   * 
   * @throws IOException
   *                     die Benutzeranträge konnten nicht gelesen werden
   */
  private ArrayList<TransferUser> readUserRegistration(String userid, String password) throws IOException {

    StringBuilder url = new StringBuilder(Constants.PROTOCOL).append(Constants.DOMAIN)
        .append("/javacommserver/administrator/user");
    try(Client client = ClientBuilder.newClient()) {
      client.register(MultiPartFeature.class);
      client.register(MessageBodyTransferUser.class);
      WebTarget webtarget = client.target(url.toString());
      GenericType<ArrayList<TransferUser>> configType = new GenericType<>() {};

      try(FormDataMultiPart multipart = new FormDataMultiPart()) {
        multipart.field("userid", userid);
        multipart.field("password", password);
        return webtarget.request(MediaType.APPLICATION_JSON)
            .post(Entity.entity(multipart, multipart.getMediaType()), configType);

      }
    }
  }

}
