<?php

declare(strict_types=1);

namespace App\Administration\Application\Service\Import;

use App\Administration\Domain\Exception\FichierImportInvalideException;

use function count;
use function fclose;
use function fgetcsv;
use function fopen;
use function mb_convert_encoding;
use function mb_detect_encoding;

/**
 * Service de parsing de fichiers CSV avec détection d'encoding UTF-8.
 *
 * Supporte les séparateurs courants (virgule, point-virgule, tabulation).
 */
final readonly class CsvParser
{
    private const int MAX_LINE_LENGTH = 0;
    private const array SEPARATORS = [';', ',', "\t"];

    public function parse(string $filePath): FileParseResult
    {
        $handle = fopen($filePath, 'r');
        if ($handle === false) {
            throw FichierImportInvalideException::fichierIllisible($filePath);
        }

        try {
            $content = file_get_contents($filePath);
            if ($content === false) {
                throw FichierImportInvalideException::fichierIllisible($filePath);
            }

            $content = $this->convertToUtf8($content);
            $content = $this->stripBom($content);

            $separator = $this->detectSeparator($content);

            $lines = $this->parseContent($content, $separator);

            if ($lines === []) {
                throw FichierImportInvalideException::fichierVide();
            }

            $columns = array_shift($lines);

            $rows = [];
            foreach ($lines as $line) {
                if ($this->isEmptyLine($line)) {
                    continue;
                }

                $row = [];
                foreach ($columns as $index => $column) {
                    $row[$column] = $line[$index] ?? '';
                }
                $rows[] = $row;
            }

            return new FileParseResult($columns, $rows);
        } finally {
            fclose($handle);
        }
    }

    private function convertToUtf8(string $content): string
    {
        $encoding = mb_detect_encoding($content, ['UTF-8', 'ISO-8859-1', 'Windows-1252'], true);

        if ($encoding !== false && $encoding !== 'UTF-8') {
            $converted = mb_convert_encoding($content, 'UTF-8', $encoding);

            return $converted !== false ? $converted : $content;
        }

        return $content;
    }

    private function stripBom(string $content): string
    {
        if (str_starts_with($content, "\xEF\xBB\xBF")) {
            return substr($content, 3);
        }

        return $content;
    }

    private function detectSeparator(string $content): string
    {
        $firstLine = strtok($content, "\n");
        if ($firstLine === false) {
            return ';';
        }

        $maxCount = 0;
        $bestSeparator = ';';

        foreach (self::SEPARATORS as $separator) {
            $count = substr_count($firstLine, $separator);
            if ($count > $maxCount) {
                $maxCount = $count;
                $bestSeparator = $separator;
            }
        }

        return $bestSeparator;
    }

    /**
     * @return list<list<string>>
     */
    private function parseContent(string $content, string $separator): array
    {
        $stream = fopen('php://temp', 'r+');
        if ($stream === false) {
            return [];
        }

        fwrite($stream, $content);
        rewind($stream);

        $lines = [];
        while (($line = fgetcsv($stream, self::MAX_LINE_LENGTH, $separator, '"', '')) !== false) {
            /** @var list<string> $sanitized */
            $sanitized = array_map('strval', $line);
            $lines[] = $sanitized;
        }

        fclose($stream);

        return $lines;
    }

    /**
     * @param list<string> $line
     */
    private function isEmptyLine(array $line): bool
    {
        return count($line) === 1 && trim((string) $line[0]) === '';
    }
}
