<?php

namespace domain\Payment;

use common\helpers\MeanPayment;
use domain\Order\Order\Order;
use domain\Order\Order\OrderSolver;
use domain\Producer\Producer\ProducerSolver;
use domain\User\User\User;
use domain\User\User\UserRepository;
use domain\_\AbstractService;
use domain\_\ManagerInterface;
use yii\base\ErrorException;

class PaymentManager extends AbstractService implements ManagerInterface
{
    protected PaymentBuilder $paymentBuilder;
    protected OrderSolver $orderSolver;
    protected UserRepository $userRepository;
    protected ProducerSolver $producerSolver;

    public function loadDependencies(): void
    {
        $this->paymentBuilder = $this->loadService(PaymentBuilder::class);
        $this->orderSolver = $this->loadService(OrderSolver::class);
        $this->userRepository = $this->loadService(UserRepository::class);
        $this->producerSolver = $this->loadService(ProducerSolver::class);
    }

    public function creditUser(User $user, float $amount, string $meanPayment, User $userAction, string $comment = null, string $dateTransaction = null): void
    {
        $this->paymentBuilder->createPayment(
            Payment::TYPE_CREDIT,
            $amount,
            $this->getProducerContext(),
            $user,
            $userAction,
            $meanPayment,
            $comment,
            $dateTransaction
        );
    }

    public function debitUser(User $user, float $amount, string $meanPayment, User $userAction, string $comment = null, string $dateTransaction = null): void
    {
        $this->paymentBuilder->createPayment(
            Payment::TYPE_DEBIT,
            $amount,
            $this->getProducerContext(),
            $user,
            $userAction,
            $meanPayment,
            $comment,
            $dateTransaction
        );
    }

    public function creditOrDebitUser(string $type, User $user, float $amount, string $meanPayment, User $userAction, string $comment, string $dateTransaction = null): void
    {
        if($type == Payment::TYPE_CREDIT) {
            $this->creditUser($user, $amount, $meanPayment, $userAction, $comment, $dateTransaction);
        }
        elseif($type == Payment::TYPE_DEBIT) {
            $this->debitUser($user, $amount, $meanPayment, $userAction, $comment, $dateTransaction);
        }
        else {
            throw new ErrorException('$type a une valeur incorrect');
        }
    }

    public function payOrder(Order $order, string $meanPayment, User $userAction, bool $checkCreditLimit)
    {
        if($meanPayment == MeanPayment::CREDIT) {
            $this->payOrderByCredit($order, $userAction, $checkCreditLimit);
        }
        elseif(key_exists($meanPayment, MeanPayment::getAll())) {
            $this->paymentBuilder->createPayment(
                Payment::TYPE_PAYMENT,
                $this->orderSolver->getOrderAmountWithTax($order, Order::AMOUNT_REMAINING),
                $this->getProducerContext(),
                $order->user,
                $userAction,
                $meanPayment,
                null,
                null,
                $order
            );
        }
        else {
            throw new ErrorException('Moyen de paiement inconnu : '.$meanPayment);
        }
    }

    public function payOrderByCredit(Order $order, User $userAction, bool $checkCreditLimit): void
    {
        $amountRemaining = round($this->orderSolver->getOrderAmountWithTax($order, Order::AMOUNT_REMAINING), 2);

        if($checkCreditLimit) {
            $creditLimit = $this->producerSolver->getConfig('credit_limit');
            $creditUser = $this->userRepository->getCredit($order->user);

            if (!is_null($creditLimit) && $amountRemaining > $creditUser - $creditLimit) {
                $amountRemaining = $creditUser - $creditLimit;
            }
        }

        if($amountRemaining > 0) {
            $this->paymentBuilder->createPayment(
                Payment::TYPE_PAYMENT,
                $amountRemaining,
                $this->getProducerContext(),
                $order->user,
                $userAction,
                MeanPayment::CREDIT,
                null,
                null,
                $order
            );
        }
    }

    public function refundOrder(Order $order, string $meanPayment, User $userAction): void
    {
        $amountPaid = round($this->orderSolver->getOrderAmount($order, Order::AMOUNT_PAID), 2);

        if ($amountPaid >= 0.01 && $order->id_user) {

            $amount = $this->orderSolver->getOrderAmount($order, Order::AMOUNT_PAID);
            if($meanPayment == MeanPayment::CREDIT) {
                $amount = $this->orderSolver->getOrderAmountPaidByCredit($order);
            }

            $this->paymentBuilder->createPayment(
                Payment::TYPE_REFUND,
                $amount,
                $this->getProducerContext(),
                $order->user,
                $userAction,
                $meanPayment,
                null,
                null,
                $order
            );
        }
    }

    public function refundOrderCredit(Order $order, User $userAction): void
    {
        $amountPaidByCredit = $this->orderSolver->getOrderAmountPaidByCredit($order);
        if($amountPaidByCredit >= 0.01 && $order->id_user) {
            $this->paymentBuilder->createPayment(
                Payment::TYPE_REFUND,
                $amountPaidByCredit,
                $this->getProducerContext(),
                $order->user,
                $userAction,
                MeanPayment::CREDIT,
                null,
                null,
                $order
            );
        }
    }

    public function refundSurplusOrderCredit(Order $order, User $userAction): void
    {
        $amountPaidByCredit = $this->orderSolver->getOrderAmountPaidByCredit($order);
        $amount = $this->orderSolver->getOrderAmountWithTax($order);

        if($amountPaidByCredit > $amount) {
            $this->paymentBuilder->createPayment(
                Payment::TYPE_REFUND,
                $amountPaidByCredit - $amount,
                $this->getProducerContext(),
                $order->user,
                $userAction,
                MeanPayment::CREDIT,
                null,
                null,
                $order
            );
        }
    }

    public function payOrRefundOrder(string $type, Order $order, string $meanPayment, User $userAction, bool $checkCreditLimit = false): void
    {
        if($type == Payment::TYPE_PAYMENT) {
            $this->payOrder($order, $meanPayment, $userAction, $checkCreditLimit);
        }
        elseif($type == Payment::TYPE_REFUND) {
            $this->refundOrder($order, $meanPayment, $userAction);
        }
        else {
            throw new ErrorException('$type a une valeur incorrect');
        }
    }
}