<?php

namespace common\logic\User\UserProducer\Service;

use common\helpers\MeanPayment;
use common\logic\AbstractBuilder;
use common\logic\Order\Order\Model\Order;
use common\logic\Order\Order\Repository\OrderRepository;
use common\logic\Order\Order\Service\OrderSolver;
use common\logic\Payment\Model\Payment;
use common\logic\Payment\Service\PaymentSolver;
use common\logic\Producer\Producer\Model\Producer;
use common\logic\Producer\Producer\Repository\ProducerRepository;
use common\logic\Producer\Producer\Service\ProducerSolver;
use common\logic\User\User\Model\User;
use common\logic\User\User\Repository\UserRepository;
use common\logic\User\UserProducer\Model\UserProducer;
use common\logic\User\UserProducer\Repository\UserProducerRepository;

class UserProducerBuilder extends AbstractBuilder
{
    protected PaymentSolver $paymentSolver;
    protected UserProducerRepository $userProducerRepository;
    protected OrderRepository $orderRepository;
    protected ProducerRepository $producerRepository;
    protected OrderSolver $orderSolver;
    protected UserRepository $userRepository;
    protected ProducerSolver $producerSolver;

    public function loadDependencies(): void
    {
        $this->paymentSolver = $this->loadService(PaymentSolver::class);
        $this->userProducerRepository = $this->loadService(UserProducerRepository::class);
        $this->orderRepository = $this->loadService(OrderRepository::class);
        $this->producerRepository = $this->loadService(ProducerRepository::class);
        $this->orderSolver = $this->loadService(OrderSolver::class);
        $this->userRepository = $this->loadService(UserRepository::class);
        $this->producerSolver = $this->loadService(ProducerSolver::class);
    }

    public function instanciateUserProducer(User $user, Producer $producer, bool $bookmark = true, bool $newsletter = true)
    {
        $userProducer = new UserProducer();

        $userProducer->populateUser($user);
        $userProducer->populateproducer($producer);
        $userProducer->setCredit(0);
        $userProducer->setActive(true);
        $userProducer->setBookmark($bookmark);
        $userProducer->setNewsletter($newsletter);

        return $userProducer;
    }

    public function createUserProducer(User $user, Producer $producer, bool $bookmark = true, bool $newsletter = true): UserProducer
    {
        $userProducer = $this->instanciateUserProducer($user, $producer, $bookmark, $newsletter);
        $this->create($userProducer);
        return $userProducer;
    }

    public function createUserProducerIfNotExist(User $user, Producer $producer, bool $bookmark = true, bool $newsletter = true): UserProducer
    {
        return $this->userProducerRepository->findOneUserProducer($user)
            ?? $this->createUserProducer($user, $producer, $bookmark, $newsletter);
    }

    public function updateCredit(Payment $payment): void
    {
        if($payment->user && (
            $payment->getType() == Payment::TYPE_DEBIT
            || $payment->getType() == Payment::TYPE_CREDIT
            || ($payment->getType() == Payment::TYPE_PAYMENT && $this->paymentSolver->isMeanPaymentCredit($payment))
            || ($payment->getType() == Payment::TYPE_REFUND && $this->paymentSolver->isMeanPaymentCredit($payment))
            )) {
            
            $userProducer = $this->userProducerRepository->findOneUserProducer($payment->user);

            if ($userProducer) {
                $oldCredit = $userProducer->getCredit();

                $this->deductCredit($userProducer, $payment);
                //$this->initMeanPaymentOrder($payment);
                $this->sendCreditLimitReminder($userProducer, $payment, $oldCredit);
            }
        }
    }

    public function deductCredit(UserProducer $userProducer, Payment $payment)
    {
        if ($this->paymentSolver->isTypeCredit($payment)) {
            $userProducer->setCredit($userProducer->getCredit() + $payment->getAmount());
        } elseif ($this->paymentSolver->isTypeDebit($payment)) {
            $userProducer->setCredit($userProducer->getCredit() - $payment->getAmount());
        }

        $this->update($userProducer);
    }

    public function initMeanPaymentOrder(Payment $payment)
    {
        if ($payment->id_order && $payment->id_order > 0) {
            $order = $this->orderRepository->findOneOrderById((int) $payment->id_order);

            if ($order) {
                $paymentStatus = $this->orderSolver->getPaymentStatus($order);
                if ($paymentStatus == Order::PAYMENT_PAID
                    || $paymentStatus == Order::PAYMENT_SURPLUS) {

                    $order->mean_payment = MeanPayment::CREDIT;

                    $this->update($order);
                }
            }
        }
    }

    public function sendCreditLimitReminder($userProducer, $payment, $oldCredit)
    {
        $newCredit = $userProducer->credit;

        if ($this->isCreditLimitCrossed($oldCredit, $newCredit)) {

            $user = $this->userRepository->findOneUserById($payment->id_user);
            $producer = $this->producerRepository->findOneProducerById($payment->id_producer);

            if($user && $user->email && strlen($user->email) > 0) {
                \Yii::$app->mailer->compose(
                    [
                        'html' => '@common/mail/creditLimitReminder-html',
                        'text' => '@common/mail/creditLimitReminder-text'
                    ],
                    [
                        'user' => $user,
                        'producer' => $producer,
                        'credit' => $newCredit
                    ]
                )
                    ->setTo($user->email)
                    ->setFrom(['contact@opendistrib.net' => 'Opendistrib'])
                    ->setSubject('[Opendistrib] Seuil limite de crédit dépassé')
                    ->send();
            }
        }
    }

    public function isCreditLimitCrossed($oldCredit, $newCredit)
    {
        $creditLimitReminder = $this->producerSolver->getConfig('credit_limit_reminder');

        return !is_null($creditLimitReminder) &&
            $oldCredit > $creditLimitReminder
            && $newCredit <= $creditLimitReminder;
    }

    public function updateActive(User $user, Producer $producer, bool $active): bool
    {
        $userProducer = $this->createUserProducerIfNotExist(
            $user,
            $producer
        );

        $userProducer->active = $active;

        return $this->update($userProducer);
    }

    public function updateBookmark(UserProducer $userProducer, bool $bookmark)
    {
        $userProducer->bookmark = $bookmark;
        return $this->update($userProducer);
    }

    public function addProducerBookmark(User $user)
    {
        $userProducer = $this->createUserProducerIfNotExist($user, $this->getProducerContext());
        return $this->updateBookmark($userProducer, true);
    }

    public function removeProducerBookmark(User $user)
    {
        $userProducer = $this->createUserProducerIfNotExist($user, $this->getProducerContext());
        return $this->updateBookmark($userProducer, false);
    }

    public function unlinkUserProducer(UserProducer $userProducer)
    {
        $userProducer->active = 0;
        $userProducer->bookmark = 0;
        return $this->update($userProducer);
    }

    public function updateUserProducerNewsletter(UserProducer $userProducer, bool $newsletter)
    {
        $userProducer->newsletter = $newsletter;
        return $this->update($userProducer);
    }
}