<?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\PocetKontrolPodlaDruhu;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\PocetKontrolPodlaDruhuPalivaAEmisnehoSystemuVozidla;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\PocetKontrolPodlaHodnotenia;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\PocetKontrolPodlaKategorieVozidla;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\PocetKontrolPodlaStavu;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\PocetKontrolSObratom;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\Subjekt;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\UdajePreTypZaznamuPouzivatel;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\ZoznamPoctuKontrolPodlaDruhu;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\ZoznamPoctuKontrolPodlaDruhuPalivaAEmisnehoSystemuVozidla;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\ZoznamPoctuKontrolPodlaHodnotenia;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\ZoznamPoctuKontrolPodlaKategorieVozidla;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\ZoznamPoctuKontrolPodlaStavu;
use Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\ZoznamPoctuKontrolSObratom;

/**
 * Class ProcessDataTEK
 *
 * @package Seka\Portal\Modul\Statistiky\Services\Processors
 */
final class ProcessDataTEK 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;
                        $subjekt = self::makeSubjekt($importLineDto);

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

                        unset($subjekt);

                        return $result;
                    }
                );

                // ak uz zaznam existuje, tak sa aktualizuje
                if ($zaznam instanceof ZaznamDTO) {
                    self::updateDTO($zaznam, $importLineDto);
                } else {
                    // ak zaznam este neexistuje, tak sa vytvori
                    $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
    {
        Log::debug(__CLASS__ . ' createDTO()', func_get_args());

        $kontrolaVStaveArchiv = StavEmisnejKontrolyEnum::ARCHIV
            ->equal(StavEmisnejKontrolyEnum::from(
                mb_strtoupper($importLineDto->kontrolaStav)));

        return ZaznamDTO::from([
            'subjekt' => self::makeSubjekt($importLineDto),
            'kodPracoviska' => $importLineDto->pracoviskoKod,
            'datum' => DateTime::createFromFormat('d.m.Y', $importLineDto->kontrolaDatum),
            'udaje' => new UdajePreTypZaznamuPouzivatel(
                ZoznamPoctuKontrolSObratom::createFromArray([[
                    'obrat' => $kontrolaVStaveArchiv ? $importLineDto->kontrolaCena : 0,
                    'pocet' => $kontrolaVStaveArchiv ? 1 : 0,
                ]]),
                ZoznamPoctuKontrolPodlaDruhu::createFromArray([[
                    'druh' => $importLineDto->kontrolaDruh,
                    'pocet' => 1,
                ]]),
                ZoznamPoctuKontrolPodlaStavu::createFromArray([[
                    'stav' => $importLineDto->kontrolaStav,
                    'pocet' => 1,
                ]]),
                ZoznamPoctuKontrolPodlaHodnotenia::createFromArray([[
                    'hodnotenie' => $importLineDto->kontrolaVyhodnotenie,
                    'pocet' => 1,
                ]]),
                ZoznamPoctuKontrolPodlaKategorieVozidla::createFromArray([[
                    'kategoria' => $importLineDto->vozidloKategoria,
                    'pocet' => 1,
                ]]),
                ZoznamPoctuKontrolPodlaDruhuPalivaAEmisnehoSystemuVozidla::createFromArray([[
                    'druh_paliva' => $importLineDto->vozidloDruhPaliva,
                    'emisny_system' => $importLineDto->vozidloEmisnySystem,
                    'pocet' => 1,
                ]])
            )
        ]);
    }

    /**
     * @param \Seka\Portal\Modul\Statistiky\Entities\Zaznam\DTOs\ImportLineDTO $importLineDto
     *
     * @return \Seka\Portal\Modul\Statistiky\Entities\Zaznam\ValueObjects\Subjekt
     */
    private static function makeSubjekt(ImportLineDTO $importLineDto): Subjekt
    {
        return new Subjekt(
            DruhSubjektuZaznamuEnum::POUZIVATEL,
            empty(trim($importLineDto->kontroluUzavrel)) ? 'NEDEFINOVANE' : $importLineDto->kontroluUzavrel
        );
    }

    /**
     * @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());

        $kontrolaVStaveArchiv = StavEmisnejKontrolyEnum::ARCHIV
            ->equal(StavEmisnejKontrolyEnum::from(
                mb_strtoupper($importLineDto->kontrolaStav)));

        $udaje = new UdajePreTypZaznamuPouzivatel(
            $zaznam->udaje->zoznamPoctuKontrolSObratom()->add(
                new PocetKontrolSObratom(
                    $kontrolaVStaveArchiv ? floatval($importLineDto->kontrolaCena) : 0,
                    $kontrolaVStaveArchiv ? 1 : 0
                )
            ),
            $zaznam->udaje->zoznamPoctuKontrolPodlaDruhu()->add(
                new PocetKontrolPodlaDruhu(
                    DruhEmisnejKontrolyEnum::from($importLineDto->kontrolaDruh),
                    1
                )
            ),
            $zaznam->udaje->zoznamPoctuKontrolPodlaStavu()->add(
                new PocetKontrolPodlaStavu(
                    StavEmisnejKontrolyEnum::from($importLineDto->kontrolaStav),
                    1
                )
            ),
            $zaznam->udaje->zoznamPoctuKontrolPodlaHodnotenia()->add(
                new PocetKontrolPodlaHodnotenia(
                    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
                )
            ),
        );

        $zaznam->udaje = $udaje;
    }
}
