<?php

namespace App\Http\Controllers\Parametres\Imports;

use App\Helpers\Normalizer\DateNormalizer;
use App\Helpers\Normalizer\PriceNormalizer;
use CARTOGRAPHIE;
use Exception;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;

class ImportCSVClient
{
    public const STEP = 100;

    public function import($file, $offset, $step, $headersAptimap, $structure_id)
    {
        $start = $offset;
        $end = $offset + $step;

        $readingFile = fopen($file, 'r');
        $lines = [];
        for ($i = 0; $i < $start; $i++) {
            fgets($readingFile);
        }
        for ($start; $start < $end; $start++) {
            $lines[] = fgetcsv($readingFile, null, ';');
        }
        foreach ($lines as $line) {
            $this->processCSVData($line, $headersAptimap, $structure_id);

        }

        fclose($readingFile);
    }

    private function processCSVData($row, $headersAptimap, $structure_id): void
    {
        if (is_array($row)) {
            try {
                $MD5 = DB::table(CARTOGRAPHIE::CSV_DONNEES_CLIENT)->select('id')->where(CARTOGRAPHIE::CHECKSUM, md5(implode(";", $row)))->get();
                $line = $this->getLineData($row, $headersAptimap, $structure_id, implode(';', $row));
                if ($MD5->isEmpty()) {
                    try {
                        DB::table(CARTOGRAPHIE::CSV_DONNEES_CLIENT)->insert($line);
                    } catch (\Exception $e) {
                        var_dump($row, $e->getMessage());
                    }
                } else {
                    try {
                        DB::table(CARTOGRAPHIE::CSV_DONNEES_CLIENT)
                            ->where('id', $MD5[0]->id)
                            ->update($line);
                    } catch (\Exception $e) {
                        var_dump($row, $e->getMessage());
                    }
                }
            } catch (\Exception $e) {
                var_dump($e->getMessage());
            }
        }
    }

    private function getLineData($row, $headersAptimap, $structure_id, $rowString): array
    {
        $line = [];
        foreach ($row as $index => $value) {
            if ($headersAptimap[$index] != null) {
                switch ($headersAptimap[$index]) {
                    case CARTOGRAPHIE::MONTANT_HT:
                    case CARTOGRAPHIE::MONTANT_TVA:
                    case CARTOGRAPHIE::MONTANT_TTC :
                        $line[$headersAptimap[$index]] = PriceNormalizer::normalizePrice($value);
                        break;
                    case CARTOGRAPHIE::DATE:
                    case CARTOGRAPHIE::DATE_FIN:
                    case CARTOGRAPHIE::DATE_DEBUT:
                        $line[$headersAptimap[$index]] = DateNormalizer::normalizeDate($value);
                        break;
                    default:
                        $line[$headersAptimap[$index]] = trim($value);
                }
            }
        }
        $line[CARTOGRAPHIE::STRUCTURE_ID] = $structure_id;
        $line[CARTOGRAPHIE::CHECKSUM] = md5($rowString);
        return $line;
    }

    public function ensureFileInRequest($request, $fileName)
    {
        if (!$request->hasFile($fileName)) {
            throw new Exception('Aucun fichier n\'a été fourni.');
        }
    }

    public function validateFile($file)
    {
        if (!$file->isValid() || !file_exists($file) || !is_readable($file)) {
            throw new Exception('Le fichier n\'est pas valide.');
        }
    }

    public function checkIfMatchingExists()
    {
        $matchingHeaders = $this->getMatchingHeaders(Auth::user()->structure_id);

        if (empty(array_filter($matchingHeaders, function ($a) {
            return $a !== null;
        }))) {
            throw new Exception('Aucun matching n\'a été réalisé. Merci de contacter l\'administrateur.');
        }

        return $matchingHeaders;
    }

    public function checkIsAtLeastOneMatchingHeader($file)
    {
        $isAtLeastOneMatchingHeader = $this->isAtLeastOneMatchingHeader($file, Auth::user()->structure_id);

        if (!$isAtLeastOneMatchingHeader) {
            throw new Exception("Le fichier envoyé ne possède aucun entête en commun avec celui enregistré par l'administrateur. Merci de réessayer avec un autre fichier.");
        }
    }

    public function checkAllHeadersAreMatched($file)
    {
        $isAllHeadersMatched = $this->isAllHeadersMatched($file, Auth::user()->structure_id);

        if (!$isAllHeadersMatched) {
            return true;
        }
    }

    public function importData($file)
    {
        $step = $this::STEP;
        $csvlinecount = $this->countCSVline($file);
        $headersAptimap = $this->getHeaders($file, Auth::user()->structure_id);

        if (!is_array($headersAptimap)) {
            throw new Exception('Erreur : Les en-têtes du fichier ne sont pas un tableau.');
        }

        for ($offset = 1; $offset < $csvlinecount; $offset += $step) {
            if ($offset + $step >= $csvlinecount) {
                $this->import($file, $offset, $csvlinecount - $offset, $headersAptimap, Auth::user()->structure_id);
            } else {
                $this->import($file, $offset, $step, $headersAptimap, Auth::user()->structure_id);
            }
        }
    }

    public function getToCompareHeader($file, $structure_id): array
    {
        $handle = fopen($file, "r");
        $contentCsv = fgetcsv($handle);
        fclose($handle);
        $csvHeaders = explode(';', $contentCsv[0]);
        $getClientHeader = $this->getClientHeader($structure_id);
        return array($csvHeaders, $getClientHeader);
    }

    private function isAtLeastOneMatchingHeader($file, $structure_id): bool
    {
        list($csvHeaders, $getClientHeader) = $this->getToCompareHeader($file, $structure_id);
        $isAtLeastOneMatching = false;

        foreach ($csvHeaders as $key => $value) {
            foreach ($getClientHeader as $client) {
                if ($value == $client) {
                    $isAtLeastOneMatching = true;
                    break;
                }
            }
        }
        return $isAtLeastOneMatching;
    }

    private function isAllHeadersMatched($file, $structure_id): bool
    {
        list($csvHeaders, $getClientHeader) = $this->getToCompareHeader($file, $structure_id);
        if ($csvHeaders === $getClientHeader) {
            return true;
        }
        return false;
    }

    public function countCSVline($file)
    {
        $readingFile = fopen($file, 'r');
        if ($readingFile) {
            return $this->getCSVLineCount($readingFile);
        }
        return fclose($readingFile);
    }

    public function getMatchingHeaders($structure_id): array
    {
        return DB::table(CARTOGRAPHIE::CSV_CLIENT_ENTETES)
            ->select(CARTOGRAPHIE::CSV_APTIMAP_ENTETES)
            ->where(CARTOGRAPHIE::STRUCTURE_ID, $structure_id)
            ->get()
            ->map(function ($item) {
                return $item->csv_aptimap_entetes;
            })
            ->toArray();
    }

    public function getClientHeader($structure_id): array
    {
        return DB::table(CARTOGRAPHIE::CSV_CLIENT_ENTETES)
            ->select(CARTOGRAPHIE::NOM)
            ->where(CARTOGRAPHIE::STRUCTURE_ID, $structure_id)
            ->get()
            ->map(function ($item) {
                return $item->nom;
            })
            ->toArray();
    }

    private function getCSVLineCount($readingFile): int
    {
        $csvlinecount = 0;
        while (!feof($readingFile)) {
            fgets($readingFile);
            $csvlinecount++;
        }
        return $csvlinecount;
    }

    public function getHeaders($file, $structure_id)
    {
        $readingFile = fopen($file, 'r');
        $headersAptimap = fgetcsv($readingFile, 0, ';');
        return $this->matching($headersAptimap, $structure_id);
    }

    public function matching($headersAptimap, $structure_id)
    {
        foreach ($headersAptimap as $key => $value) {
            $headersAptimap[$key] = DB::table(CARTOGRAPHIE::CSV_CLIENT_ENTETES)
                ->where(CARTOGRAPHIE::STRUCTURE_ID, $structure_id)
                ->where(CARTOGRAPHIE::NOM, $value)
                ->value(CARTOGRAPHIE::CSV_APTIMAP_ENTETES);
        }
        return $headersAptimap;
    }
}
