package net.javacomm.restserver;

import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.javacomm.database.WebdatabaseImpl;
import net.javacomm.protocol.CBlock;



public class SecurityService {
  final Logger log = LogManager.getLogger(SecurityService.class);
  private WebdatabaseImpl database;

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



  /**
   * Werfe eine <b>WebApplicationException</b>, wenn der Anwneder kein
   * Administrator ist.
   * 
   * 
   * @param pseudoid
   *                 die PSEUDOID vom Anwender
   * @param cblock
   *                 PSEUDOPASSWORD und TIMESTAMP
   * @return
   */
  public void verifyAdminOrThrow(String pseudoid, String cblock) {
    CBlock block = CBlock.toCBlock(cblock);

    if (block == null) {
      String errormessage = "cblock is null";
      log.error(errormessage);
      throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(errormessage).build());
    }
    if (pseudoid == null) {
      String errormessage = "pseudoid is null";
      log.error(errormessage);
      throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(errormessage).build());
    }

    if (!isUserAuthenticated(pseudoid, block.getPseudoPassword())) {
      String errormessage = "Anwender nicht bekannt: " + pseudoid;
      throw new WebApplicationException(Response.status(Status.NOT_FOUND).entity(errormessage).build());
    }

    if (!isTimestampValid(block.getTimestamp())) {
      String errormessage = "Möglicher Replay-Angriff bei ID: " + pseudoid;
      log.error(errormessage);
      throw new WebApplicationException(Response.status(Status.NOT_FOUND).entity(errormessage).build());
    }

    String userid = database.fetchUserByPseudoid(pseudoid).get();
    if (!database.isAdministrator(userid)) {
      String errormessage = "kein Admin: " + pseudoid;
      log.warn(errormessage);
      throw new WebApplicationException(Response.status(Status.FORBIDDEN).entity(errormessage).build());
    }
  }



  /**
   * Ist der Anwender eingeloggt?
   * 
   * @param pseudoid
   *                       diese Pseudoid
   * @param pseudoPassword
   *                       dieses Pseudopasswort
   * @return {@code true} eingeloggt, sonst {@code false}
   */
  public boolean isUserAuthenticated(String pseudoid, String pseudoPassword) {
    String uid = database.fetchUserByPseudoid(pseudoid).get();
    if (uid == null) return false;
    return database.checkPassword(uid, pseudoid, pseudoPassword);
  }



  /**
   * Wenn der Timestamp geringer oder gleich 3 Sekunden lang ist, liegt er
   * innerhalb der Toleranz.
   * 
   * @param pseudoid
   *                       Pseudoid vom Client
   * @param pseudoPassword
   *                       Pseudopasswort vom Client
   * @param timestamp
   *                       Timestamp vom Client
   * 
   * @return {@code true}, die Toleranz wurde eingehalten
   */
  boolean isTimestampValid(long timestamp) {

    // 1. Hol dir die aktuelle, echte Serverzeit
    long serverTs = System.currentTimeMillis();

    // 2. Hol dir den Zeitstempel aus dem CBlock (der vom Client "geicht" wurde)
    long clientTs = timestamp;

    // 3. Die Differenz-Berechnung (Der absolute Betrag)
    long difference = Math.abs(serverTs - clientTs);

    // 4. Der Schwellenwert-Vergleich
    return difference <= 3000;

  }

}
