<?php


namespace YooMoney\Model;

use pm_yoomoney;
use YooKassa\Client;
use YooKassa\Model\PaymentInterface;
use YooKassa\Model\PaymentStatus;
use YooKassa\Model\Receipt\PaymentMode;
use YooKassa\Model\ReceiptCustomer;
use YooKassa\Model\ReceiptItem;
use YooKassa\Model\ReceiptType;
use YooKassa\Model\Settlement;
use YooKassa\Request\Receipts\CreatePostReceiptRequest;
use YooKassa\Request\Receipts\ReceiptResponseInterface;
use YooKassa\Request\Receipts\ReceiptResponseItemInterface;
use YooMoney\Helpers\LoggerHelper;

class KassaSecondReceiptModel
{
    /**
     * @var array
     */
    private $orderInfo = array(
        'orderId' => 0,
        'user_email' => '',
        'user_phone' => '',
    );

    /**
     * @var PaymentInterface
     */
    private $paymentInfo;

    /**
     * @var string
     */
    private $settlementsSum;

    /**
     * @var Client
     */
    private $client;

    /**
     * @var LoggerHelper
     */
    private $logger;

    /**
     * KassaSecondReceiptModel constructor.
     * @param $paymentInfo
     * @param $orderInfo
     * @param $client Client
     */
    public function __construct($paymentInfo, $orderInfo, $client)
    {
        $this->orderInfo   = $orderInfo;
        $this->paymentInfo = $paymentInfo;
        $this->client      = $client;
    }

    /**
     * @return bool
     */
    public function sendSecondReceipt()
    {
        $this->getLogger()->sendHeka(array('second-receipt.webhook.init'));
        $this->log("info", "Hook send second receipt");

        if (!$this->isPaymentInfoValid($this->paymentInfo)) {
            $this->getLogger()->sendHeka(array('second-receipt.webhook.skip'));
            $this->log("error", "Invalid paymentInfo");
            return false;
        }

        if (empty($this->orderInfo['user_email']) || empty($this->orderInfo['user_phone'])) {
            $this->getLogger()->sendHeka(array('second-receipt.webhook.skip'));
            $this->log("error", "Invalid orderInfo orderId = " . $this->orderInfo['order_id']);
            return false;
        }

        $receiptRequest = $this->buildSecondReceipt($this->getLastReceipt($this->paymentInfo->getId()), $this->paymentInfo, $this->orderInfo);
        if ($receiptRequest === null) {
            $this->getLogger()->sendHeka(array('second-receipt.webhook.fail'));
            return false;
        }

        $this->log("info", "Second receipt request data: " . json_encode($receiptRequest->toArray()));

        try {
            $this->getLogger()->sendHeka(array('second-receipt.send.init'));
            $response = $this->client->createReceipt($receiptRequest);
            $this->getLogger()->sendHeka(array('second-receipt.send.success'));
        } catch (\Exception $e) {
            $this->log("error", "Request second receipt error: " . $e->getMessage());
            $this->getLogger()->sendAlertLog('Request second receipt error', array(
                'methodid' => 'POST/sendSecondReceipt',
                'exception' => $e,
            ), array('second-receipt.send.fail', 'second-receipt.webhook.fail'));
            return false;
        }

        $this->log("info", "Request second receipt result: " . json_encode($response->toArray()));
        $this->generateSettlementsAmountSum($response);
        $this->getLogger()->sendHeka(array('second-receipt.webhook.success'));
        return true;
    }

    /**
     * @return string
     */
    public function getSettlementsSum()
    {
        return $this->settlementsSum;
    }

    /**
     * @param ReceiptResponseInterface $response
     */
    private function generateSettlementsAmountSum($response)
    {
        $amount = 0;

        foreach ($response->getSettlements() as $settlement) {
            $amount += $settlement->getAmount()->getIntegerValue();
        }

        $this->settlementsSum = $amount / 100.0;
    }

    /**
     * @param ReceiptResponseInterface $lastReceipt
     * @param PaymentInterface $paymentInfo
     * @param $orderInfo
     *
     * @return void|CreatePostReceiptRequest
     */
    private function buildSecondReceipt($lastReceipt, $paymentInfo, $orderInfo)
    {
        $this->getLogger()->sendHeka(array('second-receipt.create.init'));

        if ($lastReceipt instanceof ReceiptResponseInterface) {
            if ($lastReceipt->getType() === "refund") {
                $this->getLogger()->sendHeka(array('second-receipt.create.skip'));
                return;
            }

            $resendItems = $this->getResendItems($lastReceipt->getItems());

            if (!count($resendItems['items'])) {
                $this->log("info", "Second receipt isn't need");
                $this->getLogger()->sendHeka(array('second-receipt.create.skip'));
                return;
            }

            try {
                $receiptBuilder = CreatePostReceiptRequest::builder();
                $customer = $this->getReceiptCustomer($this->orderInfo);

                if (empty($customer)) {
                    $this->log("error", "Need customer phone or email for second receipt");
                    $this->getLogger()->sendHeka(array('second-receipt.create.fail'));
                    return;
                }

                $receiptBuilder->setObjectId($paymentInfo->getId())
                    ->setType(ReceiptType::PAYMENT)
                    ->setItems($resendItems['items'])
                    ->setSettlements(
                        array(
                            new Settlement(
                                array(
                                    'type' => 'prepayment',
                                    'amount' => array(
                                        'value' => $resendItems['amount'],
                                        'currency' => 'RUB',
                                    ),
                                )
                            ),
                        )
                    )
                    ->setCustomer($customer)
                    ->setSend(true);

                if ($lastReceipt->getTaxSystemCode()) {
                    $receiptBuilder->setTaxSystemCode($lastReceipt->getTaxSystemCode());
                }

                $receipt = $receiptBuilder->build();
                $this->getLogger()->sendHeka(array('second-receipt.create.success'));
                return $receipt;
            } catch (\Exception $e) {
                $this->log("error", $e->getMessage() . ". Property name:". $e->getProperty());
                $this->getLogger()->sendAlertLog('Build SecondReceipt error', array(
                    'methodid' => 'POST/buildSecondReceipt',
                    'exception' => $e,
                ), array('second-receipt.create.fail'));
            }
        } else {
            $this->log("info", "Second receipt isn't need");
        }
    }

    /**
     * @param PaymentInterface $paymentInfo
     * @return bool
     */
    private function isPaymentInfoValid($paymentInfo)
    {
        if (empty($paymentInfo)) {
            $this->log("error", "Fail send second receipt paymentInfo is null: " . print_r($paymentInfo, true));
            return false;
        }

        if ($paymentInfo->getStatus() !== PaymentStatus::SUCCEEDED) {
            $this->log("error", "Fail send second receipt payment have incorrect status: " . $paymentInfo->getStatus());
            return false;
        }

        return true;
    }

    /**
     * @param $orderInfo
     * @return ReceiptCustomer
     */
    private function getReceiptCustomer($orderInfo)
    {
        $customerData = array();

        if (!empty($orderInfo['user_email'])) {
            $customerData['email'] = $orderInfo['user_email'];
        }

        if (!empty($orderInfo['user_phone'])) {
            $customerData['phone'] = $orderInfo['user_phone'];
        }

        return new ReceiptCustomer($customerData);
    }

    /**
     * @param $paymentId
     * @return mixed|ReceiptResponseInterface
     */
    private function getLastReceipt($paymentId)
    {
        try {
            $receipts = $this->client->getReceipts(array(
                'payment_id' => $paymentId,
            ))->getItems();
        } catch (\Exception $e) {
            $this->log("error", "Fail get receipt message: " . $e->getMessage());
        }

        return array_shift($receipts);
    }

    /**
     * @param ReceiptResponseItemInterface[] $items
     *
     * @return array
     */
    private function getResendItems($items)
    {
        $resendItems = array(
            'items'  => array(),
            'amount' => 0,
        );

        foreach ($items as $item) {
            if ($item->getPaymentMode() === PaymentMode::FULL_PREPAYMENT) {
                $item->setPaymentMode(PaymentMode::FULL_PAYMENT);
                $resendItems['items'][] = new ReceiptItem($item->jsonSerialize());
                $resendItems['amount'] += $item->getAmount() / 100.0;
            }
        }

        return $resendItems;
    }

    /**
     * Создание записи в лог.
     *
     * @param string $level
     * @param string $message
     * @param array $context
     *
     * @return void
     */
    public function log($level, $message, $context = array())
    {
        $this->getLogger()->log($level, $message, $context);
    }

    private function getLogger()
    {
        if (!$this->logger) {
            $pm_yoomoney = new pm_yoomoney();
            $this->logger = $pm_yoomoney->getLogger();
        }

        return $this->logger;
    }
}
