<?php

namespace Myvetshop\Module\Clinique\Import\ImportFile;

use Myvetshop\Module\Clinique\Import\Exception\MissingCustomerDataException;
use Myvetshop\Module\Clinique\Import\Model\AddressModel;
use Myvetshop\Module\Clinique\Import\Model\CartProductModel;
use Myvetshop\Module\Clinique\Import\Model\CartRuleModel;
use Myvetshop\Module\Clinique\Import\Model\CustomerModel;
use Myvetshop\Module\Clinique\Import\Model\OrderDetailModel;
use Myvetshop\Module\Clinique\Import\Model\OrderDetailTaxModel;
use Myvetshop\Module\Clinique\Import\Model\OrderModel;
use Myvetshop\Module\Clinique\Import\Model\RewardModel;
use Symfony\Component\Serializer\Serializer;

class DispatchedCustomerData
{
    private string $dispatchFolder;

    private string $dispatchedFolder;

    /**
     * @var list<AddressModel>|null
     */
    private ?array $addresses = null;

    /**
     * @var list<CartProductModel>|null
     */
    private ?array $cartProducts = null;

    private ?CustomerModel $customer = null;

    /**
     * @var list<OrderDetailModel>|null
     */
    private ?array $orderDetails = null;

    /**
     * @var list<OrderDetailTaxModel>|null
     */
    private ?array $orderDetailTaxes = null;

    /**
     * @var list<OrderModel>|null
     */
    private ?array $orders = null;

    private ?RewardModel $reward = null;

    private Serializer $serializer;

    /**
     * @var CartRuleModel[]|null
     */
    private ?array $cartRules = null;

    /**
     * @template T
     *
     * @param class-string<T> $className
     *
     * @return T
     */
    public function denormalize(string $filename, string $className)
    {
        /** @var T $ret */
        $ret = $this->serializer->denormalize(
            \json_decode(
                \file_get_contents($filename) ?: '',
                true
            ),
            $className
        );

        return $ret;
    }

    /**
     * @param string $subfolder
     *
     * @return string[]
     */
    protected function scanSubfolder(string $subfolder): array
    {
        $fullFolder = $this->dispatchFolder
            . $this->dispatchedFolder . DIRECTORY_SEPARATOR
            . $subfolder . DIRECTORY_SEPARATOR;

        if (!\is_dir($fullFolder)) {
            return [];
        }

        return
            \array_values(
                \array_filter(
                    \array_map(
                        function (string $file) use ($fullFolder) {
                            return $fullFolder . $file;
                        },
                        \scandir($fullFolder) ?: [],
                    ),
                    function (string $file) {
                        return \is_file($file);
                    }
                )
            );
    }

    /**
     * @template T
     *
     * @param class-string<T> $className
     * @param list<string> $files
     *
     * @return list<T>
     */
    protected function loadFiles(string $className, array $files): array
    {
        return \array_map(
            fn ($filename) => $this->denormalize($filename, $className),
            $files
        );
    }

    public function __construct(
        Serializer $serializer,
        string $dispatchFolder,
        string $dispatchedFolder
    ) {
        $this->serializer = $serializer;
        $this->dispatchFolder = $dispatchFolder;
        $this->dispatchedFolder = $dispatchedFolder;
    }

    /**
     * @return list<AddressModel>
     */
    public function getAdresses(): array
    {
        if (null === $this->addresses) {
            $this->addresses = $this->loadFiles(
                AddressModel::class,
                $this->scanSubfolder('ADDRESSES'),
            );
        }

        return $this->addresses;
    }

    /**
     * @return list<CartProductModel>
     */
    public function getCartProducts(): array
    {
        if (null === $this->cartProducts) {
            $this->cartProducts = $this->loadFiles(
                CartProductModel::class,
                $this->scanSubfolder('CART_PRODUCTS'),
            );
        }

        return $this->cartProducts;
    }

    /**
     * @param class-string<CustomerModel> $className
     *
     * @return CustomerModel
     *
     * @throws \Exception
     */
    public function getCustomer(string $className): CustomerModel
    {
        if (null === $this->customer) {
            $customers = $this->loadFiles(
                $className,
                $this->scanSubfolder('CUSTOMERS')
            );

            if (\count($customers) !== 1) {
                throw new MissingCustomerDataException(sprintf('Invalid CUSTOMERS data for %s (%s fichiers)', $this->dispatchedFolder, \count($customers)));
            }

            $this->customer = $customers[0];
        }

        return $this->customer;
    }

    /**
     * @return list<OrderDetailModel>
     */
    public function getOrderDetails(): array
    {
        if (null === $this->orderDetails) {
            $this->orderDetails = $this->loadFiles(
                OrderDetailModel::class,
                $this->scanSubfolder('ORDER_DETAILS'),
            );
        }

        return $this->orderDetails;
    }

    /**
     * @return list<OrderDetailTaxModel>
     */
    public function getOrderDetailTaxes(): array
    {
        if (null === $this->orderDetailTaxes) {
            $this->orderDetailTaxes = $this->loadFiles(
                OrderDetailTaxModel::class,
                $this->scanSubfolder('ORDER_DETAIL_TAXES'),
            );
        }

        return $this->orderDetailTaxes;
    }

    /**
     * @return list<OrderModel>
     */
    public function getOrders(): array
    {
        if (null === $this->orders) {
            $this->orders = $this->loadFiles(
                OrderModel::class,
                $this->scanSubfolder('ORDERS'),
            );
        }

        return $this->orders;
    }

    /**
     * @return CartRuleModel[]
     */
    public function getCartRules(): array
    {
        if (null === $this->cartRules) {
            $this->cartRules = $this->loadFiles(
                CartRuleModel::class,
                $this->scanSubfolder('CART_RULES'),
            );
        }

        return $this->cartRules;
    }

    /**
     * @param class-string<RewardModel> $className
     *
     * @return RewardModel
     *
     * @throws \Exception
     */
    public function getReward(string $className): ?RewardModel
    {
        if (null === $this->reward) {
            $rewards = $this->loadFiles(
                $className,
                $this->scanSubfolder('REWARDS'),
            );

            $this->reward = $rewards[0] ?? null;
        }

        return $this->reward;
    }
}
