<?php
declare(strict_types=1);

namespace Autogedal\EanTools\Model\Spreadsheet;

use Magento\Framework\Exception\FileSystemException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Filesystem\Driver\File;
use Throwable;

class CsvRowWriter implements RowWriterInterface
{
    private const BOM = "\xEF\xBB\xBF";
    private const DELIMITER = ';';
    private const ENCLOSURE = '"';
    private const LINE_ENDING = "\r\n";

    private mixed $handle = null;

    public function __construct(
        private readonly File $file,
        string $absolutePath,
        array $header
    ) {
        try {
            $this->handle = $this->file->fileOpen($absolutePath, 'w');
            $this->file->fileWrite($this->handle, self::BOM);
            $this->writeCsvRow($header);
        } catch (FileSystemException $e) {
            $this->closeSilently();
            $fileName = strrchr(str_replace('\\', '/', $absolutePath), '/');
            $fileName = $fileName === false ? $absolutePath : substr($fileName, 1);

            throw new LocalizedException(
                __('The output file "%1" could not be created.', $fileName),
                $e
            );
        }
    }

    public function __destruct()
    {
        $this->closeSilently();
    }

    public function writeRow(array $row, array $textColumns = []): void
    {
        $this->writeCsvRow($row);
    }

    public function close(): void
    {
        if (!is_resource($this->handle)) {
            return;
        }

        $handle = $this->handle;
        $this->handle = null;
        $this->file->fileClose($handle);
    }

    private function closeSilently(): void
    {
        try {
            $this->close();
        } catch (Throwable) {
            return;
        }
    }

    private function writeCsvRow(array $row): void
    {
        $values = array_map(function (mixed $value): string {
            $enclose = !is_int($value) && !is_float($value);
            $value = (string)$value;

            if (isset($value[0]) && in_array($value[0], ['=', '+', '-'], true)) {
                $value = ' ' . $value;
            }

            $value = str_replace(self::ENCLOSURE, self::ENCLOSURE . self::ENCLOSURE, $value);

            return $enclose ? self::ENCLOSURE . $value . self::ENCLOSURE : $value;
        }, $row);

        $this->file->fileWrite(
            $this->handle,
            implode(self::DELIMITER, $values) . self::LINE_ENDING
        );
    }
}
