<?php

namespace Modules\Additional\Specifique\Client155\Modules\Consultation\Outil;

use Entity\Model\Fourniss;
use Helpers\ExportTo\Generate;
use Helpers\Log\LogSysteme;
use Helpers\Outil\Date;
use Helpers\Trad\ITrad;
use Helpers\Trad\Trad;
use Modules\GlobalApplication\WebService\DocRattaches;
use PhpOffice\PhpSpreadsheet\IOFactory;
use Helpers\Session\ParseBase1;

class Consolidation
{
    /** @var \PDO $pdo */
    private \PDO $pdo;

    /** @var array<mixed> $grilles */
    private array $grilles;

    /** @var array|string[] $extensionAuthorize */
    private array $extensionAuthorize = [
        'xls', 'xlsx', 'csv'
    ];

    /** @var bool $isValid */
    private bool $valid = true;

    /** @var array<string> $errors */
    private array $errors = [];

    /** @var array<mixed> $rowHeader */
    private array $rowHeader = [];

    /** @var array<mixed> $rowContents */
    private array $rowContents = [];

    /** @var array<mixed> $var */
    private array $var;

    private ITrad $trad;

    /** @var string $status */
    private string $status = 'F';

    /** @var array<mixed> $filesOnError */
    private array $filesOnError = [];

    /**
     * @param \PDO $pdo
     * @param array<mixed> $var
     * @return void
     */
    public function __construct(\PDO $pdo, ITrad $trad, array $var)
    {
        $this->pdo = $pdo;
        $this->var = $var;
        $this->trad = $trad;
    }

    /**
     * @param string $message
     * @return void
     */
    public function setError(string $message): void
    {
        $this->valid = false;
        $this->errors[] = $message;
    }

    /**
     * @param string $file
     * @param string $codeFou
     * @return void
     */
    public function setFilesOnErrors($file, $codeFou): void
    {
        $explode = explode('\\', $file);
        $filename = $explode[count($explode)-1];

        $fourniss = new Fourniss($this->pdo);
        $rs = $fourniss->find($codeFou, ['RS'], [], $this->pdo::FETCH_ASSOC);

        $txtTraduit = $this->trad->t("Le fichier : @1@ est en erreur, il n'a pas été traité. Le fichier appartient au fournisseur suivant : @2@ (@3@)");
        $message = Trad::strBuild($txtTraduit, $filename, $rs['RS'], $codeFou);
        $this->filesOnError[] = $message;
    }

    /**
     * @return string
     */
    public function getFilesOnErrors(): string
    {
        return implode('<br/>', $this->filesOnError ?? []);
    }

    /**
     * @return string[]
     */
    public function getErrors(): array
    {
        return $this->errors;
    }

    /**
     * @return bool
     */
    public function getStatus(): bool
    {
        return ($this->status == 'O');
    }

    /**
     * @return bool
     */
    public function isValid(): bool
    {
        return $this->valid;
    }

    /**
     * @param array<mixed> $array
     * @return void
     */
    public function setGrilles(array $array): void
    {
        $this->grilles = $array;
    }

    /**
     * @return void
     */
    public function verifyEntries(): void
    {
        $txtTraduit = $this->trad->t("Le fichier @1@ n'est pas une grille excel (extensions autorisées : .xls, .xlsx, .csv)");
        foreach ($this->grilles as $grille) {
            $originalName = $grille['NOM_ORI'];
            $ext = pathinfo($originalName, PATHINFO_EXTENSION);

            if (!in_array($ext, $this->extensionAuthorize)) {
                $error = Trad::strBuild($txtTraduit, $originalName);
                $this->setError($error);
            }
        }
    }

    /**
     * @return void
     * @throws \PhpOffice\PhpSpreadsheet\Calculation\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public function treatment(): void
    {
        if ($this->isValid()) {
            foreach ($this->grilles as $grille) {
                $this->treatmentDoc($grille);
            }

            if ($this->isValid()) {
                $this->createMergedFile();
            }
        }
    }

    /**
     * @param array<mixed> $grille
     * @return void
     * @throws \PhpOffice\PhpSpreadsheet\Calculation\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    private function treatmentDoc(array $grille): void
    {
        $ext = pathinfo($grille['NOM_ORI'], PATHINFO_EXTENSION);

        if (file_exists((new ParseBase1())->getRep()->pj.$grille['NOM_FIC'])) {
            switch ($ext) {
                case "csv":
                    $this->extractFromCsv($grille);
                    break;

                case "xls":
                case "xlsx":
                    $this->extractFromExcel($grille);
                    break;
            }
        }
    }

    /**
     * @param array<mixed> $grille
     * @return void
     */
    private function extractFromCsv(array $grille): void
    {
        $pathToFile = (new ParseBase1())->getRep()->pj.$grille['NOM_FIC'];

        $headerInFile = false;
        $fp = fopen($pathToFile, 'r');
        if ($fp) {
            while (($data = fgetcsv($fp, 0, ',')) !== false) {
                if (!($headerInFile)) {
                    $headerInFile = $data;
                    $this->setHeaderConsolidation($data, [
                        'CODE_FOU' => $grille['CODE_FOU'],
                        'RS' => $grille['RS'],
                    ]);
                } else {
                    array_unshift($data, $grille['CODE_FOU'], $grille['RS']);
                    $this->rowContents[] = $data;
                }
            }
        }
    }

    /**
     * @param array<mixed> $grille
     * @throws \PhpOffice\PhpSpreadsheet\Calculation\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     * @return mixed
     */
    private function extractFromExcel(array $grille)
    {
        $pathToFile = (new ParseBase1())->getRep()->pj.$grille['NOM_FIC'];
        $input = IOFactory::identify($pathToFile);
        $objReader = IOFactory::createReader($input);
        $objReader->setReadDataOnly(true);

        $excel = $objReader->load($pathToFile);

        try {
            $sheet = $excel->getSheet(0);
            $nbRow = $sheet->getHighestRow();
        } catch (\Throwable $th) {
            $this->setFilesOnErrors($grille['NOM_FIC'], $grille['CODE_FOU']);
            return true;
        }

        $rowCount = $maxColumn = $positionColumnDate = 0;

        $data = [];
        foreach ($sheet->getRowIterator(1, $nbRow) as $row) {
            $rowContent = [];
            $iC = 0;
            foreach ($row->getCellIterator() as $column) {
                // Première ligne = en tête
                $value = $column->getCalculatedValue();

                if ($rowCount === 0) {
                    if (!$this->isNotEmpty($value)) {
                        if ($maxColumn === 0) {
                            $maxColumn = $iC;
                        }
                        continue;
                    }
                    if (strpos($value, 't| ') !== false) {
                        $value = str_replace('t| ', '', $value);
                    }

                    if ($column->getColumn() === 'C') {
                        $positionColumnDate = $iC;
                    }
                }

                if ($rowCount > 0) {
                    if ($maxColumn !== 0 && $iC >= $maxColumn) {
                        continue;
                    }

                    if ($column->getColumn() === 'A') {
                        if ($value === 0) {
                            break 2;
                        }
                    }

                    if ($iC === $positionColumnDate && ($value != '' && $value != '0')) {
                        $value = Date::transformExcelDateToPHP($value);
                    }
                }

                $rowContent[] = $value;
                $iC++;
            }

            if ($rowCount === 0) {
                $this->setHeaderConsolidation($rowContent, [
                    'CODE_FOU' => $grille['CODE_FOU'],
                    'RS' => $grille['RS'],
                ]);
            }

            if ($rowCount !== 0 && count($rowContent) > 0) {
                array_unshift($rowContent, $grille['CODE_FOU'], $grille['RS']);
                $dataLine = $rowContent;
                $data[] = $dataLine;
            }
            $rowCount++;
        }

        $this->rowContents = array_merge($this->rowContents, $data);
    }

    /**
     * @param array<string> $row
     * @param array<string> $detailFOU
     * @return void
     */
    private function setHeaderConsolidation(array $row, $detailFOU): void
    {
        array_unshift($row, 'Code fournisseur', 'RS fournisseur');
        if (!$this->rowHeader) {
            $this->rowHeader = $row;
        } else {
            if (count(array_diff($this->rowHeader, $row)) > 0) {
                $txtTraduit = $this->trad->t("Le fichier déposé par le fournisseur @1@ (@2@) n'est pas conforme, veuillez supprimer son document puis générer la grille consolidée à nouveau");
                $error = Trad::strBuild($txtTraduit, $detailFOU['RS'], $detailFOU['CODE_FOU']);
                $this->setError($error);
            }
        }
    }

    /**
     * @return void
     * @throws \Exception
     */
    private function createMergedFile(): void
    {
        $fileName = 'consolidation'.$this->var['NO_AO'];

        $generate = new Generate($fileName, 'excel', $this->rowContents, $this->rowHeader, false, false);

        $dataXml = [
            "langue" => $_SESSION['Session_Langue'],
            "new_name" => LogSysteme::getServerName().'.'.$fileName.'.xlsx',
            "file_name" => 'Grille_consolidée.xlsx',
            "typeObjet" => "AO_ENTET",
            "refObjet" => str_pad($this->var['NO_AO'], 9, '0', STR_PAD_LEFT),
            "typePj" => "PJ",
            "titreDoc" => "Grille consolidée",
            "auteur" => $this->var['AUTEUR'],
            "centre" => $this->var['CENTRE'],
            "dateParu" => (new \DateTime('now'))->format('Ymd'),
            "commentaire" => "Grille consolidée depuis les grilles des réponses",
            "PJCommande" => 0,
        ];

        $xmlResult = new DocRattaches();
        $xmlResult = $xmlResult->NouveauDocAtt($dataXml);
        $xmlResult = new \SimpleXMLElement($xmlResult->responseData);

        if ($xmlResult->Statut == 'O') {
            $this->status = 'O';
        }
    }

    /**
     * @param mixed $data
     * @return bool
     */
    private function isNotEmpty($data): bool
    {
        if ($data !== 'NULL' && !is_null($data) && $data != '0' && $data != '') {
            return true;
        }

        return false;
    }
}
