<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Export\Offer\Excel;

use Contao\CoreBundle\Filesystem\VirtualFilesystemInterface;
use Contao\StringUtil;
use Ferienpass\CoreBundle\Export\FormulaEscaper;
use Ferienpass\CoreBundle\Export\Offer\OffersExportInterface;
use PhpOffice\PhpSpreadsheet\Cell\CellAddress;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use Symfony\Component\Filesystem\Filesystem;

abstract class AbstractExcelExport implements ExcelExportInterface, OffersExportInterface
{
    public function __construct(private readonly VirtualFilesystemInterface $exportStorage)
    {
    }

    public function generate(iterable $offers, ?string $destination = null, bool $stream = false): mixed
    {
        $tmpPath = sys_get_temp_dir();
        $destination ??= \sprintf('xlsx-%s.xlsx', uniqid(more_entropy: true));
        $path = 'xlsx/'.$destination;

        $filesystem = new Filesystem();
        $filesystem->mkdir($tmpPath);

        $writer = new XlsxWriter($this->createSpreadsheet($offers));
        $writer->save($tmpPath.'/'.$destination);

        if ($stream) {
            return fopen($tmpPath.'/'.$destination, 'r');
        }

        $this->exportStorage->write($path, file_get_contents($tmpPath.'/'.$destination));

        return $path;
    }

    protected function createSpreadsheet(iterable $offers): Spreadsheet
    {
        $spreadsheet = new Spreadsheet();

        $spreadsheet->getDefaultStyle()->getFont()->setName('Arial');
        $sheet = $spreadsheet->getActiveSheet();

        $cols = $this->columns();
        $cols = $cols instanceof \Traversable ? iterator_to_array($cols, true) : $cols;

        // Header row
        $iCol = 1;
        foreach (array_keys($cols) as $colName) {
            $sheet->getStyle(CellAddress::fromColumnAndRow($iCol, 1))->getFont()->setBold(true);
            $sheet->setCellValue(CellAddress::fromColumnAndRow($iCol, 1), $colName);

            ++$iCol;
        }

        // Content rows
        $iRow = 2;
        foreach ($offers as $offer) {
            $iCol = 1;

            foreach ($cols as $col) {
                $cell = $sheet->getCell(CellAddress::fromColumnAndRow($iCol, $iRow));

                $style = $sheet->getStyle(CellAddress::fromColumnAndRow($iCol, $iRow));
                $dto = new CellDto($offer, $cell, $style);

                // If the callback has a return value (e.g. for arrow functions), set the value
                if ($val = $col($dto)) {
                    $cell->setValue(FormulaEscaper::escape(StringUtil::decodeEntities($val)));
                }

                ++$iCol;
            }

            ++$iRow;
        }

        return $spreadsheet;
    }
}
