<?php

namespace common\logic\Order\Order\Service;

use common\helpers\Price;
use common\logic\AbstractService;
use common\logic\Document\Document\Service\DocumentSolver;
use common\logic\Order\Order\Model\Order;
use common\logic\Product\Product\Model\Product;
use common\logic\SolverInterface;
use common\logic\User\CreditHistory\Model\CreditHistory;
use common\logic\User\CreditHistory\Service\CreditHistorySolver;
use common\logic\User\User\Model\User;
use common\logic\User\User\Service\UserSolver;
use yii\helpers\Html;

class OrderSolver extends AbstractService implements SolverInterface
{
    protected UserSolver $userSolver;
    protected DocumentSolver $documentSolver;
    protected CreditHistorySolver $creditHistorySolver;

    public function loadDependencies(): void
    {
        $this->documentSolver = $this->loadService(DocumentSolver::class);
        $this->userSolver = $this->loadService(UserSolver::class);
        $this->creditHistorySolver = $this->loadService(CreditHistorySolver::class);
    }

    public function getFieldNameAmount($typeTotal = Order::AMOUNT_TOTAL, string $typeField = ''): string
    {
        $fieldName = 'amount';
        if ($typeTotal == Order::INVOICE_AMOUNT_TOTAL) {
            $fieldName = 'invoice_amount';
        }

        if ($typeField == 'vat') {
            $fieldName = $fieldName . '_vat';
        } elseif ($typeField == 'with_tax') {
            $fieldName = $fieldName . '_with_tax';
        }

        return $fieldName;
    }

    public function getOrderTotalVat(Order $order, string $typeTotal = Order::AMOUNT_TOTAL): float
    {
        $fieldName = $this->getFieldNameAmount($typeTotal, 'vat');
        $totalVat = 0;

        foreach ($order->$fieldName as $vat) {
            $totalVat += $vat;
        }

        return $totalVat;
    }

    /**
     * Retourne le statut de paiement de la commande (payée, surplus, ou impayée).
     */
    public function getPaymentStatus(Order $order): ?string
    {
        // payé
        if ($this->getOrderAmountWithtax($order) - $this->getOrderAmount($order, Order::AMOUNT_PAID) < 0.01 &&
            $this->getOrderAmountWithtax($order) - $this->getOrderAmount($order, Order::AMOUNT_PAID) > -0.01) {
            return Order::PAYMENT_PAID;
        } // à rembourser
        elseif ($this->getOrderAmountWithtax($order) - $this->getOrderAmount($order, Order::AMOUNT_PAID) <= -0.01) {
            return Order::PAYMENT_SURPLUS;
        } // reste à payer
        elseif ($this->getOrderAmountWithtax($order) - $this->getOrderAmount($order, Order::AMOUNT_PAID) >= 0.01) {
            return Order::PAYMENT_UNPAID;
        }

        return null;
    }

    /**
     * Retourne le résumé du point de vente lié à la commande au format HTML.
     */
    public function getPointSaleSummary(Order $order): string
    {
        $html = '';

        if (isset($order->pointSale)) {
            $html .= '<span class="name-point-sale">' .
                Html::encode($order->pointSale->name) .
                '</span>' .
                '<br /><span class="locality">'
                . Html::encode($order->pointSale->locality)
                . '</span>';

            if (strlen($order->comment_point_sale)) {
                $html .= '<div class="comment"><span>'
                    . Html::encode($order->comment_point_sale)
                    . '</span></div>';
            }
        } else {
            $html .= 'Point de vente supprimé';
        }

        return $html;
    }

    /**
     * Retourne une chaine de caractère décrivant l'utilisateur lié à la commande.
     */
    // getStrUser
    public function getOrderUsername(Order $order): string
    {
        if (isset($order->user)) {
            if (isset($order->user->name_legal_person) && strlen($order->user->name_legal_person)) {
                return Html::encode($order->user->name_legal_person);
            } else {
                return Html::encode($this->userSolver->getUsername($order->user));
            }
        } elseif (strlen($order->username)) {
            return Html::encode($order->username);
        } else {
            return 'Client introuvable';
        }
    }

    public function isLinkedToValidDocument(Order $order): bool
    {
        return ($order->deliveryNote && $this->documentSolver->isStatusValid($order->deliveryNote))
            || ($order->quotation && $this->documentSolver->isStatusValid($order->quotation))
            || ($order->invoice && $this->documentSolver->isStatusValid($order->invoice));
    }

    public function getCommentReport(Order $order): string
    {
        $comment = '';
        $hasComment = false;

        if ($order->comment && strlen($order->comment) > 0) {
            $hasComment = true;
            $comment .= $order->comment;
        }

        if ($order->delivery_home && $order->delivery_address && strlen($order->delivery_address) > 0) {
            if ($hasComment) {
                $comment .= '<br /><br />';
            }

            $comment .= '<strong>Livraison à domicile :</strong><br />';
            $comment .= nl2br($order->delivery_address);
        }

        return $comment;
    }

    /**
     * Retourne un bloc html présentant une date.
     */
    // getBlockDate
    public function getDateAsHtml(Order $order): string
    {
        return '<div class="block-date">
                    <div class="day">' . strftime('%A', strtotime($order->distribution->date)) . '</div>
                    <div class="num">' . date('d', strtotime($order->distribution->date)) . '</div>
                    <div class="month">' . strftime('%B', strtotime($order->distribution->date)) . '</div>
                </div>';
    }

    /**
     * Retourne le nombre de produits commandés.
     */
    public function countProducts(Order $order): int
    {
        if ($order->productOrder && is_array($order->productOrder)) {
            return count($order->productOrder);
        }

        return 0;
    }

    public function getProductQuantityPieces(Product $product, array $orders): int
    {
        $quantity = 0;

        if (isset($orders) && is_array($orders) && count($orders)) {
            foreach ($orders as $c) {
                if (is_null($c->date_delete)) {
                    foreach ($c->productOrder as $po) {
                        if ($po->id_product == $product->id) {
                            if ($po->unit == 'piece') {
                                $quantity += $po->quantity;
                            } else {
                                if (isset($po->product) && $po->product->weight > 0) {
                                    $quantity += ($po->quantity * Product::$unitsArray[$po->unit]['coefficient']) / $po->product->weight;
                                }
                            }
                        }
                    }
                }
            }
        }

        return $quantity;
    }

    /**
     * Retourne la quantité d'un produit donné de plusieurs commandes.
     */
    public function getProductQuantity(Product $product, array $orders, bool $ignoreCancel = false, string $unit = null): float
    {
        $quantity = 0;

        if (isset($orders) && is_array($orders) && count($orders)) {
            foreach ($orders as $c) {
                if (is_null($c->date_delete) || $ignoreCancel) {
                    foreach ($c->productOrder as $po) {
                        if ($po->id_product == $product->id &&
                            ((is_null($unit) && $po->product->unit == $po->unit) || (!is_null($unit) && strlen(
                                        $unit
                                    ) && $po->unit == $unit))) {
                            $quantity += $po->quantity;
                        }
                    }
                }
            }
        }

        return $quantity;
    }

    /**
     * Retourne une classe identifiant l'historique de la commande (ajoutée,
     * modifiée, supprimée).
     */
    // getClassHistory
    public function getHistoryClass(Order $order): string
    {
        if (!is_null($order->date_delete)) {
            return 'commande-delete';
        }

        if (!is_null($order->date_update)) {
            return 'commande-update';
        }

        return 'commande-create';
    }

    /**
     * Retourne l'historique de la commande (ajoutée, modifiée, supprimée) au format HTML.
     */
    //getStrHistory
    public function getHistorySummary(Order $order): string
    {
        $arr = [
            'class' => 'create',
            'glyphicon' => 'plus',
            'str' => 'Ajoutée',
            'date' => $order->date
        ];

        if (!is_null($order->date_update)) {
            $arr = [
                'class' => 'update',
                'glyphicon' => 'pencil',
                'str' => 'Modifiée',
                'date' => $order->date_update
            ];
        }
        if (!is_null($order->date_delete)) {
            $arr = [
                'class' => 'delete',
                'glyphicon' => 'remove',
                'str' => 'Annulée',
                'date' => $order->date_delete
            ];
        }

        $html = '<div class="small"><span class="' . $arr['class'] . '">'
            . '<span class="glyphicon glyphicon-' . $arr['glyphicon'] . '"></span> '
            . $arr['str'] . '</span> le <strong>'
            . date('d/m/Y à G\hi', strtotime($arr['date'])) . '</strong></div>';

        return $html;
    }

    /**
     * Retourne l'origine de la commande (client, automatique ou admin) sous forme texte ou HTML.
     */
    // getStrOrigin
    public function getLabelOrigin(Order $order, bool $withLabel = false): string
    {
        $classLabel = '';
        $str = '';

        if ($order->origin == Order::ORIGIN_USER) {
            $classLabel = 'success';
            $str = 'Client';
        } elseif ($order->origin == Order::ORIGIN_AUTO) {
            $classLabel = 'default';
            $str = 'Auto';
        } elseif ($order->origin == Order::ORIGIN_ADMIN) {
            $classLabel = 'warning';
            $str = 'Vous';
        }

        if ($withLabel) {
            return '<span class="label label-' . $classLabel . '">'
                . $str . '</span>';
        } else {
            return $str;
        }
    }

    /**
     * Retourne le montant de la commande (total, payé, restant, ou en surplus).
     */
    // getAmount
    public function getOrderAmount(Order $order, string $type = Order::AMOUNT_TOTAL, bool $format = false)
    {
        $amount = $order->amount;
        if ($type == Order::INVOICE_AMOUNT_TOTAL && $order->invoice_amount) {
            $amount = $order->invoice_amount;
        }

        return $this->_getAmountGeneric($order, $type, $amount, $format);
    }

    // getAmountWithTax
    public function getOrderAmountWithTax(Order $order, string $type = Order::AMOUNT_TOTAL, bool $format = false)
    {
        $amount = $order->amount + $this->getOrderTotalVat($order, $type);
        if ($type == Order::INVOICE_AMOUNT_TOTAL && $order->invoice_amount) {
            $amount = $order->invoice_amount + $this->getOrderTotalVat($order, $type);
        }

        return $this->_getAmountGeneric($order, $type, $amount, $format);
    }

    protected function _getAmountGeneric(Order $order, string $type, float $amountOrder, bool $format)
    {
        switch ($type) {
            case Order::AMOUNT_TOTAL :
            case Order::INVOICE_AMOUNT_TOTAL :
                $amount = $amountOrder;
                break;
            case Order::AMOUNT_PAID :
                $amount = $order->paid_amount;
                break;
            case Order::AMOUNT_REMAINING :
                $amount = $this->getOrderAmountWithTax($order, Order::AMOUNT_TOTAL)
                    - $this->getOrderAmountWithTax($order,Order::AMOUNT_PAID);
                break;
            case Order::AMOUNT_SURPLUS :
                $amount = $this->getOrderAmountWithTax($order,Order::AMOUNT_PAID)
                    - $this->getOrderAmountWithTax($order,Order::AMOUNT_TOTAL);
                break;
        }

        if ($format) {
            return Price::format($amount);
        } else {
            return $amount;
        }
    }

    // belongsToUser
    public function isOrderBelongsToUser(Order $order, User $user = null): bool
    {
        if($user) {
            return $order->id_user == $user->id;
        }

        return false;
    }

    /**
     * Retourne les informations relatives à la commande au format JSON.
     */
    public function getDataJson(Order $order): string
    {
        $jsonOrder = [];
        if ($order) {
            $jsonOrder = [
                'products' => [],
                'amount' => $order->amount,
                'str_amount' => $this->getOrderAmountWithTax($order, Order::AMOUNT_TOTAL, true),
                'paid_amount' => $this->getOrderAmount($order, Order::AMOUNT_PAID),
                'comment' => $order->comment,
            ];

            foreach ($order->productOrder as $productOrder) {
                $jsonOrder['products'][$productOrder->id_product] = $productOrder->quantity;
            }
        }

        return json_encode($jsonOrder);
    }

    /**
     * Retourne les informations à ajouter au commentaire du CreditHistorique
     * (libellé, montant, client, action) au format HTML.
     *
     */
    public function getCreditHistoryComment(CreditHistory $creditHistory): string
    {
        $str = '';
        if (strlen($creditHistory->getComment())) {
            $str .= '<br />';
        }

        $str .= $this->creditHistorySolver->getStrWording($creditHistory);

        $order = $creditHistory->getOrderObject();
        if ($order) {
            $str .= '<br />Montant de la commande : ' . $this->getOrderAmountWithTax($order, Order::AMOUNT_TOTAL, true);
        }

        $user = $creditHistory->getUserObject();
        if ($user) {
            $str .= '<br />Client : ' . Html::encode($user->getName() . ' ' . $user->getLastname());
        }

        $userAction = $creditHistory->getUserActionObject();
        if ($userAction) {
            $str .= '<br />Action : ' . Html::encode($userAction->getName() . ' ' . $userAction->getLastname());
        }

        return $str;
    }

}