<?php

namespace Sibs\Sibs\Model;

use Exception;
use Sibs\Sibs\DB\SibsDB;
use Sibs\Sibs\Model\SibsConstants;
use Sibs\Sibs\Utils\SibsException;
use Sibs\Sibs\Utils\SibsLog;

class SibsOrder
{
    /**
     * @var \Magento\Sales\Api\Data\OrderInterface
     */
    private $order;

    /**
     * @param string $orderId
     */
    public function __construct($orderId)
    {
        try {
            $this->order = SibsDB::create('\Magento\Sales\Model\OrderRepository')->get($orderId);
        } catch (Exception $e) {
            SibsLog::crit($e->getMessage());
            $this->order = null;
        }
    }

    /**
     * Get Order
     *
     * @return \Magento\Sales\Api\Data\OrderInterface
     */
    public function get()
    {
        return $this->order;
    }

    /**
     * Delete Order
     */
    public function delete()
    {
        return $this->order->delete();
    }

    /**
     * Save Order
     *
     * @return \Magento\Sales\Api\Data\OrderInterface
     */
    public function save()
    {
        return $this->order->save();
    }

    /**
     * Gets the ID for the order.
     *
     * @return int|null Order ID.
     */
    public function getEntityId()
    {
        return $this->order->getEntityId();
    }

    /**
     * Gets the grand total for the order.
     *
     * @return float Grand total.
     */
    public function getGrandTotal()
    {
        return $this->order->getGrandTotal();
    }

    /**
     * Gets the order currency code for the order.
     *
     * @return string|null Order currency code.
     */
    public function getOrderCurrencyCode()
    {
        return $this->order->getOrderCurrencyCode();
    }

    /**
     * Gets the increment ID for the order.
     *
     * @return string|null Increment ID.
     */
    public function getIncrementId()
    {
        return $this->order->getIncrementId();
    }

    /**
     * Gets the total paid for the order.
     *
     * @return float|null Total paid.
     */
    public function getTotalPaid()
    {
        return $this->order->getTotalPaid();
    }

    /**
     * Gets the state for the order.
     *
     * @return string|null State.
     */
    public function getState()
    {
        return $this->order->getState();
    }

    /**
     * Gets the status for the order.
     *
     * @return string|null Status.
     */
    public function getStatus()
    {
        return $this->order->getStatus();
    }

    /**
     * Gets the created-at timestamp for the order.
     *
     * @return string|null Created-at timestamp.
     */
    public function getCreatedAt()
    {
        return $this->order->getCreatedAt();
    }

    /**
     * Gets the payment method code for the order.
     *
     * @return string|null Payment Method Code.
     */
    public function getPaymentMethodCode()
    {
        return $this->order->getPayment()->getMethodInstance()->getCode();
    }

    /**
     * Gets the customer name for the order.
     *
     * @return string Customer name.
     */
    public function getCustomerName()
    {
        return $this->order->getCustomerName();
    }

    /**
     * Gets the customer email address for the order.
     *
     * @return string Customer email address.
     */
    public function getCustomerEmail()
    {
        return $this->order->getCustomerEmail();
    }

    /**
     * Gets the shipping address, if any, for the order.
     *
     * @return \Magento\Sales\Api\Data\OrderAddressInterface|null Billing shipping. Otherwise, null.
     */
    public function getShippingAddress()
    {
        return $this->order->getShippingAddress();
    }

    /**
     * Gets the billing address, if any, for the order.
     *
     * @return \Magento\Sales\Api\Data\OrderAddressInterface|null Billing address. Otherwise, null.
     */
    public function getBillingAddress()
    {
        return $this->order->getBillingAddress();
    }

    /**
     * Get all visible items
     *
     * @return array
     */
    public function getAllVisibleItems()
    {
        return $this->order->getAllVisibleItems();
    }

    /**
     * Gets status histories for the order.
     *
     * @return \Magento\Sales\Api\Data\OrderStatusHistoryInterface[]|null Array of status histories.
     */
    public function getStatusHistories()
    {
        return $this->order->getStatusHistories();
    }

    /**
     * Get all items
     *
     * @return \Magento\Sales\Model\Order\Item[]
     */
    public function getAllItems()
    {
        return $this->order->getAllItems();
    }

    /**
     * Get SIBS Transaction ID
     *
     * @return string
     */
    public function getTransactionId()
    {
        return $this->order->getData('sibs_transaction_id');
    }

    /**
     * Get SIBS Payment Method
     *
     * @return string
     */
    public function getPaymentMethod()
    {
        return $this->order->getData('sibs_payment_method');
    }

    /**
     * Get SIBS Reference ID
     *
     * @return string
     */
    public function getReferenceId()
    {
        return $this->order->getData('sibs_ref_id');
    }

    /**
     * Get SIBS Payment Code
     *
     * @return string
     */
    public function getPaymentCode()
    {
        return $this->order->getData('sibs_payment_code');
    }

    /**
     * Get SIBS amount
     *
     * @return float
     */
    public function getAmount()
    {
        return $this->order->getData('sibs_amount');
    }

    /**
     * Get SIBS currency
     *
     * @return float
     */
    public function getCurrency()
    {
        return $this->order->getData('sibs_currency');
    }

    /**
     * Get SIBS payment type
     *
     * @return string
     */
    public function getPaymentType()
    {
        return $this->order->getData('sibs_payment_type');
    }

    /**
     * Get SIBS is a capture
     *
     * @return bool
     */
    public function getSibsIsCapture()
    {
        return $this->order->getData('sibs_is_capture');
    }

    /**
     * Get SIBS capture ID
     *
     * @return string
     */
    public function getCaptureId()
    {
        return $this->order->getData('sibs_capture_id');
    }

    /**
     * Get SIBS capture status
     *
     * @return string
     */
    public function getCaptureStatus()
    {
        return $this->order->getData('sibs_capture_status');
    }

    /**
     * Get SIBS refund id
     *
     * @return string
     */
    public function getRefundedId()
    {
        return $this->order->getData('sibs_refunded_id');
    }

    /**
     * Get SIBS form context
     *
     * @return string
     */
    public function getFormContext()
    {
        return $this->order->getData('sibs_form_context');
    }

    /**
     * Get SIBS transaction signature
     *
     * @return string
     */
    public function getTransactionSignature()
    {
        return $this->order->getData('sibs_transaction_signature');
    }

    /**
     * Is a SIBS Order
     *
     * @return bool
     */
    public function isSibsOrder()
    {
        return ! empty($this->getTransactionId());
    }

    /**
     * SIBS Order is ordered
     *
     * @return bool
     */
    public function isOrdered()
    {
        return ! empty($this->getTransactionId());
    }

    /**
     * SIBS Order is captured
     *
     * @return bool
     */
    public function isCaptured()
    {
        return ! empty($this->getCaptureId());
    }

    /**
     * SIBS Order is refunded
     *
     * @return bool
     */
    public function isRefunded()
    {
        return ! empty($this->getRefundedId());
    }

    /**
     * Sets the state for the order.
     *
     * @param string $state
     * @return $this
     */
    public function setState($state)
    {
        $this->order->setState($state);
        return $this;
    }

    /**
     * Sets the status for the order.
     *
     * @param string $status
     * @return $this
     */
    public function setStatus($status)
    {
        $this->order->setStatus($status);
        return $this;
    }

    /**
     * Sets the total paid for the order.
     *
     * @param float $totalPaid
     * @return $this
     */
    public function setTotalPaid($totalPaid)
    {
        $this->order->setTotalPaid($totalPaid);
        return $this;
    }

    /**
     * Set flag for order if it can sends new email to customer.
     *
     * @param bool $flag
     * @return $this
     */
    public function setCanSendNewEmailFlag($canSendNewEmailFlag)
    {
        $this->order->setCanSendNewEmailFlag($canSendNewEmailFlag);
        return $this;
    }

    /**
     * Sets the total amount refunded amount for the order.
     *
     * @param float $totalRefunded
     * @return $this
     */
    public function setTotalRefunded($totalRefunded)
    {
        $this->order->setTotalRefunded($totalRefunded);
        return $this;
    }

    /**
     * Sets SIBS Transaction ID.
     *
     * @param string $transactionId
     * @return $this
     */
    public function setTransactionId($transactionId)
    {
        $this->order->setData('sibs_transaction_id', $transactionId);
        return $this;
    }

    /**
     * Sets SIBS Payment Method.
     *
     * @param string $paymentMethod
     * @return $this
     */
    public function setPaymentMethod($paymentMethod)
    {
        $this->order->setData('sibs_payment_method', $paymentMethod);
        return $this;
    }

    /**
     * Sets SIBS Reference ID.
     *
     * @param string $refId
     * @return $this
     */
    public function setReferenceId($refId)
    {
        $this->order->setData('sibs_ref_id', $refId);
        return $this;
    }

    /**
     * Sets SIBS Payment Code.
     *
     * @param string $paymentCode
     * @return $this
     */
    public function setPaymentCode($paymentCode)
    {
        $this->order->setData('sibs_payment_code', $paymentCode);
        return $this;
    }

    /**
     * Sets SIBS amount.
     *
     * @param float $amount
     * @return $this
     */
    public function setAmount($amount)
    {
        $this->order->setData('sibs_amount', $amount);
        return $this;
    }

    /**
     * Sets SIBS currency.
     *
     * @param string $currency
     * @return $this
     */
    public function setCurrency($currency)
    {
        $this->order->setData('sibs_currency', $currency);
        return $this;
    }

    /**
     * Sets SIBS payment type.
     *
     * @param string $paymentType
     * @return $this
     */
    public function setPaymentType($paymentType)
    {
        $this->order->setData('sibs_payment_type', $paymentType);
        return $this;
    }

    /**
     * Sets SIBS is capture.
     *
     * @param string $sibsIsCapture
     * @return $this
     */
    public function setSibsIsCapture($sibsIsCapture)
    {
        $this->order->setData('sibs_is_capture', $sibsIsCapture);
        return $this;
    }

    /**
     * Sets SIBS capture ID.
     *
     * @param string $captureId
     * @return $this
     */
    public function setCaptureId($captureId)
    {
        $this->order->setData('sibs_capture_id', $captureId);
        return $this;
    }

    /**
     * Sets SIBS capture status.
     *
     * @param string $captureStatus
     * @return $this
     */
    public function setCaptureStatus($captureStatus)
    {
        $this->order->setData('sibs_capture_status', $captureStatus);
        return $this;
    }

    /**
     * Sets SIBS refunded ID.
     *
     * @param string $refundedId
     * @return $this
     */
    public function setRefundedId($refundedId)
    {
        $this->order->setData('sibs_refunded_id', $refundedId);
        return $this;
    }

    /**
     * Sets SIBS refunded Status.
     *
     * @param string $refundedStatus
     * @return $this
     */
    public function setRefundedStatus($refundedStatus)
    {
        $this->order->setData('sibs_refunded_status', $refundedStatus);
        return $this;
    }

    /**
     * Sets SIBS form context
     *
     * @param string $formContext
     * @return $this
     */
    public function setFormContext($formContext)
    {
        $this->order->setData('sibs_form_context', $formContext);
        return $this;
    }

    /**
     * Sets SIBS transaction Signature
     *
     * @param string $transactionSignature
     * @return $this
     */
    public function setTransactionSignature($transactionSignature)
    {
        $this->order->setData('sibs_transaction_signature', $transactionSignature);
        return $this;
    }

    /**
     * Add a comment to order.
     *
     * Different or default status may be specified.
     *
     * @param string $comment
     * @param bool|string $status
     * @return OrderStatusHistoryInterface
     * @deprecated 101.0.5
     * @see addCommentToStatusHistory
     */
    public function addCommentToStatusHistory($message, $status, $isVisibleOnFront)
    {
        return $this->order->addCommentToStatusHistory($message, $status, $isVisibleOnFront);
    }

    /**
     * Update Order Status.
     *
     * @param string $state
     * @param string $status
     * @param string $message
     */
    public function updateStatus($state, $status = null, $message = '')
    {
        $status = ! empty($status) ? $status : $state;

        $this->setState($state)
            ->setStatus($status)
            ->save();

        //get last order status
        $histories            = $this->getStatusHistories();
        $latestHistoryComment = array_pop($histories);
        $status               = ! empty($latestHistoryComment) ? $latestHistoryComment->getStatus() : null;

        //check if current status is different from previous status
        if ($status != $this->getStatus()) {
            $this->addComment($message, $this->getStatus(), 1);
        }
    }

    /**
     * Add Comment on order.
     *
     * @param string $message
     * @param string|bool $status
     * @param int $notify
     */
    public function addComment($message = '', $status = false, $notify = 0)
    {
        $isVisibleOnFront = true;
        $history          = $this->addCommentToStatusHistory($message, $status, $isVisibleOnFront);
        $history->setIsCustomerNotified($notify);
        $history->save();
    }

    /**
     * Make order.
     *
     * @param string $orderPaymentMethod
     * @param string $statusCode
     * @param string $paymentType
     * @param string $orderState
     * @param string $orderStatus
     * @param string $transactionID
     * @param string $amountValue
     * @param string $amountCurrency
     */
    public function makeOrder(
        $orderPaymentMethod, $statusCode, $paymentType,
        $orderState, $orderStatus, $transactionID,
        $amountValue, $amountCurrency
    ) {
        $this->setCanSendNewEmailFlag(true)
            ->updateStatus($orderState, $orderStatus);

        if ($orderStatus == SibsConstants::MAGENTO_STATE_CANCELED) {
            throw new SibsException('Your payment was declined');
        }

        //save order data
        if ($paymentType == SibsConstants::SIBS_PAYMENT_TYPE_PURS) {
            $this->setTotalPaid($amountValue);
        }

        $this->setPaymentMethod($orderPaymentMethod)
            ->setPaymentType($paymentType)
            ->setTransactionId($transactionID)
            ->setReferenceId($transactionID)
            ->setPaymentCode($statusCode)
            ->setAmount($amountValue)
            ->setCurrency($amountCurrency)
            ->save();
    }

    /**
     * Checkout.
     *
     * @param string $paymentMethod
     * @param string $transactionID
     * @param ?string $formContext
     * @param ?string $transactionSignature
     */
    public function checkout($paymentMethod, $transactionID, $formContext = null, $transactionSignature = null)
    {
        if (! empty($formContext)) {
            $this->setFormContext($formContext);
        }

        if (! empty($transactionSignature)) {
            $this->setTransactionSignature($transactionSignature);
        }

        $this->setTransactionId($transactionID)
            ->setReferenceId($transactionID)
            ->setPaymentMethod($paymentMethod)
            ->save();
    }

    /**
     * Change Status to Authorization.
     *
     * @param string $orderState
     * @param string $orderStatus
     */
    public function authorize($orderState, $orderStatus)
    {
        if (in_array($this->getStatus(), SibsConstants::SIBS_CAN_PURCHASE_STATUSES)) {
            $this->updateStatus($orderState, $orderStatus);

            SibsLog::info('Order #' . $this->getIncrementId() . " updated to (State: $orderState, Status: $orderStatus)");
        } else {
            SibsLog::warn('Order #' . $this->getIncrementId() . " cannot be updated to authorize anymore because actual Order has (State: " . $this->getState() . ", Status: " . $this->getStatus() . ")");
        }
    }

    /**
     * Change Status to Purchase.
     *
     * @param string $orderState
     * @param string $orderStatus
     * @param string $amountValue
     */
    public function purchase($orderState, $orderStatus, $amountValue)
    {
        if (in_array($this->getStatus(), SibsConstants::SIBS_CAN_PURCHASE_STATUSES)) {
            $this->updateStatus($orderState, $orderStatus);

            $this->setTotalPaid($amountValue)
                ->save();

            SibsLog::info('Order #' . $this->getIncrementId() . " updated to (State: $orderState, Status: $orderStatus, Total Paid: $amountValue)");
        } else {
            SibsLog::warn('Order #' . $this->getIncrementId() . " cannot be updated to purchase anymore because actual Order has (State: " . $this->getState() . ", Status: " . $this->getStatus() . ")");
        }
    }

    /**
     * Change Status to Capture.
     *
     * @param string $orderState
     * @param string $orderStatus
     * @param string $captureStatus
     * @param string $amountValue
     * @param string $transactionID
     */
    public function capture($orderState, $orderStatus, $captureStatus, $amountValue, $transactionID)
    {
        if (in_array($this->getStatus(), SibsConstants::SIBS_CAN_CAPTURE_STATUSES)) {
            $this->updateStatus($orderState, $orderStatus);

            $this->setTotalPaid($amountValue)
                ->setCaptureStatus($captureStatus)
                ->setCaptureId($transactionID)
                ->save();

                SibsLog::info('Order #' . $this->getIncrementId() . " updated to (State: $orderState, Status: $orderStatus, Total Paid: $amountValue, Capture Status: $captureStatus, Capture ID: $transactionID)");
        } else {
            SibsLog::warn('Order #' . $this->getIncrementId() . " cannot be updated to capture anymore because actual Order has (State: " . $this->getState() . ", Status: " . $this->getStatus() . ")");
        }
    }

    /**
     * Update Capture Status.
     *
     * @param string $orderState
     * @param string $orderStatus
     * @param string $captureStatus
     * @param ?string $transactionID
     */
    public function updateCaptureStatus($orderState, $orderStatus, $captureStatus, $transactionID = null)
    {
        $this->updateStatus($orderState, $orderStatus);

        if (! empty($transactionID)) {
            $this->setCaptureId($transactionID);
        }

        $this->setCaptureStatus($captureStatus)
            ->save();

        $this->addComment('Order has a holded Capture', false, 0);
    }

    /**
     * Update Status to Refund.
     *
     * @param string $orderState
     * @param string $orderStatus
     * @param string $refundStatus
     * @param string $amountValue
     * @param string $transactionID
     */
    public function refund($orderState, $orderStatus, $refundStatus, $amountValue, $transactionID)
    {
        if (in_array($this->getStatus(), SibsConstants::SIBS_CAN_REFUND_STATUSES)) {
            $this->updateStatus($orderState, $orderStatus);

            $this->setTotalPaid(floatval('0'))
                ->setTotalRefunded($amountValue)
                ->setRefundedStatus($refundStatus)
                ->setRefundedId($transactionID)
                ->save();

            $this->updateStockForRefundedOrders();
            $this->addComment('Order Fully Refunded', false, 0);

            SibsLog::info('Order #' . $this->getIncrementId() . " updated to (State: $orderState, Status: $orderStatus, Total Paid: $amountValue, Refund Status: $refundStatus, Refund ID: $transactionID)");
        } else {
            SibsLog::warn('Order #' . $this->getIncrementId() . " cannot be updated to refund anymore because actual Order has (State: " . $this->getState() . ", Status: " . $this->getStatus() . ")");
        }
    }

    /**
     * Update Refund Status.
     *
     * @param string $orderState
     * @param string $orderStatus
     * @param string $refundStatus
     * @param ?string $transactionID
     */
    public function updateRefundStatus($orderState, $orderStatus, $refundStatus, $transactionID = null)
    {
        $this->updateStatus($orderState, $orderStatus);

        if (! empty($transactionID)) {
            $this->setRefundedId($transactionID);
        }

        $this->setRefundedStatus($refundStatus)
            ->save();

        $this->addComment('Order has a holded Refund', false, 0);
    }

    /**
     * Update Stock for Refunded Orders.
     *
     * @return bool
     */
    public function updateStockForRefundedOrders()
    {
        try {
            foreach ($this->getAllItems() as $item) {
                if ($item->getProductType() == 'simple') {
                    SibsLog::info('******* RESTOCK SIBS *************');
                    SibsLog::info('getName: ' . $item->getName());
                    SibsLog::info('getProductId: ' . $item->getProductId());
                    SibsLog::info('getPrice: ' . $item->getPrice());
                    SibsLog::info('getSku: ' . $item->getSku());
                    SibsLog::info('getQtyOrdered: ' . $item->getQtyOrdered());
                    SibsLog::info('product_type: ' . $item->getProductType());
                    SibsLog::info('**********************************');

                    $cataloginventory_stock_item   = SibsDB::getTableName('cataloginventory_stock_item');
                    $cataloginventory_stock_status = SibsDB::getTableName('cataloginventory_stock_status');

                    $quantity   = $item->getQtyOrdered();
                    $product_id = $item->getProductId();

                    $sql = "UPDATE `$cataloginventory_stock_item` AS `item_stock`, `$cataloginventory_stock_status` AS `status_stock`
                                SET `item_stock`.`qty` = (`item_stock`.`qty` + $quantity),
                                    `item_stock`.`is_in_stock` = IF($quantity > 0, 1, 0),
                                    `status_stock`.`qty` = (`status_stock`.`qty` + $quantity),
                                    `status_stock`.`stock_status` = IF($quantity > 0, 1, 0)
                            WHERE `item_stock`.`product_id` = '$product_id'
                                AND `item_stock`.`product_id` = `status_stock`.`product_id`";

                    SibsLog::info("RESTOCK SIBS - query of update: $sql");
                    SibsDB::query($sql);
                }
            }

            $indexerFactory = SibsDB::get("Magento\Indexer\Model\IndexerFactory");
            $indexerIds     = [
                'catalog_category_product',
                'catalog_product_category',
                'catalog_product_price',
                'catalog_product_attribute',
                'cataloginventory_stock',
                'catalogrule_product',
                'catalogsearch_fulltext',
            ];

            foreach ($indexerIds as $indexerId) {
                try {
                    SibsLog::info("RESTOCK SIBS - Create index: $indexerId");
                    $indexer = $indexerFactory->create();
                    $indexer->load($indexerId);
                    $indexer->reindexAll();
                } catch (Exception $e) {
                    SibsLog::warn("RESTOCK SIBS - Error reindexing (Id:$indexerId): " . $e->getMessage());
                }
            }
        } catch (Exception $e) {
            SibsLog::err('RESTOCK SIBS - Error: ' . $e->getMessage());

            return false;
        }

        return true;
    }
}
