<?php

namespace Webapp\FileManagement\Service;

use Mobile\Device\Entity\Modality;
use Mobile\Measure\Entity\Variable\StateCode;
use Shared\Authentication\Repository\UserRepository;
use SimpleXMLElement;
use Webapp\FileManagement\Entity\RequestFile;
use Webapp\FileManagement\Exception\ParsingException;

/**
 * Class AbstractReaderHelper.
 */
abstract class AbstractReaderHelper
{
    /**
     * @var StateCode[]
     */
    protected $stateCodes;

    /**
     * @var string
     */
    protected $projectDir;

    /**
     * @var UserRepository
     */
    protected UserRepository $userRepository;

    /**
     * MobileReaderHelper constructor.
     * @param UserRepository $userRepository
     * @param string $projectDir
     */
    public function __construct(UserRepository $userRepository, string $projectDir)
    {
        $this->stateCodes = [];
        $this->projectDir = $projectDir;
        $this->userRepository = $userRepository;
    }

    /**
     * @param StateCode[] $stateCodes
     */
    public function setStateCodes(array $stateCodes): void
    {
        $this->stateCodes = $stateCodes;
    }

    /**
     * @param SimpleXMLElement $deviceXml
     * @throws ParsingException
     */
    protected function testDevice(SimpleXMLElement $deviceXml, array $uriMap, string $deviceUri): void
    {
        if (!isset($deviceXml->attributes()['nom'])
        ) {
            throw new ParsingException("", RequestFile::ERROR_UNKNOWN_MATERIAL);
        }
        if (isset($uriMap[$deviceUri])) {
            throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
        }
    }

    /**
     * @param SimpleXMLElement $variableXml
     * @param array $uriMap
     * @param string $variableUri
     * @return void
     * @throws ParsingException
     */
    protected function testVariable(SimpleXMLElement $variableXml, array $uriMap, string $variableUri): void
    {
        if (
            !isset($variableXml->attributes()['nom']) ||
            !isset($variableXml->attributes()['nomCourt']) ||
            !isset($variableXml->attributes()['nbRepetitionSaisies']) ||
            !isset($variableXml->attributes()['dateCreation']) ||
            !isset($variableXml->attributes()['dateDerniereModification'])
        ) {
            throw new ParsingException("", RequestFile::ERROR_STATE_VARIABLE_INFO_MISSING);
        }
        if (isset($variableXml->attributes()['nomGenere'])) {
            if (!isset($variableXml->children()->variablesGenerees)) {
                throw new ParsingException("", RequestFile::ERROR_NO_GENERATED_VARIABLE);
            }
        }
        if (isset($variableXml->attributes("xsi", true)['type']) && $variableXml->attributes("xsi", true)['type']->__toString() === "adonis.modeleMetier.projetDeSaisie.variables:VariableSemiAuto") {
            if (!isset($variableXml->attributes()['materiel']) || !isset($uriMap[$variableXml->attributes()['materiel']->__toString()])) {
                throw new ParsingException("", RequestFile::ERROR_UNKNOWN_MATERIAL);
            }
        }
        if (isset($uriMap[$variableUri])) {
            throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
        }
    }

    /**
     * @param SimpleXMLElement $valueListXml
     * @throws ParsingException
     */
    protected function testValueHintList(SimpleXMLElement $valueListXml): void
    {
        if (!isset($valueListXml->attributes()['nom'])) {
            throw new ParsingException("", RequestFile::ERROR_VALUE_HINT_LIST_INFO_MISSING);
        }
    }

    /**
     * @param SimpleXMLElement $previousValueXml
     * @param array $uriMap
     * @return void
     * @throws ParsingException
     */
    protected function testPreviousValue(SimpleXMLElement $previousValueXml, array $uriMap): void
    {
        if (
            !isset($previousValueXml->attributes()['objetMetier']) ||
            !isset($previousValueXml->attributes()['variable'])
        ) {
            throw new ParsingException("", RequestFile::ERROR_PREVIOUS_VALUE_INFO_MISSING);
        }
        if (!isset($uriMap[$previousValueXml->attributes()['variable']->__toString()])) {
            throw new ParsingException("", RequestFile::ERROR_URI_NOT_FOUND);
        }
        if (
            isset($previousValueXml->attributes()['codeEtat']) &&
            isset($uriMap[$previousValueXml->attributes()['codeEtat']->__toString()]) &&
            !array_key_exists($uriMap[$previousValueXml->attributes()['codeEtat']->__toString()]->getCode(), $this->stateCodes)
        ) {
            throw new ParsingException("Code d'état utilisé dans les mesures de variable connecté non présent dans le projet courant", RequestFile::ERROR_URI_NOT_FOUND);
        }
        if (!isset($uriMap[$previousValueXml->attributes()['objetMetier']->__toString()])) {
            throw new ParsingException("", RequestFile::ERROR_UNKNOWN_PREVIOUS_VALUE_LINKED_OBJECT);
        }
    }

    /**
     * @param SimpleXMLElement $scaleXml
     * @throws ParsingException
     */
    protected function testScale(SimpleXMLElement $scaleXml): void
    {
        if (!isset($scaleXml->attributes()['nom'])) {
            throw new ParsingException("", RequestFile::ERROR_SCALE_INFO_MISSING);
        }
    }

    /**
     * @param SimpleXMLElement $markXml
     * @throws ParsingException
     */
    protected function testMark(SimpleXMLElement $markXml): void
    {
        if ($markXml->attributes()['image'] != "" && !file_exists($this->projectDir . 'echelles/' . $markXml->attributes()['image'])) {
            throw new ParsingException("", RequestFile::ERROR_FILE_NOT_FOUND);
        }
    }

    /**
     * @param SimpleXMLElement $testXml
     * @param array $uriMap
     * @throws ParsingException
     */
    protected function testTest(SimpleXMLElement $testXml, array $uriMap, string $variableUri): void
    {
        if (
            !isset($testXml->attributes()['nom']) ||
            !isset($testXml->attributes()['active'])
        ) {
            throw new ParsingException("", RequestFile::ERROR_TEST_INFO_MISSING);
        }
        switch ($testXml->attributes()['nom']->__toString()) {
            case "Test sur intervalle" :
                break;
            case "Test d'accroissement" :
                if (count(explode("/", $testXml->attributes()['variableDeComparaison'])) < 3) {
                    if (!isset($uriMap[$testXml->attributes()['variableDeComparaison']->__toString()])) {
                        throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_LINKED_VARIABLE);
                    }
                } else {
                    if (!isset($uriMap[$testXml->attributes()['variableDeComparaison']->__toString()])) {
                        throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_LINKED_VARIABLE);
                    }
                }
                break;
            case "Test sur combinaison entre variables" :
                if (count(explode("/", $testXml->attributes()['operande1'])) < 3) {
                    if (!isset($uriMap[$testXml->attributes()['operande1']->__toString()])) {
                        throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_LINKED_VARIABLE);
                    }
                } else {
                    if (!isset($uriMap[$testXml->attributes()['operande1']->__toString()])) {
                        throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_LINKED_VARIABLE);
                    }
                }
                if (count(explode("/", $testXml->attributes()['operande2'])) < 3) {
                    if (!isset($uriMap[$testXml->attributes()['operande2']->__toString()])) {
                        throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_LINKED_VARIABLE);
                    }
                } else {
                    if (!isset($uriMap[$testXml->attributes()['operande2']->__toString()])) {
                        throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_LINKED_VARIABLE);
                    }
                }
                break;
            case "Précalcul conditionnel" :
                if (count(explode("/", $testXml->attributes()['variableDeComparaison'])) < 3) {
                    if (!isset($uriMap[$testXml->attributes()['variableDeComparaison']->__toString()])) {
                        throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_LINKED_VARIABLE);
                    }
                } else {
                    if (!isset($uriMap[$testXml->attributes()['variableDeComparaison']->__toString()])) {
                        throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_LINKED_VARIABLE);
                    }
                }
                if (isset($testXml->attributes()['variableAComparer'])) {
                    if (count(explode("/", $testXml->attributes()['variableAComparer'])) < 3) {
                        if (!isset($uriMap[$testXml->attributes()['variableAComparer']->__toString()])) {
                            throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_LINKED_VARIABLE);
                        }
                    } else {
                        if (!isset($uriMap[$testXml->attributes()['variableAComparer']->__toString()])) {
                            throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_LINKED_VARIABLE);
                        }
                    }
                }
                break;
            default :
                throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_TYPE);
        }
        if (!isset($uriMap[$variableUri])) {
            throw new ParsingException("", RequestFile::ERROR_UNKNOWN_TEST_LINKED_VARIABLE);
        }
    }

    /**
     * @param SimpleXMLElement $dataEntryProjectXml
     * @throws ParsingException
     */
    protected function testDataEntryProject(SimpleXMLElement $dataEntryProjectXml): void
    {
        if ($dataEntryProjectXml->attributes()['typeSaisie'] == "spatialisation") {
            throw new ParsingException("", RequestFile::ERROR_INCORRECT_FILE_TYPE);
        }
    }

    /**
     * @param SimpleXMLElement $mandatoryAnnotationXml
     * @param $uriMap
     * @return void
     * @throws ParsingException
     */
    protected function testRequiredAnnotation(SimpleXMLElement $mandatoryAnnotationXml, $uriMap): void
    {
        foreach (explode(' ', $mandatoryAnnotationXml->attributes()['objetMetier']) as $item) {
            if (!empty($item)) {
                if (!isset($uriMap[$item])) {
                    throw new ParsingException("", RequestFile::ERROR_WORKPATH_ITEM_NOT_FOUND);
                }
            }
        }
        switch ($mandatoryAnnotationXml->attributes()['nature'] === null ? "son" : $mandatoryAnnotationXml->attributes()['nature']->__toString()) {
            case "texte":
            case "photo":
            case "son":
                break;
            default :
                throw new ParsingException("", RequestFile::ERROR_INCORRECT_ANNOTATION_TYPE);
        }
    }

    /**
     * @param $pathsXml
     * @param $uriMap
     * @return void
     * @throws ParsingException
     */
    protected function testPaths($pathsXml, $uriMap)
    {
        foreach ($pathsXml as $pathXml) {
            if (
                !isset($pathXml->attributes()['objets']) ||
                !isset($pathXml->attributes()['nom'])
            ) {
                continue;
            }
            foreach (explode(' ', $pathXml->attributes()['objets']) as $item) {
                if (!isset($uriMap[$item])) {
                    throw new ParsingException("", RequestFile::ERROR_WORKPATH_ITEM_NOT_FOUND);
                }
            }
        }
    }

    /**
     * @param SimpleXMLElement $platformXml
     * @param array $uriMap
     * @return void
     * @throws ParsingException
     */
    protected function testPlatform(SimpleXMLElement $platformXml, array $uriMap): void
    {
        if (!isset($platformXml->attributes()['nom']) ||
            !isset($platformXml->attributes()['nomSite']) ||
            !isset($platformXml->attributes()['nomLieu']) ||
            !isset($platformXml->attributes()['dateCreation'])
        ) {
            throw new ParsingException("", RequestFile::ERROR_PLATFORM_MISSING);
        }
        if (!isset($platformXml->children()->dispositifs)) {
            throw new ParsingException("", RequestFile::ERROR_NO_DEVICE);
        }
    }

    /**
     * @param SimpleXMLElement $experimentXml
     * @param array $uriMap
     * @param string $experimentURI
     * @return void
     * @throws ParsingException
     */
    protected function testExperiment(SimpleXMLElement $experimentXml, array $uriMap, string $experimentURI): void
    {
        if (!isset($experimentXml->children()->protocoles)) {
            throw new ParsingException("", RequestFile::ERROR_NO_PROTOCOL);
        }
        if (isset($uriMap[$experimentURI])) {
            throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
        }
        $blocCNT = 0;
        foreach ($experimentXml->children()->blocs as $blocXml) {
            if (isset($uriMap[$experimentURI . "/@blocs." . $blocCNT])) {
                throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
            }
        }
    }

    /**
     * @param SimpleXMLElement $protocolXml
     * @param array $uriMap
     * @param string $protocolUri
     * @return void
     * @throws ParsingException
     */
    protected function testProtocol(SimpleXMLElement $protocolXml, array $uriMap, string $protocolUri): void
    {
        if (
            !isset($protocolXml->attributes()['nom']) ||
            !isset($protocolXml->attributes()['dateCreation']) ||
            !isset($protocolXml->attributes()['createur']) ||
            !isset($protocolXml->attributes()['algorithmeTirage'])
        ) {
            throw new ParsingException("", RequestFile::ERROR_PROTOCOL_INFO_MISSING);
        }
        if (isset($uriMap[$protocolUri])) {
            throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
        }
    }

    /**
     * @param SimpleXMLElement $factorXml
     * @param array $uriMap
     * @param string $factorUri
     * @return void
     * @throws ParsingException
     */
    protected function testFactor(SimpleXMLElement $factorXml, array $uriMap, string $factorUri): void
    {
        if (!isset($factorXml->attributes()['nom'])) {
            throw new ParsingException("", RequestFile::ERROR_PROTOCOL_INFO_MISSING);
        }
        if (isset($uriMap[$factorUri])) {
            throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
        }
    }

    /**
     * @param SimpleXMLElement $modalityXml
     * @throws ParsingException
     */
    protected function testModality(SimpleXMLElement $modalityXml, array $uriMap, string $moralityUri): void
    {
        if (!isset($modalityXml->attributes()['valeur'])) {
            throw new ParsingException("", RequestFile::ERROR_PROTOCOL_INFO_MISSING);
        }
        if (isset($uriMap[$moralityUri])) {
            throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
        }
    }

    /**
     * @param SimpleXMLElement $treatmentXml
     * @param array $uriMap
     * @return void
     * @throws ParsingException
     */
    protected function testTreatment(SimpleXMLElement $treatmentXml, array $uriMap, string $treatmentUri): void
    {
        if (
            !isset($treatmentXml->attributes()['nom']) ||
            !isset($treatmentXml->attributes()['nomCourt']) ||
            !isset($treatmentXml->attributes()['combinaisons'])
        ) {
            throw new ParsingException("", RequestFile::ERROR_PROTOCOL_INFO_MISSING);
        }
        foreach (explode(' ', trim($treatmentXml->attributes()['combinaisons'])) as $item) {
            if (!isset($uriMap[$item])) {
                throw new ParsingException("", RequestFile::ERROR_MODALITY_ITEM_NOT_FOUND);
            }
            if (!get_class($uriMap[$item]) === Modality::class) {
                throw new ParsingException("", RequestFile::ERROR_MODALITY_ITEM_NOT_FOUND);
            }
        }
        if (isset($uriMap[$treatmentUri])) {
            throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
        }
    }

    /**
     * @param SimpleXMLElement $blocXml
     * @throws ParsingException
     */
    protected function testBlock(SimpleXMLElement $blocXml, array $uriMap, string $blockUri): void
    {
        if (!isset($blocXml->children()->parcellesUnitaire) &&
            !isset($blocXml->children()->sousBlocs)) {
            throw new ParsingException("", RequestFile::ERROR_NO_BLOC_CHILDREN);
        }
        if (isset($uriMap[$blockUri])) {
            throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
        }
    }

    /**
     * @param SimpleXMLElement $outExperimentationZoneXml
     * @param array $uriMap
     * @return void
     * @throws ParsingException
     */
    protected function testOutExperimentationZone(SimpleXMLElement $outExperimentationZoneXml, array $uriMap): void
    {
        if (
            !isset($outExperimentationZoneXml->attributes()['numero']) ||
            !isset($outExperimentationZoneXml->attributes()['natureZhe'])
        ) {
            throw new ParsingException("", RequestFile::ERROR_OUT_EXPERIMENTATION_ZONE_INFO_MISSING);
        }
        if (!isset($uriMap[$outExperimentationZoneXml->attributes()['natureZhe']->__toString()])) {
            throw new ParsingException("", RequestFile::ERROR_URI_NOT_FOUND);
        }
    }

    /**
     * @param array $uriMap
     * @param string $subBlockUri
     * @throws ParsingException
     */
    protected function testSubBlock(array $uriMap, string $subBlockUri): void
    {
        if (isset($uriMap[$subBlockUri])) {
            throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
        }
    }

    /**
     * @param array $uriMap
     * @param string $puUri
     * @return void
     * @throws ParsingException
     */
    protected function testUnitPlot(array $uriMap, string $puUri): void
    {
        if (isset($uriMap[$puUri])) {
            throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
        }
    }

    /**
     * @param array $uriMap
     * @param string $individualUri
     * @return void
     * @throws ParsingException
     */
    protected function testIndividual(array $uriMap, string $individualUri): void
    {
        if (isset($uriMap[$individualUri])) {
            throw new ParsingException("", RequestFile::ERROR_DUPLICATED_URI);
        }
    }

    /**
     * @param SimpleXMLElement $stateCodeXml
     * @throws ParsingException
     */
    protected function testStateCode(SimpleXMLElement $stateCodeXml): void
    {
        if (
            !isset($stateCodeXml->attributes()['code']) ||
            !isset($stateCodeXml->attributes()['intitule'])
        ) {
            throw new ParsingException("", RequestFile::ERROR_STATE_CODE_INFO_MISSING);
        }
    }

    /**
     * @param SimpleXMLElement $noteXml
     * @return void
     * @throws ParsingException
     */
    protected function testEntryNote(SimpleXMLElement $noteXml): void
    {
        if ($this->userRepository->findOneBy(["username" => $noteXml->attributes()["loginCreateur"]->__toString()]) === null) {
            throw new ParsingException("", RequestFile::ERROR_NOTE_CREATOR_NOT_FOUND);
        }
    }

    /**
     * @param SimpleXMLElement $projectXml
     * @throws ParsingException
     */
    protected function testProject(SimpleXMLElement $projectXml): void
    {
        if (!isset($projectXml->attributes()['nom']) ||
            !isset($projectXml->attributes()['dateCreation'])
        ) {
            throw new ParsingException("", RequestFile::ERROR_DATA_ENTRY_PROJECT_MISSING);
        }
    }

    /**
     * @param $dataEntryXml
     * @param $uriMap
     * @return void
     * @throws ParsingException
     */
    protected function testDataEntry($dataEntryXml, $uriMap)
    {
        if (!isset($dataEntryXml->attributes()['projetDeSaisie'])) {
            throw new ParsingException("", RequestFile::ERROR_DATA_ENTRY_INFO_MISSING);
        }
    }

    /**
     * @param $sessionXml
     * @return void
     * @throws ParsingException
     */
    protected function testSession($sessionXml)
    {
        if (!isset($sessionXml->attributes()['dateDebut']) ||
            !isset($sessionXml->attributes()['dateFin'])
        ) {
            throw new ParsingException("", RequestFile::ERROR_DATA_ENTRY_INFO_MISSING);
        }
    }
}
