<?php

class DXEvidenceSync
{
    private $backendCommunication;
    private $dataAccess;

    function  __construct()
    {
        $this->backendCommunication = new DXBackendCommunication();
        $this->dataAccess = new DXDataAccess();
    }

    public function processSync()
    {
        $result = true;

        $isServerAlive = $this->backendCommunication->ping();
        if (!$isServerAlive) {
            $endpoint = $this->backendCommunication->getRestApiOptions()->endpoint;
            error_log("The Dance-X-Backend under '$endpoint' did not answer (on ping).");
            error_log("No DataSync was processed");
            return false;
        }

        $isAuthCallPossible = $this->backendCommunication->isAuthenticatedCallPossible();
        if (!$isAuthCallPossible) {
            $endpoint = $this->backendCommunication->getRestApiOptions()->endpoint;
            error_log("The evidence-backend under '$endpoint' seems to be present but it was not possible to make an authenticated REST-Call. Is the ApiKey correctly configured?");
            error_log("No DataSync was processed");
            return false;
        }

        try {

            $da = new DXDataAccess();

            //cleanup
            $da->cleanupDatabaseFromDanceXData();

            //import data
            $events = $this->loadEvents();
            $this->insertData($events, DXType::GetType(DXTypeEnum::Event), "titel");

            $kursgruppen = $this->loadKursgruppen();
            $this->insertData($kursgruppen, DXType::GetType(DXTypeEnum::Kursgruppe), null);

            $kurstypen = $this->loadKurstypen();
            $this->insertData($kurstypen, DXType::GetType(DXTypeEnum::Kurstyp), null);

            $kursstufen = $this->loadKursstufen();
            $this->insertData($kursstufen, DXType::GetType(DXTypeEnum::Kursstufe), null);

            $kursleiter = $this->loadKursleiter();
            $this->insertData($kursleiter, DXType::GetType(DXTypeEnum::Kursleiter), "vorname");

            $kurse = $this->loadKurse();
            $this->insertData($kurse, DXType::GetType(DXTypeEnum::Kurs), "name");

            $kursperioden = $this->deriveKursperioden($kurse);
            $this->insertData($kursperioden, DXType::GetType(DXTypeEnum::Kursperiode), null);

            //process relations
            $this->processKurseRelations($kurse, $kursleiter, $kursstufen, $kurstypen, $kursgruppen, $kursperioden);
        } catch (\Throwable $th) {
            $result = false;
            error_log("Exception occured while processing 'processSync':" . $th->getMessage());
        }

        return $result;
    }

    private function processKurseRelations($kurse, $kursleiter, $kursstufen, $kurstypen, $kursgruppen, $kursperioden)
    {
        $da =  new DXDataAccess();
        $relationsArr = $da->loadJetEngineDanceXRelationIds();

        $relId_kursleiter = $relationsArr[DXTypeEnum::Kursleiter];
        $relId_kursgruppe  = $relationsArr[DXTypeEnum::Kursgruppe];
        $relId_kursstufe  = $relationsArr[DXTypeEnum::Kursstufe];
        $relId_kurstyp  = $relationsArr[DXTypeEnum::Kurstyp];
        $relId_kursperiode  = $relationsArr[DXTypeEnum::Kursperiode];

        global $wpdb;
        $relationTable = DXCctTableEnum::wpdb_prefix(DXCctTableEnum::JetRelDefault);

        foreach ($kurse as $kurs) {

            $kursId = $kurs['insert_id'];

            //array_values = reapply 0-indexed array
            //Kursgruppen
            $matchedkursGruppen = array_values(array_filter($kursgruppen, function ($kursgruppe) use ($kurs) {
                if ($kursgruppe['objectid'] == $kurs['fkkursgruppe']) {
                    return true;
                }
            }));

            if (count($matchedkursGruppen) >= 1) {
                $kursgruppeId = $matchedkursGruppen[0]['insert_id'];

                $wpdb->insert($relationTable, array(
                    "rel_id" => $relId_kursgruppe,
                    "parent_rel" => 0,
                    "parent_object_id" => $kursgruppeId,
                    "child_object_id" => $kursId
                ));
            }

            //-----------

            //Kursstufen
            $matchedKursstufen = array_values(array_filter($kursstufen, function ($kursstufe) use ($kurs) {
                if ($kursstufe['objectid'] == $kurs['fkkursstufe']) {
                    return true;
                }
            }));

            if (count($matchedKursstufen) >= 1) {
                $kursstufeId = $matchedKursstufen[0]['insert_id'];

                $wpdb->insert($relationTable, array(
                    "rel_id" => $relId_kursstufe,
                    "parent_rel" => 0,
                    "parent_object_id" => $kursstufeId,
                    "child_object_id" => $kursId
                ));
            }

            //-----------

            //Kurstypen
            $matchedKurstypen = array_values(array_filter($kurstypen, function ($kurstyp) use ($kurs) {
                if ($kurstyp['objectid'] == $kurs['fkkurstyp']) {
                    return true;
                }
            }));

            if (count($matchedKurstypen) >= 1) {
                $kurstypId = $matchedKurstypen[0]['insert_id'];

                $wpdb->insert($relationTable, array(
                    "rel_id" => $relId_kurstyp,
                    "parent_rel" => 0,
                    "parent_object_id" => $kurstypId,
                    "child_object_id" => $kursId
                ));
            }

            //-----------

            //Kursleiter
            $kursleiter_objectids = unserialize($kurs['kursleiter_objectids']);

            foreach ($kursleiter_objectids as $kursleiter_objectid) {

                $matchedKursleiter = array_values(array_filter($kursleiter, function ($kursleiterItem) use ($kursleiter_objectid) {
                    if ($kursleiterItem['objectid'] == $kursleiter_objectid['kursleiter_objectid']) {
                        return true;
                    }
                }));

                if (count($matchedKursleiter) == 1) {
                    $kursleiterId = $matchedKursleiter[0]['insert_id'];

                    $wpdb->insert($relationTable, array(
                        "rel_id" => $relId_kursleiter,
                        "parent_rel" => 0,
                        "parent_object_id" => $kursleiterId,
                        "child_object_id" => $kursId
                    ));
                }
            }

            //-----------

            //Kursperioden
            $matchedKursperiode = array_values(array_filter($kursperioden, function ($kursperiode) use ($kurs) {
                if ($kursperiode['kursperiode'] == $kurs['kursperiode']) {
                    return true;
                }
            }));

            if (count($matchedKursperiode) == 1) {
                $kursperiodeId = $matchedKursperiode[0]['insert_id'];

                $wpdb->insert($relationTable, array(
                    "rel_id" => $relId_kursperiode,
                    "parent_rel" => 0,
                    "parent_object_id" => $kursperiodeId,
                    "child_object_id" => $kursId
                ));
            }
        }
    }

    private function loadKurse(): array
    {
        $kurseForDb = array();
        $da = new DXDataAccess();

        try {

            $kurse = $this->backendCommunication->getKurse();

            //Mapping JSON-Data to DB-fields
            foreach ($kurse as $kurs) {

                $kursdaten = array();
                $itemCount = 0;

                $kurstageauflistungArr = array();

                foreach ($kurs['Kursdaten'] as $oneKursData) {

                    $von = $this->loadKeyParseDatetime("Von", $oneKursData);
                    $bis = $this->loadKeyParseDatetime("Bis", $oneKursData);

                    $item = array(
                        "wochentag" => $this->loadkey("Wochentag", $oneKursData),
                        "von" => $von,
                        "bis" => $bis,
                        "datum" => $this->loadkey("Datum", $oneKursData),
                        "dauer" => $this->loadkey("Dauer", $oneKursData),
                        "kursraum" => $this->loadkey("Kursraum", $oneKursData),
                        "nummer" => $this->loadkey("Nummer", $oneKursData),
                        "abmeldegrund" => $this->loadkey("AbmeldeGrund", $oneKursData),
                    );

                    //only add the first two letters of the german Wochentag to the array
                    $kurstageauflistungArr[] = substr($item["wochentag"], 0, 2);

                    $kursdaten["item-$itemCount"] = $item;

                    $itemCount++;
                }

                $kurstageauflistungArr = array_unique($kurstageauflistungArr);
                $kurstageauflistung = implode(", ", $kurstageauflistungArr);

                //---

                $kursleiterIds = array();
                $itemCount = 0;
                foreach ($kurs['KursleiterIds'] as $oneKursleiterId) {

                    $item = array(
                        "kursleiterid" => $oneKursleiterId
                    );

                    $kursleiterIds["item-$itemCount"] = $item;

                    $itemCount++;
                }

                //---

                $kursleiter_vornamen = array();
                $kursleiter_nachname = array();
                $kursleiter_objectids = array();
                $itemCount = 0;
                foreach ($kurs['FkKursleiter'] as $oneKursleiterObjectId) {

                    $item = array(
                        "kursleiter_objectid" => $oneKursleiterObjectId
                    );

                    $kursleiter_objectids["item-$itemCount"] = $item;

                    $kursleiterRow = $da->getKursleiderByObjectId($oneKursleiterObjectId);
                    $kursleiter_vornamen[] = $kursleiterRow['vorname'];
                    $kursleiter_nachname[] = $kursleiterRow['name'];

                    $itemCount++;
                }

                $kursdatenAuflistungOriginal = $this->loadkey("KursdatenAuflistung", $kurs);
                $kursdatenAuflistungOptimized = $this->rearrangeKursdatenAuflistung($kursdatenAuflistungOriginal);

                $kurseForDb[] = array(
                    "objectid" => $this->loadkey("ObjectId", $kurs),
                    "geaendertam" => $this->loadkey("GeaendertAm", $kurs),
                    "kursleiter" => $this->loadkey("Kursleiter", $kurs),
                    "kursleiter_vorname" => implode(", ", $kursleiter_vornamen),
                    "kursleiter_nachname" => implode(", ", $kursleiter_nachname),
                    "fkkursgruppe" => $this->loadkey("FkKursgruppe", $kurs),
                    "kursgruppe" => $this->loadkey("Kursgruppe", $kurs),
                    "fkkurstyp" => $this->loadkey("FkKurstyp", $kurs),
                    "kurstyp" => $this->loadkey("Kurstyp", $kurs),
                    "fkkursstufe" => $this->loadkey("FkKursstufe", $kurs),
                    "kursstufe" => $this->loadkey("Kursstufe", $kurs),
                    "kursperiode" => $this->loadkey("Kursperiode", $kurs),
                    "sortierung" => $this->loadkey("Sortierung", $kurs),
                    "preis" =>  $this->loadKeyParseDecimal("Preis", $kurs),
                    "kleinklassenzuschlag" => $this->loadKeyParseDecimal("Kleinklassenzuschlag", $kurs),
                    "minimumteilnehmerkleinklasse" => $this->loadkey("MinimumTeilnehmerKleinklasse", $kurs),
                    "singleanmeldungzwingend" => $this->loadkey("SingleAnmeldungZwingend", $kurs),
                    "paaranmeldungzwingend" => $this->loadkey("PaarAnmeldungZwingend", $kurs),
                    "paartanz" => $this->loadkey("Paartanz", $kurs),
                    "paarrabatt" => $this->loadKeyParseDecimal("PaarRabatt", $kurs),
                    "schuelerstudentenrabatt" => $this->loadKeyParseDecimal("SchuelerStudentenRabatt", $kurs),
                    "viprabatt" => $this->loadKeyParseDecimal("VIPRabatt", $kurs),
                    "wiederholungsrabatt" => $this->loadKeyParseDecimal("WiederholungsRabatt", $kurs),
                    "kursanmeldungstopvoll" => $this->loadkey("KursAnmeldungStopVoll", $kurs),
                    "freieplaetzemann" => $this->loadkey("FreiePlaetzeMann", $kurs),
                    "freieplaetzefrau" => $this->loadkey("FreiePlaetzeFrau", $kurs),
                    "freieplaetze" => $this->loadkey("FreiePlaetze", $kurs),
                    "kursnummer" => $this->loadkey("Kursnummer", $kurs),
                    "name" => $this->loadkey("Name", $kurs),
                    "ctafliesstext" => $this->loadkey("CTAFliesstext", $kurs),
                    "kurswebtext" => $this->loadkey("KursWebText", $kurs),
                    "kurzzeichen" => $this->loadkey("Kurzzeichen", $kurs),
                    "anzahllektionen" => $this->loadkey("AnzahlLektionen", $kurs),
                    "ersterkurstag" => $this->loadkey("ErsterKurstag", $kurs),
                    "letzterkurstag" => $this->loadkey("LetzterKurstag", $kurs),
                    "kursdatenauflistung" => $kursdatenAuflistungOptimized,
                    "kurstageauflistung" => $kurstageauflistung,
                    "kursleiterids" => serialize($kursleiterIds),
                    "kursleiter_objectids" => serialize($kursleiter_objectids),
                    "kursdaten" => serialize($kursdaten),
                    "blockdauerdisplay" => $this->generateBlockDauerDisplay($kursdaten),
                    "blockzeitendisplay" => $this->generateBlockZeitenDisplay($kursdaten)
                );
            }
        } catch (\Throwable $th) {
            error_log("Exception occured while processing 'loadKurse':" . $th->getMessage());
            throw $th;
        }

        return  $kurseForDb;
    }

    private function loadEvents(): array
    {
        $eventsForDb = array();

        try {

            $events = $this->backendCommunication->getAllEvents();

            //Mapping JSON-Data to DB-fields
            foreach ($events as $event) {

                $flyer = $this->loadkey("Flyer", $event);

                $vorverkaufspreis = $this->loadkey("Vorverkaufspreis", $event);
                if ($vorverkaufspreis)
                    $vorverkaufspreis = str_replace(',', '.', $vorverkaufspreis);

                $abendkasse = $this->loadkey("Abendkasse", $event);
                if ($abendkasse)
                    $abendkasse = str_replace(',', '.', $abendkasse);

                $addedEntry = array(
                    "objectid" => $this->loadkey("ObjectId", $event),
                    "geaendertam" => $this->loadkey("GeaendertAm", $event),
                    "adresse" => $this->loadkey("Adresse", $event),
                    "beschreibung" => $this->loadkey("Beschreibung", $event),
                    "bis" => $this->loadkey("Bis", $event),
                    "datum" => $this->loadkey("Datum", $event),
                    "eventnummer" => $this->loadkey("Eventnummer", $event),
                    "kategorie" => $this->loadkey("Kategorie", $event),
                    "link" => $this->loadkey("Link", $event),
                    "ort" => $this->loadkey("Ort", $event),
                    "vorverkaufspreis" => $vorverkaufspreis,
                    "abendkasse" => $abendkasse,
                    "titel" => $this->loadkey("Titel", $event),
                    "von" => $this->loadkey("Von", $event),
                    "flyer_Dateiname" =>  $this->loadkey("Dateiname", $flyer),
                    "flyer_DokumentId" =>  $this->loadkey("DokumentId", $flyer),
                    "flyer_BildId" =>  $this->loadkey("BildId", $flyer),
                );

                if ($addedEntry["flyer_DokumentId"] != null && $addedEntry["flyer_Dateiname"] != null) {

                    $flyer_DokumentId = $addedEntry["flyer_DokumentId"];

                    $base64Doc = $this->backendCommunication->getDocument($flyer_DokumentId);
                    if ($base64Doc != null) {
                        $attachId = DXUtil::getAttachmentIdFromPreviouslyUploadedMedia($base64Doc);
                        if ($attachId == null) {
                            $attachId = DXUtil::importDocumentToMediaLibrary($addedEntry["flyer_Dateiname"], $base64Doc);
                        }
                        $addedEntry["flyer_Dokument"] = $attachId;
                    }
                }

                if ($addedEntry["flyer_BildId"] != null) {

                    $flyer_BildId = $addedEntry["flyer_BildId"];

                    $base64Img = $this->backendCommunication->getImage($flyer_BildId);
                    if ($base64Img != null) {
                        $attachId = DXUtil::getAttachmentIdFromPreviouslyUploadedMedia($base64Img);
                        if ($attachId == null) {
                            $attachId = DXUtil::importImageToMediaLibrary($base64Img);
                        }
                        $addedEntry["flyer_Bild"] = $attachId;
                    }
                }

                $eventsForDb[] = $addedEntry;
            }
        } catch (\Throwable $th) {
            error_log("Exception occured while processing 'loadEvents':" . $th->getMessage());
            throw $th;
        }

        return $eventsForDb;
    }

    private function loadKursgruppen(): array
    {
        $kursgruppenForDb = array();

        try {

            $kursgruppen = $this->backendCommunication->getAllKursgruppe();

            //Mapping JSON-Data to DB-fields
            foreach ($kursgruppen as $kursgruppe) {

                $addedEntry =  array(
                    "objectid" => $this->loadkey("ObjectId", $kursgruppe),
                    "geaendertam" => $this->loadkey("GeaendertAm", $kursgruppe),
                    "kurzzeichen" => $this->loadkey("Kurzzeichen", $kursgruppe),
                    "name" => $this->loadkey("Name", $kursgruppe),
                    "webtext" => $this->loadkey("WebText", $kursgruppe),
                    "teasertext" => $this->loadkey("Teasertext", $kursgruppe),
                    "sortierung" => $this->loadkey("Sortierung", $kursgruppe),
                    "webfiltera" => $this->loadkey("WebFilterA", $kursgruppe),
                    "webfilterb" => $this->loadkey("WebFilterB", $kursgruppe),
                    "bild" => $this->loadkey("Bild", $kursgruppe)
                );

                if ($addedEntry["bild"] != null) {

                    $base64Img = $addedEntry["bild"];

                    $attachId = DXUtil::getAttachmentIdFromPreviouslyUploadedMedia($base64Img);
                    if ($attachId == null) {
                        $attachId = DXUtil::importImageToMediaLibrary($base64Img);
                    }
                    $addedEntry["bild"] = $attachId;
                }

                $kursgruppenForDb[] = $addedEntry;
            }
        } catch (\Throwable $th) {
            error_log("Exception occured while processing 'loadKursgruppe':" . $th->getMessage());
            throw $th;
        }

        return $kursgruppenForDb;
    }

    private function loadKurstypen(): array
    {
        $kurstypenForDb = array();

        try {

            $kurstypen = $this->backendCommunication->getAllKurstyp();

            //Mapping JSON-Data to DB-fields
            foreach ($kurstypen as $kurstyp) {

                $addedEntry = array(
                    "objectid" => $this->loadkey("ObjectId", $kurstyp),
                    "geaendertam" => $this->loadkey("GeaendertAm", $kurstyp),
                    "kurzzeichen" => $this->loadkey("Kurzzeichen", $kurstyp),
                    "name" => $this->loadkey("Name", $kurstyp),
                    "webtext" => $this->loadkey("WebText", $kurstyp),
                    "sortierung" => $this->loadkey("Sortierung", $kurstyp),
                    "top4" => $this->loadkey("Top4", $kurstyp),
                    "bild" => $this->loadkey("Bild", $kurstyp)
                );

                if ($addedEntry["bild"] != null) {

                    $base64Img = $addedEntry["bild"];

                    $attachId = DXUtil::getAttachmentIdFromPreviouslyUploadedMedia($base64Img);
                    if ($attachId == null) {
                        $attachId = DXUtil::importImageToMediaLibrary($base64Img);
                    }
                    $addedEntry["bild"] = $attachId;
                }

                $kurstypenForDb[] = $addedEntry;
            }
        } catch (\Throwable $th) {
            error_log("Exception occured while processing 'loadKurstypen':" . $th->getMessage());
            throw $th;
        }

        return $kurstypenForDb;
    }

    private function loadKursstufen(): array
    {
        $kursstufenForDb = array();

        try {

            $kursstufen = $this->backendCommunication->getAllKursstufe();

            //Mapping JSON-Data to DB-fields
            foreach ($kursstufen as $kursstufe) {

                $addedEntry = array(
                    "objectid" => $this->loadkey("ObjectId", $kursstufe),
                    "geaendertam" => $this->loadkey("GeaendertAm", $kursstufe),
                    "kurzzeichen" => $this->loadkey("Kurzzeichen", $kursstufe),
                    "name" => $this->loadkey("Name", $kursstufe),
                    "webtext" => $this->loadkey("WebText", $kursstufe),
                    "teasertext" => $this->loadkey("TeaserText", $kursstufe),
                    "sortierung" => $this->loadkey("Sortierung", $kursstufe),
                    "top4" => $this->loadkey("Top4", $kursstufe),
                    "bild" => $this->loadkey("Bild", $kursstufe)
                );

                if ($addedEntry["bild"] != null) {

                    $base64Img = $addedEntry["bild"];

                    $attachId = DXUtil::getAttachmentIdFromPreviouslyUploadedMedia($base64Img);
                    if ($attachId == null) {
                        $attachId = DXUtil::importImageToMediaLibrary($base64Img);
                    }
                    $addedEntry["bild"] = $attachId;
                }

                $kursstufenForDb[] = $addedEntry;
            }
        } catch (\Throwable $th) {
            error_log("Exception occured while processing 'loadKursstufen':" . $th->getMessage());
            throw $th;
        }

        return $kursstufenForDb;
    }

    private function loadKursleiter(): array
    {
        $kursleiterForDb = array();

        try {

            $kursleiters = $this->backendCommunication->getAllKursleiterDetail();

            //Mapping JSON-Data to DB-fields
            foreach ($kursleiters as $kursleiter) {

                $addedEntry = array(
                    "objectid" => $this->loadkey("ObjectId", $kursleiter),
                    "geaendertam" => $this->loadkey("GeaendertAm", $kursleiter),
                    "id" => $this->loadkey("Id", $kursleiter),
                    "name" => $this->loadkey("Name", $kursleiter),
                    "vorname" => $this->loadkey("Vorname", $kursleiter),
                    "funktion" => $this->loadkey("Funktion", $kursleiter),
                    "webtext" => $this->loadkey("WebText", $kursleiter),
                    "sortierung" => $this->loadkey("Sortierung", $kursleiter),
                    "foto" => $this->loadkey("Foto", $kursleiter)
                );

                if ($addedEntry["foto"] != null) {

                    $base64Img = $addedEntry["foto"];

                    $attachId = DXUtil::getAttachmentIdFromPreviouslyUploadedMedia($base64Img);
                    if ($attachId == null) {
                        $attachId = DXUtil::importImageToMediaLibrary($base64Img);
                    }
                    $addedEntry["foto"] = $attachId;
                }

                $kursleiterForDb[] = $addedEntry;
            }
        } catch (\Throwable $th) {
            error_log("Exception occured while processing 'loadKursleiter':" . $th->getMessage());
            throw $th;
        }

        return  $kursleiterForDb;
    }


    //the & (reference operator) is needed or the insert_id-update does not work
    private function insertData(array &$rows, DXType $dxType, ?string $colToTakeTitleFrom)
    {
        try {
            global $wpdb;
            $prefixedTable = DXCctTableEnum::wpdb_prefix($dxType->dxCctTableEnum);

            foreach ($rows as &$row) {

                if ($dxType->hasSinglePost) {
                    $postTitle = 'no title set';
                    if (array_key_exists($colToTakeTitleFrom, $row))
                        $postTitle = $row[$colToTakeTitleFrom];

                    //post_name (slug) is automatically sanitized 
                    $createdPost =  wp_insert_post(array(
                        "post_author" => 1, //should be always admin -> todo:matz -> is this smart? Is there a better way?
                        "post_title" => $postTitle,
                        "post_name" => $postTitle,
                        "post_status" => "publish",
                        "post_type" => $dxType->cptTypeName
                    ), true, false);

                    if (is_wp_error($createdPost)) {
                        throw new Exception(implode("|", $createdPost->get_error_messages()));
                    }

                    //and link the CCT with the "shadow-behind-post"
                    $row['cct_single_post_id'] = $createdPost;
                }

                //so that all CCT are directly published
                $row['cct_status'] = 'publish';

                //effectively insert the CCT-row
                $wpdb->insert($prefixedTable, $row);

                if ($wpdb->insert_id == false)
                    throw new Exception("There was a problem inserting a new row!");

                //and add the inserted id to make it easier to create the relations later on
                $row['insert_id'] = $wpdb->insert_id;
            }
        } catch (\Throwable $th) {
            error_log("Exception occured while processing 'insertData' of table {$prefixedTable}:" . $th->getMessage());
            throw $th;
        }
    }

    private function loadkey($key, $arr)
    {
        if ($key == null || $arr == null)
            return null;

        return array_key_exists($key, $arr) ? $arr[$key] : null;
    }

    private function loadKeyParseDecimal($key, $arr)
    {
        $entry = $this->loadkey($key, $arr);
        if ($entry == null)
            return null;

        return str_replace(',', '.', $entry);
    }

    private function loadKeyParseDatetime($key, $arr): string|null
    {
        $entry = $this->loadkey($key, $arr);
        if ($entry == null)
            return null;

        $dateTime = DateTime::createFromFormat("Y-m-d H:i:s", $entry);
        if ($dateTime !== false) {
            return $dateTime->format("Y-m-d H:i");
        }

        return null;
    }

    private function rearrangeKursdatenAuflistung(string|null $input): string|null
    {
        if ($input == null)
            return null;

        // Split the string by ' / '
        $iso8601Dates_array = explode('/', $input);

        if (empty($iso8601Dates_array))
            return $input;

        // Array to hold the formatted dates
        $formatted_dates = array();

        // Loop through each date in the array
        foreach ($iso8601Dates_array as $iso8601Date) {

            // Convert the date to a timestamp
            $timestamp = strtotime(trim($iso8601Date));

            if ($timestamp == false)
                continue;

            // Format the date as DD.MM.YYYY
            $formatted_date = date('d.m.Y', $timestamp);

            // Add the formatted date to the array
            $formatted_dates[] = $formatted_date;
        }

        if (empty($formatted_dates))
            return $input;

        // Join the formatted dates with ' / ' delimiter
        return implode(' / ', $formatted_dates);
    }

    private function deriveKursperioden(array $kurse): array
    {
        $derivedKursperioden = array();

        foreach ($kurse as $kurs) {

            $kursperiode = $kurs['kursperiode'];

            $ersterkurstag = $kurs['ersterkurstag'];
            $currentErsterkurstagDate = new DateTime($ersterkurstag);

            $letzterkurstag = $kurs['letzterkurstag'];
            $currentLetzterkurstagDate = new DateTime($letzterkurstag);

            if (array_key_exists($kursperiode, $derivedKursperioden)) {

                $kursPeriodeDataToCheck = $derivedKursperioden[$kursperiode];

                if ($kursPeriodeDataToCheck->ersterkurstagDate > $currentErsterkurstagDate) {
                    $kursPeriodeDataToCheck->ersterkurstagDate = $currentErsterkurstagDate;
                }

                if ($kursPeriodeDataToCheck->letzterkurstagDate < $currentLetzterkurstagDate) {
                    $kursPeriodeDataToCheck->letzterkurstagDate = $currentLetzterkurstagDate;
                }
            } else {

                $kursPeriodeData = new stdClass();
                $kursPeriodeData->ersterkurstagDate = $currentErsterkurstagDate;
                $kursPeriodeData->letzterkurstagDate = $currentLetzterkurstagDate;

                $derivedKursperioden[$kursperiode] = $kursPeriodeData;
            }
        }

        $numberToGermanMonthArr = array(
            1 => "Januar",
            2 => "Februar",
            3 => "März",
            4 => "April",
            5 => "Mai",
            6 => "Juni",
            7 => "Juli",
            8 => "August",
            9 => "September",
            10 => "Oktober",
            11 => "November",
            12 => "Dezember"
        );

        $result = array();
        foreach ($derivedKursperioden as $key => $value) {

            $ersterkurstagDate = $value->ersterkurstagDate;
            $startkursperiodemonatzahl = $ersterkurstagDate->format('n'); //Get the Month as a Number without Leading Zeros
            $startkursperiodemonat = $numberToGermanMonthArr[$startkursperiodemonatzahl];

            $letzterkurstagDate = $value->letzterkurstagDate;
            $endekursperiodemonatzahl = $letzterkurstagDate->format('n'); //Get the Month as a Number without Leading Zeros
            $endekursperiodemonat = $numberToGermanMonthArr[$endekursperiodemonatzahl];

            $result[] = array(
                "kursperiode" => $key,

                "startkursperiodemonat" => $startkursperiodemonat,
                "startkursperiodemonatzahl" => $startkursperiodemonatzahl,
                "startkursperiodejahr" => $ersterkurstagDate->format('Y'), //Get the Year as a Number

                "endekursperiodemonat" =>  $endekursperiodemonat,
                "endekursperiodemonatzahl" => $endekursperiodemonatzahl,
                "endekursperiodejahr" => $letzterkurstagDate->format('Y'), //Get the Year as a Number
            );
        }

        return $result;
    }

    private function generateBlockDauerDisplay(array $kursdaten): string|null
    {
        if ($kursdaten == null)
            return null;

        $countingArr = array();

        // Go through the kursdaten and count together the Dauer of them (https://web-helferlein.atlassian.net/browse/DXCP-4)
        foreach ($kursdaten as $kursData) {

            $dauer = $kursData['dauer'];

            if (array_key_exists($dauer, $countingArr)) {
                $countingArr[$dauer] = $countingArr[$dauer] + 1;
            } else {
                $countingArr[$dauer] = 1;
            }
        }

        // Sort the array by key (dauer) in ascending order - smallest dauer to biggest dauer
        ksort($countingArr);

        //prepare strings
        $stringArr = array();
        foreach ($countingArr as $dauer => $count) {
            $stringArr[] = "{$count} x {$dauer}";
        }

        //and combine them
        return implode(" | ", $stringArr);
    }

    private function generateBlockZeitenDisplay(array $kursdaten): string|null
    {
        if ($kursdaten == null)
            return null;

        $countingArr = array();

        // Go through the kursdaten and extract (differnt) Von-Zeit - Bis-Zeit (https://web-helferlein.atlassian.net/browse/DXCP-4)
        foreach ($kursdaten as $kursData) {

            $von = $kursData['von'];
            //  $vonDt = DateTime::createFromFormat("Y-m-d H:i", $von);
            $bis = $kursData['bis'];

            $vonTime = $this->extractTimePortion($von);
            $bisTime = $this->extractTimePortion($bis);

            $vonTimeToBisTime =  "{$vonTime} - {$bisTime}";

            if (!array_key_exists($vonTime, $countingArr)) {
                $countingArr[$vonTime] = $vonTimeToBisTime;
            }
        }

        // Sort the array by key (von-Datetime) in ascending order so that the first starting Blocks are displayed first
        ksort($countingArr);

        return implode(" | ", $countingArr);
    }

    private function extractTimePortion(string|null $input): string|null
    {
        if ($input == null)
            return null;

        // Convert the date to a timestamp
        $timestamp = strtotime(trim($input));

        if ($timestamp == false)
            return null;

        // Format the date as HH:MM
        return date('H:i', $timestamp);
    }
}
