<?php

namespace Myvetshop\Module\Clinique\Accounting\Export\Exporter\Provider\Product;

use Myvetshop\Module\Clinique\Accounting\Export\Exporter\Provider\OrderInvoiceLineProviderInterface;
use Myvetshop\Module\Clinique\Accounting\Export\Exporter\Provider\OrderSlipLineProviderInterface;
use Myvetshop\Module\Clinique\Accounting\Export\Model\SimpleAccountingLine;
use Myvetshop\Module\Clinique\Accounting\Export\Repository\CountryRepository;
use Myvetshop\Module\Clinique\Accounting\Export\Repository\InvoiceAddressRepository;
use Myvetshop\Module\Clinique\Accounting\Export\Repository\OrderDetailRepository;
use Myvetshop\Module\Clinique\Accounting\Export\Repository\OrderDetailTaxRepository;
use Myvetshop\Module\Clinique\Accounting\Export\Repository\OrderSlipDetailRepository;

class ProductProvider implements OrderInvoiceLineProviderInterface, OrderSlipLineProviderInterface
{
    /**
     * @var array<string, array<numeric-string|string, string>>
     */
    public const PRODUCT_ACCOUNT_BY_COUNTRY_AND_VAT
        = [
            'FR' => [
                '5.500' => '7071101',
                '10.000' => '7071102',
                '20.000' => '70711',
            ],
            'CH' => [
                '5.500' => '7071301',
                '10.000' => '7071302',
                '20.000' => '70713',
            ],
        ];

    protected CountryRepository $countryRepository;

    protected InvoiceAddressRepository $invoiceAddressRepository;

    protected OrderDetailRepository $orderDetailRepository;

    protected OrderDetailTaxRepository $orderDetailTaxRepository;

    protected OrderSlipDetailRepository $orderSlipDetailRepository;

    /**
     * @return array<numeric-string|string, string>
     */
    protected function getAccounts(\Order $order): array
    {
        $invoiceAddress = $this->invoiceAddressRepository->getByOrder($order);
        $country = $this->countryRepository->getById($invoiceAddress->id_country);

        $accounts = self::PRODUCT_ACCOUNT_BY_COUNTRY_AND_VAT[$country->iso_code] ?? null;
        if (!$accounts) {
            throw new \Exception('Country not supported');
        }

        return $accounts;
    }

    /**
     * @param array<numeric-string|string, float> $amountsByRate
     * @param array<numeric-string|string, string> $accounts
     *
     * @return list<SimpleAccountingLine>
     */
    protected function mapAmountsToAccounts(
        array $amountsByRate,
        array $accounts,
        bool $amountAsCredit
    ): array {
        $ret = [];

        foreach ($amountsByRate as $rate => $value) {
            $account = $accounts[$rate] ?? '70112';

            $ret[] = new SimpleAccountingLine(
                $account,
                $amountAsCredit ? \round($value, 2) : 0.00,
                $amountAsCredit ? 0.00 : \round($value, 2),
            );
        }

        return $ret;
    }

    public function __construct(
        CountryRepository $countryRepository,
        InvoiceAddressRepository $invoiceAddressRepository,
        OrderDetailRepository $orderDetailRepository,
        OrderDetailTaxRepository $orderDetailTaxRepository,
        OrderSlipDetailRepository $orderSlipDetailRepository
    ) {
        $this->countryRepository = $countryRepository;
        $this->invoiceAddressRepository = $invoiceAddressRepository;
        $this->orderDetailRepository = $orderDetailRepository;
        $this->orderDetailTaxRepository = $orderDetailTaxRepository;
        $this->orderSlipDetailRepository = $orderSlipDetailRepository;
    }

    public function getInvoiceAccountingLines(\OrderInvoice $orderInvoice, \Order $order): array
    {
        $accounts = $this->getAccounts($order);

        $ods = $this->orderDetailRepository->getByOrder($order);
        $odTaxes = $this->orderDetailTaxRepository->getByOrder($order);

        $amountsByRate = \array_reduce(
            $ods,
            function (array $carry, \OrderDetail $od) use ($odTaxes): array {
                $taxInfo = $odTaxes[(int) $od->id] ?? null;
                if (!$taxInfo) {
                    throw new \Exception('Tax info not found');
                }
                $rate = \number_format($taxInfo['rate'], 3, '.', '');

                $carry[$rate] = ($carry[$rate] ?? 0.0) + $od->total_price_tax_excl;

                return $carry;
            },
            []
        );

        return $this->mapAmountsToAccounts(
            $amountsByRate,
            $accounts,
            true
        );
    }

    public function getSlipAccountingLines(\OrderSlip $orderSlip, \Order $order): array
    {
        $accounts = $this->getAccounts($order);

        $ods = $this->orderSlipDetailRepository->getByOrderSlip($orderSlip);
        $odTaxes = $this->orderDetailTaxRepository->getByOrder($order);

        $amountsByRate = \array_reduce(
            $ods,
            function (array $carry, array $od) use ($odTaxes): array {
                $taxInfo = $odTaxes[$od['id_order_detail']] ?? null;
                if (!$taxInfo) {
                    throw new \Exception('Tax info not found');
                }
                $rate = \number_format($taxInfo['rate'], 3, '.', '');

                $carry[$rate] = ($carry[$rate] ?? 0.0) + $od['amount_tax_excl'];

                return $carry;
            },
            []
        );

        return $this->mapAmountsToAccounts(
            $amountsByRate,
            $accounts,
            false
        );
    }

    /**
     * @param \OrderInvoice|\OrderSlip $document
     */
    public function getAccountingLines($document, \Order $order): array
    {
        return $document instanceof \OrderInvoice
            ? $this->getInvoiceAccountingLines($document, $order)
            : $this->getSlipAccountingLines($document, $order);
    }
}
