<?php

namespace common\logic\Order\Order\Repository;

use common\logic\AbstractRepository;
use common\logic\Distribution\Distribution\Model\Distribution;
use common\logic\Distribution\ProductDistribution\Repository\ProductDistributionRepository;
use common\logic\Order\Order\Model\Order;
use common\logic\Order\Order\Service\OrderBuilder;
use common\logic\Order\Order\Service\OrderSolver;
use common\logic\Order\ProductOrder\Repository\ProductOrderRepository;
use common\logic\PointSale\PointSale\Model\PointSale;
use common\logic\Producer\Producer\Model\Producer;
use common\logic\Producer\Producer\Repository\ProducerRepository;
use common\logic\Product\Product\Model\Product;
use common\logic\Product\Product\Service\ProductSolver;
use common\logic\User\User\Model\User;
use common\logic\User\UserProducer\Model\UserProducer;
use yii\helpers\Html;

class OrderRepository extends AbstractRepository
{
    protected OrderRepositoryQuery $query;
    protected ProductOrderRepository $productOrderRepository;
    protected ProducerRepository $producerRepository;
    protected OrderSolver $orderSolver;
    protected ProductDistributionRepository $productDistributionRepository;
    protected ProductSolver $productSolver;

    public function loadDependencies(): void
    {
        $this->query = $this->loadService(OrderRepositoryQuery::class);
        $this->productOrderRepository = $this->loadService(ProductOrderRepository::class);
        $this->producerRepository = $this->loadService(ProducerRepository::class);
        $this->orderSolver = $this->loadService(OrderSolver::class);
        $this->productDistributionRepository = $this->loadService(ProductDistributionRepository::class);
        $this->productSolver = $this->loadService(ProductSolver::class);
    }

    public function getDefaultOptionsSearch(): array
    {
        return [
            'with' => [
                'productOrder',
                'productOrder.product',
                'creditHistory',
                'creditHistory.userAction',
                'pointSale'
            ],
            'join_with' => [
                'distribution',
                'user',
                'user.userProducer'
            ],
            'orderby' => 'order.date ASC',
            'attribute_id_producer' => 'distribution.id_producer'
        ];
    }

    public function findOneOrderById(int $id)
    {
        return Order::searchOne(['order.id' => $id]);;
    }

    public function findOrdersByDate(string $date): array
    {
        return Order::searchAll([
            'distribution.date' => $date
        ]);
    }

    /**
     * Recherche et initialise des commandes.
     */
    // searchBy
    // findBy
    public function findOrdersBy(array $params = [], array $options = [])
    {
        $orderBuilder = OrderBuilder::getInstance();
        $orders = Order::searchBy($params, $options);

        /*
         * Initialisation des commandes
         */
        if (is_array($orders)) {
            if (count($orders)) {
                foreach ($orders as $order) {
                    if (is_a($order, 'common\logic\Order\Order\Model\Order')) {
                        $orderBuilder->initOrder($order);
                    }
                }
                return $orders;
            }
        } else {
            $order = $orders;
            if (is_a($order, 'common\logic\Order\Order\Model\Order')) {
                $orderBuilder->initOrder($order);
                return $order;
            } // count
            else {
                return $order;
            }
        }

        return false;
    }

    // findByDistribution
    public function findOrdersByDistribution(Distribution $distribution, string $conditionAppend = '')
    {
        return Order::searchAll([
            'distribution.date' => $distribution->date,
            'distribution.id_producer' => $distribution->id_producer
        ],
            [
                'conditions' => 'date_delete IS NULL ' . $conditionAppend
            ]);
    }

    // queryHistory
    public function queryOrdersHistory(Producer $producer, User $user)
    {
        $query = Order::find()
            ->with('productOrder', 'pointSale', 'creditHistory')
            ->joinWith('distribution', 'distribution.producer')
            ->where([
                'id_user' => $user->id,
                'distribution.id_producer' => $producer->id
            ])
            ->params([':date_today' => date('Y-m-d')]);

        $queryIncoming = clone $query;
        $queryIncoming
            ->andWhere('distribution.date >= :date_today')
            ->orderBy('distribution.date ASC');

        $queryPassed = clone $query;
        $queryPassed
            ->andWhere('distribution.date < :date_today')
            ->orderBy('distribution.date DESC');

        return ['incoming' => $queryIncoming, 'passed' => $queryPassed];
    }

    /**
     * Retourne le résumé du panier au format HTML.
     * @TODO : à déplacer dans OrderSolver
     */
    public function getCartSummary(Order $order, $htmlFormat = true): string
    {
        if (!isset($order->productOrder)) {
            $order->productOrder = $this->productOrderRepository->findProductOrdersByOrder($order);
        }

        $html = '';
        $i = 0;
        $count = count($order->productOrder);
        foreach ($order->productOrder as $p) {
            if (isset($p->product)) {
                $html .= Html::encode($p->product->name) . ' (' . $p->quantity . '&nbsp;' . $this->productSolver->strUnit(
                        $p->unit,
                        'wording_short',
                        true
                    ) . ')';
                if (++$i != $count) {
                    if ($htmlFormat) {
                        $html .= '<br />';
                    } else {
                        $html .= "\n";
                    }
                }
            }
        }

        return $html;
    }

    /**
     * Retourne le résumé du paiement (montant, statut).
     * @TODO : à déplacer dans OrderSolver
     */
    public function getAmountSummary(Order $order): string
    {
        $html = '';
        $creditActive = $this->producerRepository->getConfig('credit');
        $html .= $this->orderSolver->getOrderAmountWithTax($order, Order::AMOUNT_TOTAL, true);

        if ($creditActive) {
            $html .= '<br />';
            if ($order->paid_amount) {
                if ($this->orderSolver->getPaymentStatus($order) == Order::PAYMENT_PAID) {
                    $html .= '<span class="label label-success">Payée</span>';
                } elseif ($this->orderSolver->getPaymentStatus($order) == Order::PAYMENT_UNPAID) {
                    $html .= '<span class="label label-danger">Non payée</span><br />
                                                Reste <strong>' . $this->orderSolver->getOrderAmount(
                            $order,
                            Order::AMOUNT_REMAINING,
                            true
                        ) . '</strong> à payer';
                } elseif ($this->orderSolver->getPaymentStatus($order) == Order::PAYMENT_SURPLUS) {
                    $html .= '<span class="label label-success">Payée</span>';
                }
            } else {
                $html .= '<span class="label label-default">Non réglé</span>';
            }
        }

        return $html;
    }

    /**
     * Retourne l'état de la commande (livrée, modifiable ou en préparation.
     * @TODO : à déplacer dans OrderSolver
     */
    public function getState(Order $order): string
    {
        $orderDate = strtotime($order->distribution->date);
        $today = strtotime(date('Y-m-d'));
        $todayHour = date('G');
        $dayDistribution = strtolower(date('l', strtotime($order->distribution->date)));

        $orderDelay = $this->producerRepository->getConfig(
            'order_delay',
            $order->distribution->id_producer
        );
        $orderDelaySpecific = $this->producerRepository->getConfig(
            'order_delay_' . $dayDistribution,
            $order->distribution->id_producer
        );
        if ($orderDelaySpecific) {
            $orderDelay = $orderDelaySpecific;
        }

        $orderDeadline = $this->producerRepository->getConfig(
            'order_deadline',
            $order->distribution->id_producer
        );
        $orderDeadlineSpecific = $this->producerRepository->getConfig(
            'order_deadline_' . $dayDistribution,
            $order->distribution->id_producer
        );
        if ($orderDeadlineSpecific) {
            $orderDeadline = $orderDeadlineSpecific;
        }

        $nbDays = (int) round((($orderDate - $today) / (24 * 60 * 60)));

        if ($nbDays <= 0) {
            return Order::STATE_DELIVERED;
        } elseif ($nbDays >= $orderDelay &&
            ($nbDays != $orderDelay ||
                ($nbDays == $orderDelay && $todayHour < $orderDeadline))) {
            return Order::STATE_OPEN;
        }

        return Order::STATE_PREPARATION;
    }

    public function isOrderStateOpen(Order $order): bool
    {
        return Order::STATE_OPEN == $this->getState($order);
    }

    public function isOrderStatePreparation(Order $order): bool
    {
        return Order::STATE_PREPARATION == $this->getState($order);
    }

    public function isOrderStateDelivered(Order $order): bool
    {
        return Order::STATE_DELIVERED == $this->getState($order);
    }

    public function findOneOrderLastOfYear(Producer $producer)
    {
        return Order::find()->innerJoinWith('distribution', true)
            ->where(['>=', 'distribution.date', date('Y') . '-01-01'])
            ->andWhere([
                'distribution.id_producer' => $producer->id
            ])
            ->andWhere(['not', ['order.reference' => null]])
            ->orderBy('order.reference DESC')
            ->one();
    }

    // searchByDistribution
    public function findProductDistributionsByDistribution(Distribution $distribution): array
    {
        $orderArray = $this->findOrdersByDistribution($distribution);
        $productDistributionArray = $this->productDistributionRepository->findProductDistributionsByDistribution($distribution);

        foreach ($productDistributionArray as $productDistribution) {
            if (isset($productDistribution->product)) {
                $productDistributionArray[$productDistribution->id_product] = [
                    'active' => (int) $productDistribution->active,
                    'unavailable' => (int) $productDistribution->product->unavailable,
                    'quantity_max' => $productDistribution->quantity_max,
                    'quantity_order' => $this->orderSolver->getProductQuantity($productDistribution->product, $orderArray),
                    'quantity_remaining' => $productDistribution->quantity_max - $this->orderSolver->getProductQuantity($productDistribution->product, $orderArray)
                ];
            }
        }

        return $productDistributionArray;
    }

    public function isCreditAutoPayment(Order $order)
    {
        $pointSale = PointSale::findOne($order->id_point_sale);
        $distribution = Distribution::findOne($order->id_distribution);
        $creditFunctioning = $this->producerRepository->getPointSaleCreditFunctioning($pointSale);

        if ($order->id_user && $this->producerRepository->getConfig('credit') && $pointSale->credit) {
            if ($creditFunctioning == Producer::CREDIT_FUNCTIONING_OPTIONAL) {
                return 0;
            } elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_MANDATORY) {
                return 1;
            } elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_USER) {
                $userProducer = UserProducer::searchOne([
                    'id_user' => $order->id_user,
                    'id_producer' => $distribution->id_producer
                ]);
                if ($userProducer) {
                    return $userProducer->credit_active;
                }
            }
        }

        return 0;
    }

}