<?php

/**
 * Copyright distrib (2018)
 *
 * contact@opendistrib.net
 *
 * Ce logiciel est un programme informatique servant à aider les producteurs
 * à distribuer leur production en circuits courts.
 *
 * Ce logiciel est régi par la licence CeCILL soumise au droit français et
 * respectant les principes de diffusion des logiciels libres. Vous pouvez
 * utiliser, modifier et/ou redistribuer ce programme sous les conditions
 * de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
 * sur le site "http://www.cecill.info".
 *
 * En contrepartie de l'accessibilité au code source et des droits de copie,
 * de modification et de redistribution accordés par cette licence, il n'est
 * offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
 * seule une responsabilité restreinte pèse sur l'auteur du programme,  le
 * titulaire des droits patrimoniaux et les concédants successifs.
 *
 * A cet égard  l'attention de l'utilisateur est attirée sur les risques
 * associés au chargement,  à l'utilisation,  à la modification et/ou au
 * développement et à la reproduction du logiciel par l'utilisateur étant
 * donné sa spécificité de logiciel libre, qui peut le rendre complexe à
 * manipuler et qui le réserve donc à des développeurs et des professionnels
 * avertis possédant  des  connaissances  informatiques approfondies.  Les
 * utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
 * logiciel à leurs besoins dans des conditions permettant d'assurer la
 * sécurité de leurs systèmes et ou de leurs données et, plus généralement,
 * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.
 *
 * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
 * pris connaissance de la licence CeCILL, et que vous en avez accepté les
 * termes.
 */

namespace backend\controllers;

use common\helpers\CSV;
use common\helpers\GlobalParam;
use common\helpers\Price;
use common\logic\Document\DeliveryNote\Model\DeliveryNote;
use common\logic\Document\Document\Model\Document;
use common\logic\Document\Invoice\Model\Invoice;
use common\logic\Document\Quotation\Model\Quotation;
use common\logic\Order\Order\Model\Order;
use common\logic\Order\ProductOrder\Model\ProductOrder;
use common\logic\Product\Product\Model\Product;
use kartik\mpdf\Pdf;
use yii\base\UserException;
use yii;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
use yii\web\NotFoundHttpException;
use yii\helpers\Html;

class DocumentController extends BackendController
{
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::class,
                'actions' => [
                ],
            ],
            'access' => [
                'class' => AccessControl::class,
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@'],
                        'matchCallback' => function ($rule, $action) {
                            return $this->getUserManager()->hasAccessBackend();
                        }
                    ]
                ],
            ],
        ];
    }

    public function actionGeneratePdfValidatedDocuments()
    {
        set_time_limit(0);

        $validatedDocumentsArray = array_merge(
            Quotation::find()->where(['status' => Document::STATUS_VALID])->all(),
            DeliveryNote::find()->where(['status' => Document::STATUS_VALID])->all(),
            Invoice::find()->where(['status' => Document::STATUS_VALID])->all()
        );

        foreach ($validatedDocumentsArray as $document) {
            if (!file_exists($document->getFilenameComplete())) {
                $document->generatePdf(Pdf::DEST_FILE);
            }
        }
    }

    public function actionCreate()
    {
        $documentManager = $this->getDocumentManager();

        $class = $this->getClass();
        $class = 'common\\logic\\Document\\'.$class.'\\Model\\'.$class;
        $model = new $class();

        $documentManager->initTaxCalculationMethod($model);

        if ($model->load(\Yii::$app->request->post())) {
            $model->id_producer = GlobalParam::getCurrentProducerId();

            if ($model->save()) {
                $this->processInvoiceViaDeliveryNotes($model);

                $this->setFlash('success', $this->getFlashMessage('create', $model));
                return $this->redirect(['/' . $this->getControllerUrl() . '/update', 'id' => $model->id]);
            } else {
                $this->setFlash('error', 'Un problème est survenu lors de la création du document.');
            }
        }

        return $this->render('/document/create', [
            'title' => $this->getTitle('Ajouter'),
            'typeDocument' => $this->getDocumentType(),
            'model' => $model,
        ]);
    }

    public function processInvoiceViaDeliveryNotes($model)
    {
        $documentManager = $this->getDocumentManager();
        if ($documentManager->getClass($model) == 'Invoice') {
            if ($model->deliveryNotes && is_array($model->deliveryNotes) && count($model->deliveryNotes)) {
                foreach ($model->deliveryNotes as $key => $idDeliveryNote) {
                    Order::updateAll([
                        'id_invoice' => $model->id
                    ], [
                        'id_delivery_note' => $idDeliveryNote
                    ]);
                }
            }
        }
    }

    /**
     * Modifie un modèle Produit existant.
     * Si la modification réussit, le navigateur est redirigé vers la page 'index'.
     *
     * @param integer $id
     * @return mixed
     */
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);

        if (!$model) {
            throw new yii\web\NotFoundHttpException('Le document n\'a pas été trouvé.');
        }

        if ($model && $model->load(\Yii::$app->request->post()) && $model->save()) {
            $this->setFlash('success', $this->getFlashMessage('update', $model));
        }

        return $this->render('/document/update', [
            'title' => $this->getTitle('Modifier'),
            'typeDocument' => $this->getDocumentType(),
            'model' => $model,
        ]);
    }

    public function actionDelete($id)
    {
        $documentManager = $this->getDocumentManager();

        $model = $this->findModel($id);

        if ($documentManager->isStatusValid($model)) {
            throw new UserException('Vous ne pouvez pas supprimer un document validé.');
        }

        $documentManager->delete($model);

        if ($this->getClass() == 'DeliveryNote') {
            Order::updateAll([
                'order.id_delivery_note' => null
            ], [
                'order.id_delivery_note' => $id
            ]);
        }

        if ($this->getClass() == 'Quotation') {
            Order::updateAll([
                'order.id_quotation' => null
            ], [
                'order.id_quotation' => $id
            ]);
        }

        if ($this->getClass() == 'Invoice') {
            Order::updateAll([
                'order.id_invoice' => null
            ], [
                'order.id_invoice' => $id
            ]);
        }

        $this->setFlash('success', $this->getFlashMessage('delete', $model));

        $this->redirect([$this->getControllerUrl() . '/index']);
    }

    public function actionExportCsvEvoliz(int $id)
    {
        $documentManager = $this->getDocumentManager();
        $productOrderManager = $this->getProductOrderManager();

        $datas = [];
        $document = $this->findModel($id);

        // données
        $datas[] = [
            'N° facture externe *',
            'Date facture *',
            'Client',
            'Code client *',
            'Total TVA',
            'Total HT',
            'Total TTC',
            'Total réglé',
            'Etat',
            'Date Etat',
            'Date de création',
            'Objet',
            'Date d\'échéance',
            'Date d\'exécution',
            'Taux de pénalité',
            'Frais de recouvrement',
            'Taux d\'escompte',
            'Conditions de règlement *',
            'Mode de paiement',
            'Remise globale',
            'Acompte',
            'Nombre de relance',
            'Commentaires',
            'N° facture',
            'Annulé',
            'Catalogue',
            'Réf.',
            'Désignation *',
            'Qté *',
            'Unité',
            'PU HT *',
            'Remise',
            'TVA',
            'Total TVA',
            'Total HT',
            'Créateur',
        ];

        foreach ($documentManager->getProductsOrders($document) as $productOrderArray) {
            foreach ($productOrderArray as $productOrder) {

                $price = $productOrder->getPrice();
                if ($documentManager->isInvoicePrice($document) && $productOrder->getInvoicePrice()) {
                    $price = $productOrder->getInvoicePrice();
                }

                $typeTotal = $documentManager->isInvoicePrice($document) ? Order::INVOICE_AMOUNT_TOTAL : Order::AMOUNT_TOTAL;
                $priceTotal = $productOrderManager->getPriceByTypeTotal($productOrder, $typeTotal) * $productOrder->quantity;
                $tva = Price::getVat(
                    $priceTotal,
                    $productOrder->taxRate->value,
                    $document->tax_calculation_method
                );

                $datas[] = [
                    $document->reference,   // N° facture externe *
                    date('d/m/Y', strtotime($document->date)),        // Date facture *
                    '', // Client
                    $document->user->evoliz_code, // Code client *
                    '', // Total TVA
                    '', // Total HT
                    '', // Total TTC
                    '', // Total réglé
                    '', // Etat
                    '', // Date Etat
                    '', // Date de création
                    $document->name, // Objet
                    '', // Date d'échéance
                    '', // Date d'exécution
                    '', // Taux de pénalité
                    '', // Frais de recouvrement
                    '', // Taux d\'escompte
                    'A réception', // Conditions de règlement *
                    '', // Mode de paiement
                    '', // Remise globale
                    '', // Acompte
                    '', // Nombre de relance
                    '', // Commentaires
                    '', // N° facture
                    '', // Annulé
                    'Non', // Catalogue
                    '', // Réf.
                    $productOrder->product->name, // Désignation *
                    $productOrder->quantity, // Qté *
                    '', // Product::strUnit($productOrder->unit, 'wording'), // Unité
                    $price, // PU HT *
                    '', // Remise
                    $productOrder->taxRate->value * 100, // TVA
                    $tva, // Total TVA
                    $priceTotal, // Total HT
                    '', // Créateur
                ];
            }
        }

        // nom fichier
        $reference = $document->id;
        if ($document->reference && strlen($document->reference)) {
            $reference = $document->reference;
        }

        // status
        $status = '';
        if ($documentManager->isStatusDraft($document)) {
            $status = 'brouillon_';
        }

        CSV::downloadSendHeaders(strtolower($this->getDocumentType()) . '_' . $status . $reference . '.csv');
        echo CSV::array2csv($datas);
        die();
    }

    public function actionDownload($id)
    {
        $documentManager = $this->getDocumentManager();
        $document = $this->findModel($id);
        return $documentManager->downloadPdf($document);
    }

    public function actionRegenerate($id)
    {
        $documentManager = $this->getDocumentManager();
        $document = $this->findModel($id);
        $documentManager->downloadPdf($document, true);
        $this->setFlash('success', 'Le document PDF a bien été regénéré.');

        return $this->redirect([$this->getControllerUrl() . '/update', 'id' => $id]);
    }

    public function actionSend(int $id, $backUpdateForm = false)
    {
        $documentManager = $this->getDocumentManager();
        $document = $this->findModel($id);
        if ($documentManager->send($document)) {
            $document->is_sent = true;
            $document->save();

            $this->setFlash('success', $this->getFlashMessage('send', $document));
        } else {
            $this->setFlash('danger', $this->getFlashMessage('send', $document));
        }

        if ($backUpdateForm) {
            return $this->redirect([$this->getControllerUrl() . '/update', 'id' => $id]);
        } else {
            return $this->redirect([$this->getControllerUrl() . '/index']);
        }
    }

    public function actionAjaxUserInfos($typeAction, $idUser, $classDocument, $idDocument = false)
    {
        $userManager = $this->getUserManager();
        $documentManager = $this->getDocumentManager();

        \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        if ($idUser > 0) {
            $user = $userManager->findOneUserById($idUser);

            if ($user) {
                $document = null;
                if ($documentManager->isValidClass($classDocument) && $idDocument) {
                    $document = $this->findModel($idDocument, $classDocument);
                }

                if ($document && $document->id_user == $user->id) {
                    $address = $document->address;
                } else {
                    $address = $userManager->getFullAddress($user);
                }

                $json = [
                    'return' => 'success',
                    'address' => $address
                ];

                if ($classDocument == 'Invoice') {
                    $options = [
                        'orderby' => 'distribution.date ASC',
                        'join_with' => ['user AS user_delivery_note', 'orders', 'producer']
                    ];
                    $deliveryNotesCreateArray = DeliveryNote::searchAll([
                        'id_user' => $user->id,
                        'status' => Document::STATUS_VALID,
                        'ignore_when_billing' => null
                    ], $options);
                    $deliveryNotesUpdateArray = DeliveryNote::searchAll([
                        'id_user' => $user->id,
                        'status' => Document::STATUS_VALID,
                        'order.id_invoice' => $idDocument,
                        'ignore_when_billing' => null
                    ], $options);
                    $json['delivery_note_create_array'] = $this->initDeliveryNoteArray('create', $deliveryNotesCreateArray);
                    $json['delivery_note_update_array'] = $this->initDeliveryNoteArray('update', $deliveryNotesUpdateArray);
                }

                return $json;
            }
        }

        return ['return' => 'error'];
    }

    public function initDeliveryNoteArray($type, $deliveryNoteArrayResults)
    {
        $deliveryNoteArray = [];
        $isCreate = false;
        if ($type == 'create') {
            $isCreate = true;
        }

        if ($deliveryNoteArrayResults) {
            foreach ($deliveryNoteArrayResults as $deliveryNote) {
                $deliveryNoteData = $this->addDeliveryNoteToArray($deliveryNote, $isCreate);
                if ($deliveryNoteData) {
                    $deliveryNoteArray[] = $deliveryNoteData;
                }
            }
        }

        return $deliveryNoteArray;
    }

    public function addDeliveryNoteToArray($deliveryNote, $isCreate = true)
    {
        $deliveryNoteManager = $this->getDeliveryNoteManager();

        $deliveryNoteData = array_merge(
            $deliveryNote->getAttributes(),
            [
                'url' => $this->getUrlManagerBackend()->createUrl(['delivery-note/update', 'id' => $deliveryNote->id]),
                'total' => $deliveryNoteManager->getAmountWithTax($deliveryNote, Order::INVOICE_AMOUNT_TOTAL)
            ]
        );

        if (($isCreate && !$deliveryNoteManager->isInvoiced($deliveryNote)) || !$isCreate) {
            return $deliveryNoteData;
        }

        return false;
    }

    public function actionValidate($id, $backUpdateForm = false)
    {
        $documentManager = $this->getDocumentManager();
        $classDocument = $this->getClass();

        if ($id > 0 && $documentManager->isValidClass($classDocument)) {
            $document = $this->findModel($id);

            if ($document) {
                $documentManager->changeStatus($document,Document::STATUS_VALID);
                $documentManager->saveUpdate($document);

                // génération PDF
                $documentManager->generatePdf($document, Pdf::DEST_FILE);

                $this->setFlash('success', $this->getFlashMessage('validate', $document));

                if ($backUpdateForm) {
                    return $this->redirect([$this->getControllerUrl() . '/update', 'id' => $id]);
                } else {
                    return $this->redirect([$this->getControllerUrl() . '/index']);
                }
            }
        }

        $this->setFlash('danger', 'Une erreur est survenue lors de la validation du document.');

        return $this->redirect([$this->getControllerUrl() . '/index']);
    }

    public function actionAjaxValidateDocument($idDocument, $classDocument)
    {
        \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        $documentManager = $this->getDocumentManager();

        if ($idDocument > 0 && $documentManager->isValidClass($classDocument)) {
            $document = $classDocument::searchOne([
                'id' => $idDocument
            ]);

            if ($document) {
                $documentManager->changeStatus($document,Document::STATUS_VALID);
                $documentManager->saveUpdate($document);

                return [
                    'return' => 'success',
                    'alert' => [
                        'type' => 'success',
                        'message' => 'Document validé'
                    ]
                ];
            }
        }

        return [
            'return' => 'error',
            'alert' => [
                'type' => 'danger',
                'message' => 'Une erreur est survenue lors de la validation du document.'
            ]
        ];
    }

    public function actionAjaxInit($idDocument, $classDocument)
    {
        \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        $orderManager = $this->getOrderManager();
        $productManager = $this->getProductManager();
        $documentManager = $this->getDocumentManager();
        $deliveryNoteManager = $this->getDeliveryNoteManager();
        $userProducerManager = $this->getUserProducerManager();
        $pointSaleManager = $this->getPointSaleManager();

        if ($idDocument > 0 && $documentManager->isValidClass($classDocument)) {

            $document = $this->findModel($idDocument, $classDocument);

            if ($document) {
                $ordersArray = [];
                $productsArray = $productManager->findProducts();

                foreach ($document->orders as $order) {
                    $orderManager->initOrder($order);
                    $productsOrderArray = [];

                    foreach ($order->productOrder as $productOrder) {
                        $productsOrderArray[$productOrder->id] = array_merge($productOrder->getAttributes(), [
                            'url_order' => $this->getUrlManagerBackend()->createUrl(['distribution/index', 'idOrderUpdate' => $productOrder->id_order])
                        ]);
                    }
                    $ordersArray[$order->id] = array_merge(
                        $order->getAttributes(),
                        [
                            'username' => $orderManager->getOrderUsername($order),
                            'distribution_date' => isset($order->distribution) ? date(
                                'd/m/Y',
                                strtotime(
                                    $order->distribution->date
                                )
                            ) : null,
                            'point_sale_name' => isset($order->pointSale) ? $order->pointSale->name : null,
                            'productOrder' => $productsOrderArray,
                        ]
                    );
                }

                $userProducer = $userProducerManager->findOneUserProducer($document->user, $this->getProducerCurrent());
                $pointSale = $pointSaleManager->findOnePointSaleByIdUser($document->user->id);

                $productsArray = yii\helpers\ArrayHelper::map(
                    $productsArray,
                    'order',
                    function ($product) use ($document, $userProducer, $pointSale, $productManager) {
                        return array_merge($product->getAttributes(), [
                            'unit_coefficient' => Product::$unitsArray[$product->unit]['coefficient'],
                            'prices' => $productManager->getPriceArray($product, $userProducer->user, $pointSale),
                            'wording_unit' => $product->wording_unit,
                            'tax_rate' => $product->taxRate->value
                        ]);
                    }
                );

                return [
                    'return' => 'success',
                    'tax_rate_producer' => GlobalParam::getCurrentProducer()->taxRate->value,
                    'document' => array_merge($document->getAttributes(), [
                        'html_label' => $documentManager->getHtmlLabel($document),
                        'class' => $documentManager->getClass($document)
                    ]),
                    'id_user' => $document->user->id,
                    'products' => $productsArray,
                    'orders' => $ordersArray,
                    'total' => ($documentManager->getClass($document) == 'Invoice' || $documentManager->getClass($document) == 'DeliveryNote') ? $documentManager->getAmount(
                        $document,
                        Order::INVOICE_AMOUNT_TOTAL
                    ) : $documentManager->getAmount($document, Order::AMOUNT_TOTAL),
                    'total_with_tax' => ($documentManager->getClass($document) == 'Invoice' || $documentManager->getClass($document) == 'DeliveryNote') ? $documentManager->getAmountWithTax(
                        $document,
                        Order::INVOICE_AMOUNT_TOTAL
                    ) : $documentManager->getAmountWithTax($document, Order::AMOUNT_TOTAL),
                    'invoice_url' => ($documentManager->getClass($document) == 'DeliveryNote' && $deliveryNoteManager->getInvoice($document)) ? $this->getUrlManagerBackend()->createUrl(['invoice/update', 'id' => $deliveryNoteManager->getInvoice($document)->id]) : null
                ];
            }
        }

        return ['return' => 'error'];
    }

    public function actionAjaxAddProduct($idDocument, $classDocument, $idProduct, $quantity, $price)
    {
        \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        $documentManager = $this->getDocumentManager();
        $productManager = $this->getProductManager();

        if ($documentManager->isValidClass($classDocument)) {
            $document = $this->findModel($idDocument, $classDocument);
            $product = $productManager->findOneProductById($idProduct);

            if ($document && $product) {
                if (count($document->orders) == 0) {
                    $order = new Order;
                    $order->id_user = $document->id_user;
                    $order->id_point_sale = null;
                    $order->id_distribution = null;
                    $order->status = 'tmp-order';
                    $order->origin = Order::ORIGIN_ADMIN;
                    $order->date = date('Y-m-d H:i:s');
                    $fieldIdDocument = 'id_' . $classDocument::tableName();
                    $order->$fieldIdDocument = $document->id;
                    $order->save();
                } else {
                    $order = $document->orders[0];
                }

                if ($order) {
                    $productOrder = new ProductOrder();
                    $productOrder->id_order = $order->id;
                    $productOrder->id_product = $idProduct;
                    $quantity = $quantity / Product::$unitsArray[$product->unit]['coefficient'];
                    $productOrder->quantity = $quantity;
                    $productOrder->price = (float)$price;
                    $productOrder->unit = $product->unit;
                    $productOrder->step = $product->step;
                    $productOrder->id_tax_rate = $product->taxRate->id;
                    $productOrder->save();

                    return [
                        'return' => 'success',
                        'alert' => [
                            'type' => 'success',
                            'message' => 'Produit ajouté'
                        ]
                    ];
                }
            }
        }

        return [
            'return' => 'error',
            'alert' => [
                'type' => 'danger',
                'message' => 'Une erreur est survenue lors de la suppression du produit.'
            ]
        ];
    }

    public function actionAjaxDeleteProductOrder($idProductOrder)
    {
        \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        $productOrderManager = $this->getProductOrderManager();

        $productOrder = $productOrderManager->findOneProductOrderById($idProductOrder);

        if ($productOrder) {
            $productOrderManager->delete($productOrder);

            return [
                'return' => 'success',
                'alert' => [
                    'type' => 'danger',
                    'message' => 'Produit supprimé'
                ]
            ];
        }

        return [
            'return' => 'error',
            'alert' => [
                'type' => 'danger',
                'message' => 'Une erreur est survenue lors de la suppression du produit.'
            ]
        ];
    }

    public function getClass()
    {
        $class = get_class($this);
        $class = str_replace('Controller', '', $class);
        $class = str_replace('backend\controllers\\', '', $class);
        return $class;
    }

    public function getDocumentType()
    {
        $class = $this->getClass();

        if ($class == 'Invoice') {
            $documentType = 'Facture';
        } elseif ($class == 'DeliveryNote') {
            $documentType = 'Bon de livraison';
        } elseif ($class == 'Quotation') {
            $documentType = 'Devis';
        }

        if (isset($documentType)) {
            return $documentType;
        }

        return '';
    }

    public function getFlashMessage(string $type, $model)
    {
        $class = $this->getClass();

        $message = $this->getDocumentType();
        $message .= ' <strong>' . Html::encode($model->name) . '</strong> ';

        if ($type == 'create') {
            $message .= 'ajouté';
        } elseif ($type == 'update') {
            $message .= 'modifié';
        } elseif ($type == 'delete') {
            $message .= 'supprimé';
        } elseif ($type == 'validate') {
            $message .= 'validé';
        } elseif ($type == 'send') {
            $message .= 'envoyé';
        }

        if ($class == 'Invoice') {
            $message .= 'e';
        }

        return $message;
    }

    protected function getTitle($prepend)
    {
        $class = $this->getClass();

        switch ($class) {
            case 'Invoice' :
                $title = $prepend . ' une facture';
                break;
            case 'DeliveryNote' :
                $title = $prepend . ' un bon de livraison';
                break;
            case 'Quotation' :
                $title = $prepend . ' un devis';
                break;
        }

        return $title;
    }

    public function getControllerUrl()
    {
        $path = strtolower($this->getClass());
        $path = str_replace('deliverynote', 'delivery-note', $path);
        return $path;
    }

    /**
     * Recherche un Document en fonction de son ID.
     *
     * @param integer $id
     * @return Document
     * @throws NotFoundHttpException si le modèle n'est pas trouvé
     */
    protected function findModel($idDocument, $classDocument = null)
    {
        $documentManager = $this->getDocumentManager();
        $model = $documentManager->findOneDocumentByIdAndClass($idDocument, $classDocument ?? $this->getClass());

        if ($model) {
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }
}
