<?php
/**
 * @copyright (c) Kufer Software GmbH, Loisachstrasse 4, D-84453 Mühldorf
 */


class StornoAPI
{
  public const HINDERUNG_STATUS_NICHT_STORNIERBAR = 0;
  public const HINDERUNG_KURS_BEREITS_BEGONNEN = 1;
  public const HINDERUNG_HAT_ZAHLUNGEN = 2;
  public const HINDERUNG_TEIL_EINER_MEHRFACHANMLEDUNG = 3;
  public const HINDERUNG_TEIL_EINER_ELTERN_KIND_ANMELDUNG = 4;
  public const HINDERUNG_ANMELDESCHLUSS_ERREICHT = 5;
  public const HINDERUNG_MIN_TN_ANZAHL_WIRD_UNTERSCHRITTEN = 6;

  public const SEND_ERROR = 0;
  public const SEND_SUCCESSFUL_PER_EMAIL = 1;
  public const SEND_SUCCESSFUL_PER_DIREKT = 2;
  public const SEND_SUCCESSFUL_PER_DMZ = 3;

  
  public static function getHinderungsGruende($pBelegung, $pToolSettings = null)
  {
    if (\is_null($pToolSettings) || $pToolSettings === '') {
      $pToolSettings = new \stdClass(); 
    }

    if (SessionOperations::isDmzBetrieb()) {
      return self::getHinderungsGruendePerDMZ($pBelegung, $pToolSettings);
    } else {
      return self::getHinderungsGruendePerDatenbank($pBelegung, $pToolSettings);
    }
  }

  
  private static function getHinderungsGruendePerDatenbank($pBelegung, $pToolSettings)
  {
    $gruende = array();

    
    $minTNanzahlUnterschrittenPruefen = (isset($pToolSettings->stornoMinTNanzahlPruefen)) ? $pToolSettings->stornoMinTNanzahlPruefen : true;

    if (!StornoAPI::isStatusStornierbar($pBelegung->status)) {
      $gruende[] = self::HINDERUNG_STATUS_NICHT_STORNIERBAR;
    }
    if (StornoAPI::hasZahlungen($pBelegung)) {
      $gruende[] = self::HINDERUNG_HAT_ZAHLUNGEN;
    }
    if (!SessionOperations::isCurrentUserMitveranstalter() && StornoAPI::isTeilEinerMehrfachAnmeldung($pBelegung)) {
      $gruende[] = self::HINDERUNG_TEIL_EINER_MEHRFACHANMLEDUNG;
    }
    if (StornoAPI::isTeilEinerElternKindAnmeldung($pBelegung)) {
      $gruende[] = self::HINDERUNG_TEIL_EINER_ELTERN_KIND_ANMELDUNG;
    }
    if ($minTNanzahlUnterschrittenPruefen) {
      if (StornoAPI::wirdMinTnAnzahlUnterschritten($pBelegung->status, $pBelegung->kurs->getVarValue('teil_min'), $pBelegung->kurs->getVarValue('anmeldungen'))) {
        $gruende[] = self::HINDERUNG_MIN_TN_ANZAHL_WIRD_UNTERSCHRITTEN;
      }
    }

    return $gruende;
  }

  
  private static function getHinderungsGruendePerDMZ($pBelegung, $pToolSettings)
  {
    $gruende = array();

    
    $minTNanzahlUnterschrittenPruefen = $pToolSettings->stornoMinTNanzahlPruefen ?? true;

    try {
      $infosPerDmz = self::getInfosZurBelegungPerDmz($pBelegung->id);
    } catch (Exception $e) {
      Logger::logg('StornoAPI: Hinderungsgründe konnten nicht per DMZ ermittelt werden. Diese werden nun auf der MySQL-DB geprüft. Fehlermeldung: ' . $e->getMessage(), 110);

      return self::getHinderungsGruendePerDatenbank($pBelegung, $pToolSettings);
    }

    
    if (!StornoAPI::isStatusStornierbar($infosPerDmz['Status1'])) {
      $gruende[] = self::HINDERUNG_STATUS_NICHT_STORNIERBAR;
    }

    if (!SessionOperations::isCurrentUserMitveranstalter() && ($infosPerDmz['IsTeilVonFirmenanmeldung'])) {
      $gruende[] = self::HINDERUNG_TEIL_EINER_MEHRFACHANMLEDUNG;
    }

    if ($infosPerDmz['IsTeilVonMehrfachanmeldung']) {
      $gruende[] = self::HINDERUNG_TEIL_EINER_MEHRFACHANMLEDUNG;
    }

    if ($infosPerDmz['IsElternKindanmeldung']) {
      $gruende[] = self::HINDERUNG_TEIL_EINER_ELTERN_KIND_ANMELDUNG;
    }

    if ($infosPerDmz['SummeIst'] > 0 || $infosPerDmz['SummeSollZahlungenMitZahlnummerGroesserNull'] > 0) {
      $gruende[] = self::HINDERUNG_HAT_ZAHLUNGEN;
    }

    if ($minTNanzahlUnterschrittenPruefen) {
      if (StornoAPI::wirdMinTnAnzahlUnterschritten($infosPerDmz['Status1'], $infosPerDmz['TnMin'], $infosPerDmz['AnzahlAnmeldungen'])) {
        $gruende[] = self::HINDERUNG_MIN_TN_ANZAHL_WIRD_UNTERSCHRITTEN;
      }
    }

    return $gruende;
  }

  private static function getInfosZurBelegungPerDmz($belegId)
  {
    $abgleichApi = new AbgleichAPI();
    $infoNode = new XMLNode('Belegungstatus');
    $infoNode->addChild(new XMLNode('belid', $belegId));
    $nutzlast = new Nutzlast($infoNode);

    

    
    $result = $abgleichApi->sendeNachricht(
      AbgleichAPI::ABGLEICH_GETBELEGUNGSTATUS,
      $nutzlast
    );

    return json_decode($result->reason, true);
  }

  
  public static function getText4HinderungsKonstanten($pHinterungsgrund, $pBelegung, $pSprachsettings, $pZusatzText = '')
  {
    $text = '';
    switch ($pHinterungsgrund) {
      case self::HINDERUNG_STATUS_NICHT_STORNIERBAR:

        $text = $pSprachsettings->Teilnehmer['Singular_mit_Artikel']['Nominativ']
                . ' ist nicht mit einem stornierbaren Status (A, I, P, U und W) in ' . $pSprachsettings->Kurs['Singular_mit_Artikel']['Dativ'];
        break;

      case self::HINDERUNG_KURS_BEREITS_BEGONNEN:

        $text = $pSprachsettings->Kurs['Singular_mit_Artikel']['Nominativ'] . ' hat bereits am ' . $pBelegung->kurs->beginn_dat . ' begonnen';
        break;

      case self::HINDERUNG_HAT_ZAHLUNGEN:

        $text = 'Aufgrund bereits erfolgter Zahlungen/Rechnungen ist eine automatische Stornierung nicht möglich.';
        break;

      case self::HINDERUNG_TEIL_EINER_MEHRFACHANMLEDUNG:

        $text = $pSprachsettings->Teilnehmer['Singular_mit_Artikel']['Nominativ'] . ' gehört zu einer Mehrfach- oder Firmenanmeldung';
        break;

      case self::HINDERUNG_TEIL_EINER_ELTERN_KIND_ANMELDUNG:

        $text = 'Zu dieser Anmeldung gehören Kinderanmeldungen';
        break;

      case self::HINDERUNG_ANMELDESCHLUSS_ERREICHT:

        $text = 'Der Anmeldeschluss ist bereits erreicht (' . $pBelegung->kurs->anm_ende . ')';
        break;

      case self::HINDERUNG_MIN_TN_ANZAHL_WIRD_UNTERSCHRITTEN:

        $text = 'Durch eine Abmeldung würde die Mindestteilnehmerzahl unterschritten (minimal ' . $pBelegung->kurs->getVarValue('teil_min')
                . ' - derzeit ' . $pBelegung->kurs->getVarValue('anmeldungen') . ')';
        break;

    }

    return $text;
  }

  
  public static function handleStorno($pEmail, $pBelegung, $pSprachsettings, $pStatus, $pStornogrund = '', $pToolSettings = '', $pAustritt = null)
  {
    $gruende = StornoAPI::getHinderungsGruende($pBelegung, $pToolSettings);
    if (count($gruende) !== 0 || (SessionOperations::isReplikat() && !SessionOperations::isDmzBetrieb())) {
      $success = StornoAPI::sendStornoMail($pEmail, $pBelegung, $pSprachsettings, $gruende, $pStatus, $pStornogrund, $pAustritt);
      if ($success) {
        return StornoAPI::SEND_SUCCESSFUL_PER_EMAIL;
      }
    } elseif (SessionOperations::isDmzBetrieb()) {
      $success = StornoAPI::stornoDMZBetrieb($pBelegung, $pStatus, $pStornogrund, $pAustritt);
      if ($success) {
        return StornoAPI::SEND_SUCCESSFUL_PER_DMZ;
      }
    } else {
      $success = StornoAPI::stornoDirektBetrieb($pBelegung, $pStatus, $pStornogrund, $pAustritt);
      if ($success) {
        return StornoAPI::SEND_SUCCESSFUL_PER_DIREKT;
      }
    }

    return StornoAPI::SEND_ERROR;
  }

  
  public static function handleStornoKursMitAboGebuehr($pBelegung, $pStornoGrund, $pAustritt)
  {
    if (SessionOperations::isDmzBetrieb()) {
      $success = StornoAPI::stornoKursMitAboGebuehrDMZBetrieb($pBelegung, $pStornoGrund, $pAustritt);
      if ($success) {
        return StornoAPI::SEND_SUCCESSFUL_PER_DMZ;
      }
    } elseif (!SessionOperations::isReplikat()) {
      $success = StornoAPI::stornoKursMitAboGebuehrDirektBetrieb($pBelegung, $pStornoGrund, $pAustritt);
      if ($success) {
        return StornoAPI::SEND_SUCCESSFUL_PER_DIREKT;
      }
    }

    return StornoAPI::SEND_ERROR;
  }

  public static function stornoDmz($pBelegung)
  {
    $success = StornoAPI::stornoDMZBetrieb($pBelegung);
    if ($success) {
      return StornoAPI::SEND_SUCCESSFUL_PER_DMZ;
    } else {
      return StornoAPI::SEND_ERROR;
    }
  }

  
  private static function sendStornoMail($pEmail, $pBelegung, $pSprachsettings, $pHinderungsGruende, $pStatus, $pStornogrund = '', $pAustritt = null)
  {
    $mitarbeiter = SessionOperations::getCurrentWebToolsUser();

    $betreff = StornoAPI::getEMailBetreff($pBelegung, $pSprachsettings);
    $body = StornoAPI::getEMailBody($pBelegung, $pSprachsettings, $pHinderungsGruende, $pStatus, $pStornogrund, $mitarbeiter, $pAustritt);

    $user = $mitarbeiter;
    $email = new \Kufer\Mail\EMail\EMailPlaintext($betreff, $body);
    $email->addKuferToolsFooter(
      'Stornierung eines Teilnehmers vom Kurs',
      $user,
      $pBelegung->kurs
    );

    $sender = \Kufer\Mail\Sender\EMailSenderFactory::createForToolsWithCustomAddress($pEmail);

    return $sender->sendFromToolsUserToEinrichtung($email, $user)->hasSentAnyMails();
  }

  
  private static function stornoDMZBetrieb($pBelegung, $pStatus = 'S', $pStornogrund = '', $pAustritt = null)
  {
    try {
      $dbwBelegung = new DBWorkBelegung(1, true);

      $anzahlFreiwerdenderPlaetze = in_array($pBelegung->status, ConstantOperations::getStatusangemeldetArraySimple()) ? 1 : 0;

      
      
      
      $pWartelistenHandlingDmz = false;
      BelegungAPI::storniereBelegungDMZ($pBelegung->id, $pStatus, $pStornogrund, $anzahlFreiwerdenderPlaetze, false, array(), $pAustritt, false, $pWartelistenHandlingDmz);

      
      
      

      
      $dbwBelegung->doStorno($pBelegung->id, $pStatus, $pStornogrund);

      return true;
    } catch (Exception $e) {
      \Logger::logg('StornoAPI::_stornoDMZBetrieb: ' . $e->getMessage(), 520);

      return false;
    }
  }

  
  private static function stornoKursMitAboGebuehrDMZBetrieb($pBelegung, $pStornoGrund, $pAustritt)
  {
    try {
      $dbwBelegung = new DBWorkBelegung(1, true);

      BelegungAPI::storniereBelegungMitAboGebuehrDMZ($pBelegung->id, $pStornoGrund, $pAustritt);

      $dbwBelegung->doStornoKursMitAboGebuehr($pBelegung->id, $pStornoGrund, $pAustritt);

      return true;
    } catch (Exception $e) {
      return false;
    }
  }

  
  private static function stornoDirektBetrieb($pBelegung, $pStatus, $pStornogrund = '', $pAustritt = null)
  {
    $dbwBelegung = new DBWorkBelegung(1, true);
    $user = SessionOperations::getCurrentWebToolsUser();

    $kennung = '';
    if ($user->ben > 0) {
      $mitarbeiter = SessionOperations::getCurrentWebToolsMitarbeiter();
      $kennung = $mitarbeiter->kennung;
    }

    $dbwBelegung->doStorno($pBelegung->id, $pStatus, $pStornogrund, $kennung, $pAustritt);

    return true;
  }

  
  private static function stornoKursMitAboGebuehrDirektBetrieb($pBelegung, $pStornoGrund, $pAustritt)
  {
    $dbwBelegung = new DBWorkBelegung(1, true);

    $dbwBelegung->doStornoKursMitAboGebuehr($pBelegung->id, $pStornoGrund, $pAustritt);

    return true;
  }

  
  private static function getEMailBetreff($pBelegung, $pSprachsettings)
  {
    return 'Abmeldung von ' . $pBelegung->teilnehmer->namevorname . ' von '
                  . $pSprachsettings->Kurs['Singular_mit_Artikel']['Dativ'] . ' ' . $pBelegung->knr . ' (' . $pBelegung->kurs->getVarValue('kurzbez_titel') . ')';
  }

  
  private static function getEMailBody($pBelegung, $pSprachsettings, $pHinderungsGruende, $pStatus, $pStornogrund, $pMitarbeiter, $pAustritt = null)
  {
    

    
    $body = 'Mitarbeiter ' . $pMitarbeiter->name . ' ' . $pMitarbeiter->vorname . ' möchte ' . $pSprachsettings->Teilnehmer['Singular_mit_Artikel']['Akkusativ']
            . ' ' . $pBelegung->teilnehmer->getVarValue('name_vorname_ohne_komma') . ' von ' . $pSprachsettings->Kurs['Singular_mit_Artikel']['Dativ'] . ' "'
            . $pBelegung->kurs->getVarValue('kurzbez_titel') . '" (Kurs-Nr. ' . $pBelegung->knr . ') abmelden.' . "\n\n";

    $austrittDatum = '';
    if (isset($pAustritt)) {
      $austrittDatum = $pAustritt !== '' ? $pAustritt : DateTimeOperations::getDateNow();
    }

    if ($pStornogrund != '') {
      $body .= 'Dabei wurde vom Mitarbeiter folgender Grund angegeben: ' . "\n" . $pStornogrund . "\n\n";
    }
    $body .= 'neuer Status: ' . $pStatus . "\n";
    $body .= $austrittDatum !== '' ? 'Austritt: ' . $austrittDatum . "\n\n" : "\n";
    $gruendeCount = count($pHinderungsGruende);
    if ($gruendeCount > 0) {
      $body .= ' Eine automatische Abmeldung konnte ';
      $body .= 'aus folgende' . ($gruendeCount == 1 ? 'm Grund' : 'n Gründen');
      $body .= ' nicht durchgeführt werden:' . "\n\n";
      foreach ($pHinderungsGruende as $grund) {
        $body .= '- ' . StornoAPI::getText4HinderungsKonstanten($grund, $pBelegung, $pSprachsettings) . "\n";
      }
    }
    $body .= "\n\n" . 'Diese E-Mail wurde am ' . DateTimeOperations::getDateNow() . ' um ' . DateTimeOperations::getTimeNow() . ' aus den ' . KUFERTOOLS . ' versandt.';

    return $body;
  }

  
  private static function isStatusStornierbar($pStatus)
  {
    if (DBWorkBASysSettings::isInnsbruckStadt()) {
      return in_array($pStatus, array('A', ' ', '', 'I', 'P', 'U', 'W', 'G', 'X'));
    }

    return in_array($pStatus, array('A', ' ', '', 'I', 'P', 'U', 'W'));
  }

  
  private static function hasZahlungen($pBelegung)
  {
    $dbwZahlung = DBWorkZahlung::getDefaultInstance();

    return !$dbwZahlung->isStornoBzglZahlungOkForBelegung($pBelegung->id);
  }

  
  private static function isTeilEinerMehrfachAnmeldung($pBelegung)
  {
    $dbwBelegung = DBWorkBelegung::getDefaultInstance(1, true, true);

    return $dbwBelegung->isTeilEinerMehrfachAnmeldung($pBelegung->id);
  }

  
  private static function isTeilEinerElternKindAnmeldung($pBelegung)
  {
    $dbwBelegung = DBWorkBelegung::getDefaultInstance(1, true);

    return $dbwBelegung->IsElternKindanmeldung($pBelegung);
  }

  
  private static function wirdMinTnAnzahlUnterschritten($pStatus, $pTeilMin, $pAnzahlAnmeldungen)
  {
    if (in_array($pStatus, ConstantOperations::getStatusangemeldetArraySimple())) {
      return $pTeilMin == $pAnzahlAnmeldungen;
    }

    return false;
  }

  
  public static function handleStornoDmzOrDirektWithWarteliste(\Belegung $belegung, string $status, $stornogrund = '', $austritt = null, $fnWarteliste = null)
  {
    assert(!in_array($status, \ConstantOperations::getStatusangemeldetArraySimple()));
    assert(\SessionOperations::isDmzBetrieb() || !\SessionOperations::isReplikat(), 'Kann nur von DMZ oder Direktbetrieb aufgerufen werden!');

    if (\SessionOperations::isDmzBetrieb()) {
      $success = \StornoAPI::stornoDMZBetriebWithWarteliste($belegung, $status, $stornogrund, $austritt, $fnWarteliste);
      if ($success) {
        return \StornoAPI::SEND_SUCCESSFUL_PER_DMZ;
      }
    } elseif (!\SessionOperations::isReplikat()) {
      $success = \StornoAPI::stornoDirektBetriebWithWarteliste($belegung, $stornogrund, $stornogrund, $austritt, $fnWarteliste);
      if ($success) {
        return \StornoAPI::SEND_SUCCESSFUL_PER_DIREKT;
      }
    }

    return StornoAPI::SEND_ERROR;
  }

  private static function sollWartelisteBenachrichtigtWerden(\Belegung $pBelegung): bool
  {
    $daysBefore = 0;
    if (\DBWorkBASysSettings::isBlak() && $pBelegung->kurs->getVarValue('is_webinar')) {
      $daysBefore = -2; 
    }
    $kursIstNichtUeberbucht = $pBelegung->kurs->getVarValue('teil_frei') >= 0;
    $kursIstNichtBeendet = !\DateTimeOperations::isDatebeforeNow($pBelegung->kurs->ende_dat, $daysBefore);
    $kursIstNichtAusgefallen = !$pBelegung->kurs->ausfall;
    $esWirdEinPlatzFrei = $pBelegung->status !== 'W';

    return $kursIstNichtUeberbucht && $kursIstNichtBeendet && $esWirdEinPlatzFrei && $kursIstNichtAusgefallen;
  }

  
  private static function stornoDMZBetriebWithWarteliste(\Belegung $pBelegung, string $pStatus, $pStornogrund = '', $pAustritt = null, $fnWarteliste = null)
  {
    try {
      $dbwBelegung = \DBWorkBelegung::getDefaultInstance(1, true);

      $anzahlFreiwerdenderPlaetze = in_array($pBelegung->status, \ConstantOperations::getStatusangemeldetArraySimple()) ? 1 : 0;

      $dmzResult = \BelegungAPI::storniereBelegungDMZ($pBelegung->id, $pStatus, $pStornogrund, $anzahlFreiwerdenderPlaetze, false, array(), $pAustritt, false, true);
      
      $warteListenSuccessAry = \JsonOperations::getFromString($dmzResult->reason);

      
      $dbwBelegung->doStorno($pBelegung->id, $pStatus, $pStornogrund, '', $pAustritt);

      
      $wlEintrag = null;
      if (isset($warteListenSuccessAry[0])) {
        $wlEintrag = $warteListenSuccessAry[0];
      }

      if (isset($wlEintrag) && is_object($wlEintrag) && self::sollWartelisteBenachrichtigtWerden($pBelegung)) {
        $wartelistenBelId = $wlEintrag->belid;
        $wlBelegung = $dbwBelegung->getBelegung($wartelistenBelId);
        $teilnehm = $wlBelegung->teilnehmer;
        if ($wlEintrag->firma > 0) {
          $teilnehm = \DBWorkTeilnehmer::getDefaultInstance()->getTeilnehm($wlEintrag->firma);
        }

        $wlEintragstatus_1 = $wlEintrag->status;
        if (!empty($wlBelegung->id) && $wlBelegung->tnr == $wlEintrag->tnr && $wlBelegung->status == $wlEintragstatus_1 && $wlBelegung->knr == $pBelegung->knr) {
          
          $kennung = \ToolSettingsAPI::getToolSettings(0)->direktMaKuerzel;
          $dbwBelegung->changeWarteListenStatus($wlBelegung, $kennung); 
        } else {
          
          \Logger::logg('Eintragung auf Warteliste im Web konnte nicht durchgeführt werden, da DMZ und Replikat nicht übereinstimmen', 200);

          
          $wlBelegung->status = $wlEintragstatus_1;
          $wlBelegung->tnr = $wlEintrag->tnr;
          $wlBelegung->firma = $wlEintrag->firma;
        }

        if (empty($teilnehm->id)) { 
          
          $teilnehm->id = $wlEintrag->firma > 0 ? $wlEintrag->firma : $wlEintrag->tnr;
          $teilnehm->ansprache = $wlEintrag->ansprache;
          $teilnehm->anrede = $wlEintrag->anrede;
          $teilnehm->titel = $wlEintrag->titel;
          $teilnehm->geschlecht = $wlEintrag->geschlecht;
          $teilnehm->name = $wlEintrag->name;
          $teilnehm->vorname = $wlEintrag->vorname;
          $teilnehm->email = $wlEintrag->email;
        }

        
        if (!is_null($fnWarteliste)) {
          call_user_func($fnWarteliste,  $wlBelegung,  $teilnehm);
        }
      }

      return true;
    } catch (\Exception $e) {
      \Logger::logg('StornoAPI::_stornoDMZBetrieb: ' . $e->getMessage(), 520);

      return false;
    }
  }

  
  private static function stornoDirektBetriebWithWarteliste(\Belegung $pBelegung, string $pStatus, $pStornogrund = '', $pAustritt = null, $fnWarteliste = null)
  {
    $dbwBelegung = \DBWorkBelegung::getDefaultInstance(1, true);
    $user = \SessionOperations::getCurrentWebToolsUser();

    $kennung = '';
    if ($user->ben > 0) {
      $mitarbeiter = \SessionOperations::getCurrentWebToolsMitarbeiter();
      $kennung = $mitarbeiter->kennung;
    } else {
      $kennung = \ToolSettingsAPI::getToolSettings(0)->direktMaKuerzel;
    }

    $dbwBelegung->doStorno($pBelegung->id, $pStatus, $pStornogrund, $kennung, $pAustritt);

    $anzahlFreiwerdenderPlaetze = in_array($pBelegung->status, \ConstantOperations::getStatusangemeldetArraySimple()) ? 1 : 0;

    
    if ($anzahlFreiwerdenderPlaetze > 0 && self::sollWartelisteBenachrichtigtWerden($pBelegung)) {
      $wlBelegung = $dbwBelegung->getFirstWartelistenBelegung($pBelegung->knr);
      if (!empty($wlBelegung->id)) { 
        $teilnehm = $wlBelegung->teilnehmer;
        if ($wlBelegung->firma > 0) {
          
          $teilnehm = \DBWorkTeilnehmer::getDefaultInstance()->getTeilnehm($wlBelegung->firma);
        }

        $dbwBelegung->changeWarteListenStatus($wlBelegung, $kennung); 

        
        if (!is_null($fnWarteliste)) {
          call_user_func($fnWarteliste,  $wlBelegung,  $teilnehm);
        }
      }
    }

    return true;
  }
}