<?php

declare(strict_types=1);

namespace Seka\Portal\Modul\Statistiky\Services\Processors;

use DateTime;
use Illuminate\Support\Facades\Log;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\DTOs\ImportLineDTO;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\DTOs\ZaznamDTO;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\Enums\DruhEmisnejKontrolyEnum;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\Enums\DruhPalivaEnum;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\Enums\DruhSubjektuZaznamuEnum;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\Enums\EmisnySystemEnum;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\Enums\HodnotenieEmisnejKontrolyEnum;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\Enums\StavEmisnejKontrolyEnum;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\PocetKontrolPodlaDruhuAHodnotenia;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\PocetKontrolPodlaDruhuPalivaAEmisnehoSystemuVozidla;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\PocetKontrolPodlaKategorieVozidla;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\PocetKontrolPodlaObratuASubjektuFakturacie;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\PocetKontrolPodlaStavu;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\Subjekt;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\UdajePreTypZaznamuPracovisko;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\ZoznamPoctuKontrolAObratuPodlaSubjektuFakturacie;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\ZoznamPoctuKontrolPodlaDruhuAHodnotenia;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\ZoznamPoctuKontrolPodlaDruhuPalivaAEmisnehoSystemuVozidla;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\ZoznamPoctuKontrolPodlaKategorieVozidla;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\ZoznamPoctuKontrolPodlaStavu;

/**
 * Class ProcessDataPEK
 *
 * @package Seka\Portal\Modul\Statistiky\Services\Processors
 */
final class ProcessDataPEK implements ProcessContract
{
    /**
     * @param \Seka\Portal\Modul\Statistiky\Entities\Zaznam\DTOs\ImportLineDTO[] $data
     *
     * @return \Seka\Portal\Modul\Statistiky\Entities\Zaznam\DTOs\ZaznamDTO[]
     */
    public static function process(array $data): array
    {
        $out = [];

        foreach ($data as $importLineDto) {
            $zaznam = null;

            if (empty($out)) {
                $out[] = self::createDTO($importLineDto);
            } else {
                $zaznam = array_reduce(
                    $out,
                    function (?ZaznamDTO &$default, ZaznamDTO &$item) use ($importLineDto): ?ZaznamDTO {
                        $result = &$default;

                        if (
                            $item->kodPracoviska === $importLineDto->pracoviskoKod
                            && $item->datum->diff(DateTime::createFromFormat('d.m.Y', $importLineDto->kontrolaDatum))->days === 0
                            && $item->subjekt->druh()->equals(DruhSubjektuZaznamuEnum::PRACOVISKO())
                        ) {
                            $result = &$item;
                        }

                        return $result;
                    }
                );

                if ($zaznam instanceof ZaznamDTO) {
                    self::updateDTO($zaznam, $importLineDto);
                } else {
                    $zaznam = self::createDTO($importLineDto);
                    $out[] = $zaznam;
                }
            }
        }

        return $out;
    }

    /**
     * @param \Seka\Portal\Modul\Statistiky\Entities\Zaznam\DTOs\ImportLineDTO $importLineDto
     *
     * @return \Seka\Portal\Modul\Statistiky\Entities\Zaznam\DTOs\ZaznamDTO
     */
    private static function createDTO(ImportLineDTO $importLineDto): ZaznamDTO
    {
        return ZaznamDTO::create(
            new Subjekt(DruhSubjektuZaznamuEnum::PRACOVISKO(), $importLineDto->pracoviskoNazov),
            $importLineDto->pracoviskoKod,
            DateTime::createFromFormat('d.m.Y', $importLineDto->kontrolaDatum),
            new UdajePreTypZaznamuPracovisko(
                ZoznamPoctuKontrolPodlaDruhuAHodnotenia::createFromArray([[
                    'druh' => $importLineDto->kontrolaDruh,
                    'hodnotenie' => $importLineDto->kontrolaVyhodnotenie,
                    'pocet' => 1,
                ]]),
                ZoznamPoctuKontrolPodlaKategorieVozidla::createFromArray([[
                    'kategoria' => $importLineDto->vozidloKategoria,
                    'pocet' => 1,
                ]]),
                ZoznamPoctuKontrolPodlaDruhuPalivaAEmisnehoSystemuVozidla::createFromArray([[
                    'druh_paliva' => $importLineDto->vozidloDruhPaliva,
                    'emisny_system' => $importLineDto->vozidloEmisnySystem,
                    'pocet' => 1,
                ]]),
                ($importLineDto->fakturacia == 'TRUE') ? ZoznamPoctuKontrolAObratuPodlaSubjektuFakturacie::createFromArray([[
                    'subjekt' => $importLineDto->kontrolaOdberatel,
                    'obrat' => floatval($importLineDto->kontrolaCena),
                    'pocet' => 1,
                ]]) : null,
                ZoznamPoctuKontrolPodlaStavu::createFromArray([[
                    'stav' => $importLineDto->kontrolaStav,
                    'pocet' => 1,
                ]])
            )
        );
    }

    /**
     * @param \Seka\Portal\Modul\Statistiky\Entities\Zaznam\DTOs\ZaznamDTO     $zaznam
     * @param \Seka\Portal\Modul\Statistiky\Entities\Zaznam\DTOs\ImportLineDTO $importLineDto
     */
    private static function updateDTO(ZaznamDTO &$zaznam, ImportLineDTO $importLineDto): void
    {
        Log::debug(__CLASS__ . ' updateDTO()', func_get_args());

        $zoznamPoctuKontrolAObratuPodlaSubjektuFakturacie = null;

        if ($importLineDto->fakturacia == 'TRUE') {
            $pocet = new PocetKontrolPodlaObratuASubjektuFakturacie(
                $importLineDto->kontrolaOdberatel,
                floatval($importLineDto->kontrolaCena),
                1
            );

            if (is_null($zaznam->udaje->zoznamPoctuKontrolAObratuPodlaSubjektuFakturacie())) {
                $zoznamPoctuKontrolAObratuPodlaSubjektuFakturacie = new ZoznamPoctuKontrolAObratuPodlaSubjektuFakturacie([$pocet]);
            } else {
                $zoznamPoctuKontrolAObratuPodlaSubjektuFakturacie = $zaznam->udaje->zoznamPoctuKontrolAObratuPodlaSubjektuFakturacie()->add($pocet);
            }
        }

        $udaje = new UdajePreTypZaznamuPracovisko(
            $zaznam->udaje->zoznamPoctuKontrolPodlaDruhuAHodnotenia()->add(
                new PocetKontrolPodlaDruhuAHodnotenia(
                    DruhEmisnejKontrolyEnum::from($importLineDto->kontrolaDruh),
                    HodnotenieEmisnejKontrolyEnum::from($importLineDto->kontrolaVyhodnotenie),
                    1
                )
            ),
            $zaznam->udaje->zoznamPoctuKontrolPodlaKategorieVozidla()->add(
                new PocetKontrolPodlaKategorieVozidla($importLineDto->vozidloKategoria, 1)
            ),
            $zaznam->udaje->zoznamPoctuKontrolPodlaDruhuPalivaAEmisnehoSystemuVozidla()->add(
                new PocetKontrolPodlaDruhuPalivaAEmisnehoSystemuVozidla(
                    DruhPalivaEnum::from($importLineDto->vozidloDruhPaliva),
                    empty($importLineDto->vozidloEmisnySystem) ? EmisnySystemEnum::NULL() : EmisnySystemEnum::from(mb_strtoupper($importLineDto->vozidloEmisnySystem)),
                    1
                )
            ),
            $zoznamPoctuKontrolAObratuPodlaSubjektuFakturacie,
            $zaznam->udaje->zoznamPoctuKontrolPodlaStavu()->add(
                new PocetKontrolPodlaStavu(StavEmisnejKontrolyEnum::from($importLineDto->kontrolaStav), 1)
            )
        );

        $zaznam->udaje = $udaje;
    }
}
