<?php

namespace Modules\EtatStatistiques\Service;

use Helpers\Outil\ArrayHelper;
use Helpers\Outil\Sql;
use Helpers\Outil\XmlHelper;
use SimpleXMLElement;

class EtatStatImportParser
{
    public function __construct()
    {
    }
    
    /**
     * @return array<mixed>
     */
    public function parseEtatStat(SimpleXMLElement $xml) :array
    {
        return [
            'CODETAT' => (string) $xml->CODETAT,
            'DESCRIPT' => (string) $xml->DESCRIPT,
            'NOMFIC' => (string) $xml->NOMFIC,
            'Source' => 'Requeteur',
            'TYPERES' => $typeres = (string) $xml->TYPERES,
            'PERIMETRE' => Sql::toBool((string) $xml->PERIMETRE),

            'COMMENTAI' => (string) $xml->COMMENTAI,

            'ListFiltreType' => array_filter(array_map(function ($fentXml) {
                if (((string) $fentXml->TYPE) != 'USER') {
                    return null;
                }
                return $this->parseFent($fentXml);
            }, XmlHelper::toArray($xml->ListeREQ_FILTRES, 'REQ_FILTRES'))),
        ]
        //+ ($source == 'OXACHARTS_REQ' ?
        + [
            'Requete' => $this->parseReq($xml),

            // TODO: ListeREQ_OPTIONS
            // TODO: ListeREQ_SERIES
        ]
        + ($typeres == 'G'/* Graphique */
            ? [
                'Option' => $this->parseOption($xml->ListeREQ_OPTIONS->REQ_OPTIONS),
                'ListSerie' => array_map(function ($fentXml) {
                    return $this->parseSerie($fentXml);
                }, XmlHelper::toArray($xml->ListeREQ_SERIES, 'REQ_SERIES')),
            ]
            : []
        );
    }

    /**
     * @return array<mixed>
     */
    private function parseReq(SimpleXMLElement $xml) :array
    {
        $reqXml = $xml->ListeREQ_ENT->REQ_ENT;
        $mapFiltre = array_column(
            array_map(function ($filtreXml) {
                return ($filtre = $this->parseFiltre($filtreXml))
                    + ['_id' => $filtre['TYPE_FILTRE'] . '|' . $filtre['CODE_FILTRE']];
            }, XmlHelper::toArray($xml->ListeREQ_FILTRES, 'REQ_FILTRES')),
            null,
            '_id'
        );

        $listJoin = array_map(function ($joinXml) {
            return $this->parseJoin($joinXml);
        }, XmlHelper::toArray($reqXml->ListeREQ_FROM, 'REQ_FROM'));

        return [
            // REQ_ID is ignored, only one request is supported anyhow
            'REQ_NOM' => (string) $reqXml->REQ_NOM,
            'TOP_N' => (string) $reqXml->TOP_N,
            'Where' => (string) $reqXml->MEMO_WHERE,// TODO DB side
            
            'NOM_CHP_PER_SOC' => (string) $reqXml->NOM_CHP_PER_SOC, // TODO
            'NOM_CHP_PER_CONF' => (string) $reqXml->NOM_CHP_PER_CONF, // TODO

            'REQ_LIBRE' => $isReqLibre = Sql::toBool($reqXml->REQ_LIBRE), // TODO

            'ListFilter' => array_filter(array_map(function ($filtreXml) use ($mapFiltre) {
                return $this->parseFiltreAssoc($filtreXml, $mapFiltre);
            }, XmlHelper::toArray($reqXml->ListeREQ_FILTRES_DON, 'REQ_FILTRES_DON'))),
            'Rupture' => $this->parseRupture($reqXml),
        ]
        + ($isReqLibre ? [
            'ReqLibre' => (string) $reqXml->MEMO_REQ_LIB, // TODO
        ] : [
            'From' => ArrayHelper::findFirst($listJoin, function ($item) {
                return $item['TYPE_JOINT'] == 'M' /* Table principale */;
            }),
            'ListJoin' => array_filter($listJoin, function ($item) {
                return $item['TYPE_JOINT'] != 'M';
            }),
            'ListSelect' => array_map(function ($selectXml) {
                return $this->parseSelect($selectXml);
            }, XmlHelper::toArray($reqXml->ListeREQ_SELECT, 'REQ_SELECT')),
        ]);
    }

    /**
     * @return array<mixed>
     */
    private function parseJoin(SimpleXMLElement $xml) :array
    {
        return [
            'RFRO_NO_ORDRE' => intval($xml->NO_ORDRE),
            'TABLE_NOM' => (string) $xml->NOM,
            'TABLE_ALIAS' => (string) $xml->ALIAS,
            'TYPE_JOINT' => (string) $xml->TYPE_JOINT, // TODO : check in_array
            'ClausePillable' => [(string) $xml->CLAUSE_JOINT], // TODO CLAUSE_JOINT
            // TYPE_TV is ignored
        ];
    }

    /**
     * @return array<mixed>
     */
    private function parseSelect(SimpleXMLElement $xml) :array
    {
        return [
            'ELEM_NORD' => (int) $xml->NORD, // TODO
            'ELEM_SQL' => (string) $xml->SQL,
            'ELEM_ALIAS' => (string) $xml->ALIAS,
            'RSEL_TYPE_DONNEE' => (string) $xml->TYPE_DONNEE,
            'ELEM_GRP_BY' => (string) $xml->GRP_BY,
            'ELEM_OPE_GRP' => (string) $xml->OPE_GRP,
            'TYPE_TRI' => (string) $xml->TYPE_TRI,
            'ORDRE_TRI' => (int) $xml->ORDRE_TRI, // TODO : support ORDRE_TRI
        ];
    }

    /**
     * @param array<mixed> $mapFitlre
     * @return ?array<mixed>
     */
    private function parseFiltreAssoc(SimpleXMLElement $xml, array $mapFitlre) :?array
    {
        $filtre = [
            'TYPE_FILTRE' => (string) $xml->TYPE,
            'CODE_FILTRE' => (string) $xml->CODE,
            'NomChamp' => (string) $xml->NOM_CHP,
        ];

        $filtreType = $mapFitlre[
            $filtre['TYPE_FILTRE'] . '|' . $filtre['CODE_FILTRE']
        ] ?? null;

        // Note : sometime magic export Filtre without type
        // Case : ignore broken Filtre
        if (empty($filtreType)) {
            return null;
        }

        return $filtre + $filtreType;
    }

    /**
     * @return array<mixed>
     */
    private function parseFiltre(SimpleXMLElement $xml) :array
    {
        return [
            'TYPE_FILTRE' => (string) $xml->TYPE,
            'CODE_FILTRE' => (string) $xml->CODE,
            'LIB_FILTRE' => (string) $xml->LIB,
            'TYPE_CHP_FILTRE' => (string) $xml->TYPE_CHP,
            'TYPE_DON_FILTRE' => (string) $xml->TYPE_DON,
        ];
    }

    /**
     * @return array<mixed>
     */
    private function parseOption(SimpleXMLElement $xml) :array
    {
        return [
            'G_TITRE' => (string) $xml->G_TITRE,
            'G_STITRE' => (string) $xml->G_STITRE,
            'G_LABELS' => Sql::toBool($xml->G_LABELS),
            'G_DATA' => Sql::toBool($xml->G_DATA),
            'G_MULTITYPE' => Sql::toBool($xml->G_MULTITYPEW),
            'G_TYPE_MONO' => (string) $xml->G_TYPE_MONO,
            'X_ALIAS' => (string) $xml->X_ALIAS,
            'X_TITRE' => (string) $xml->X_TITRE,
            'X_INCLINE' => Sql::toBool($xml->X_INCLINE),
            'Y1_TITRE' => (string) $xml->Y1_TITRE,
            'Y1_UNITE' => (string) $xml->Y1_UNITE,
            'Y2_AFF' => Sql::toBool($xml->Y2_AFF),
            'Y2_TITRE' => (string) $xml->Y2_TITRE,
            'Y2_UNITE' => (string) $xml->Y2_UNITE,
            // TODO ? 'T_TITRE' => (string) $xml->T_TITRE,
            // 'T_STITRE' => (string) $xml->T_STITRE,
        ];
    }

    /**
     * @return array<mixed>
     */
    private function parseSerie(SimpleXMLElement $xml) :array
    {
        return [
            'SERIE_ID' => (int) $xml->ID,
            'SERIE_ALIAS' => (string) $xml->ALIAS,
            'SERIE_NOM' => (string) $xml->NOM,
            'TYPE_GRAPH' => (string) $xml->TYPE_GRAPH,
            'NUM_AXE' => (int) $xml->NUM_AXE,
            'SERIE_COLOR' => (string) $xml->COLOR,
        ];
    }

    /**
     * @return array<mixed>
     */
    private function parseFent(SimpleXMLElement $xml)
    {
        $fent = $xml->ListeFILT_ENT->FILT_ENT;
        return [
            'FENT_CODE_FILTRE' => (string) $xml->CODE,
            'FENT_LIB_FILTRE' => (string) $xml->LIB,
            'FENT_TYPE_DONNEE' => (string) $xml->TYPE_DON,
            // TODO : TYPE_CHP ?
            'FENT_TYPE_AFFICHAGE' => ($type = (string)$fent->TYPE_AFFICHAGE),
        ]
        + ($type == 'L' ? [
            'ListChoix' => array_map(function ($choixXml) {
                return [
                    'FLIS_VALEUR' => (string) $choixXml->VALEUR,
                    'FLIS_LIBELLE' => (string) $choixXml->LIBELLE,
                ];
            }, XmlHelper::toArray($fent->ListeFILT_LISTE, 'FILT_LISTE'))
            
        ] : [
            'FENT_BASE_DE_DONNEE' => (string) $xml->BASE_DE_DONNEE,
            'ReqLibre' => (string) $xml->MEMO_REQ_LIB,
            'FENT_NOM_CHP_PER_SOC' => (string) $xml->NOM_CHP_PER_SOC,
            'FENT_NOM_CHP_PER_CONF' => (string) $xml->NOM_CHP_PER_CONF,
        ]);
    }

    /**
     * @return array<mixed>
     */
    private function parseRupture(SimpleXMLElement $xml)
    {
        $rupXml = $xml->ListeREQ_RUPTURES->REQ_RUPTURES;
        return [
            'NUM_ORD' => intval($rupXml->NUM_ORD), // TODO
            'NOM_CHP' => (string) $rupXml->NOM_CHP,
            'TEXTE_RUP' => (string) $rupXml->TEXTE_RUP,
            'ListRuptureTot' => array_map(function ($rupTotXml) {
                return [
                    'NUM_ORD' => intval($rupTotXml->NUM_ORD), // TODO
                    'NOM_CHP' => (string) $rupTotXml->NOM_CHP,
                    'ELEM_OPE_GRP' => (string) $rupTotXml->OPE_GRP,
                ];
            }, XmlHelper::toArray($xml->ListeREQ_RUPTURES_TOTAUX, 'REQ_RUPTURES_TOTAUX'))
        ];
    }
}
