<?php

namespace QUI\ERP\Payments\Stripe\PaymentMethods\Recurring;

use QUI;
use QUI\ERP\Accounting\Invoice\Invoice;
use QUI\ERP\Order\AbstractOrder;
use QUI\ERP\Payments\Stripe\AbstractBasePayment;
use Stripe\Exception\ApiErrorException;
use Stripe\PaymentIntent as StripePaymentIntent;
use QUI\ERP\Accounting\Payments\Types\RecurringPaymentInterface;
use QUI\ERP\Payments\Stripe\Provider;
use Stripe\Subscription as StripeSubscription;
use QUI\ERP\Payments\Stripe\Utils;

/**
 * Class BaseRecurringPayment
 *
 * Stripe payment for recurring payments
 */
abstract class AbstractBaseRecurringPayment extends AbstractBasePayment implements RecurringPaymentInterface
{
    /**
     * Return attributes for creating a PaymentIntent
     *
     * @param AbstractOrder $Order
     * @param string $paymentMethodId - Stripe PaymentMethod ID
     * @return StripePaymentIntent
     *
     * @throws ApiErrorException
     * @throws QUI\Exception
     */
    protected function createPaymentIntentForOrder(AbstractOrder $Order, $paymentMethodId)
    {
        return StripePaymentIntent::create([
            'payment_method'      => $paymentMethodId,
            'amount'              => Utils::getCentAmount($Order),
            'currency'            => mb_strtolower($Order->getCurrency()->getCode()),
            'confirmation_method' => 'manual',
            'confirm'             => true,
            'setup_future_usage'  => 'off_session',
            'use_stripe_sdk'      => true,
            'description'         => Utils::getPaymentDescriptionForOrder($Order)
        ]);
    }

    /**
     * Create a Scubscription from a (temporary) Order
     *
     * @param AbstractOrder $Order
     * @return string - Subscription ID
     *
     * @throws \Exception
     */
    public function createSubscription(AbstractOrder $Order)
    {
        return Subscriptions::createSubscription($Order);
    }

    /**
     * Capture subscription amount based on an Invoice
     *
     * @param Invoice $Invoice
     * @return void
     *
     * @throws \Exception
     */
    public function captureSubscription(Invoice $Invoice)
    {
        Subscriptions::billSubscriptionBalance($Invoice);
    }

    /**
     * Cancel a Subscription
     *
     * @param int|string $subscriptionId
     * @param string $reason (optional) - The reason why the subscription is cancelled
     * @return void
     *
     * @throws \Exception
     */
    public function cancelSubscription($subscriptionId, $reason = '')
    {
        Subscriptions::cancelSubscription($subscriptionId, $reason);
    }

    /**
     * Sets a subscription as inactive (on the side of this QUIQQER system only!)
     *
     * IMPORTANT: This does NOT mean that the corresponding subscription at the payment provider
     * side is cancelled. If you want to do this please use cancelSubscription() !
     *
     * @param $subscriptionId
     * @return void
     */
    public function setSubscriptionAsInactive($subscriptionId)
    {
        Subscriptions::setSubscriptionAsInactive($subscriptionId);
    }

    /**
     * Can the Subscription of this payment method be edited
     * regarding essential data like invoice frequency, amount etc.?
     *
     * @return bool
     */
    public function isSubscriptionEditable()
    {
        return false;
    }

    /**
     * Check if a Subscription is associated with an order and
     * return its ID (= identification at the payment method side; e.g. PayPal)
     *
     * @param AbstractOrder $Order
     * @return int|string|false - ID or false of no ID associated
     */
    public function getSubscriptionIdByOrder(AbstractOrder $Order)
    {
        try {
            $result = QUI::getDataBase()->fetch([
                'select' => ['stripe_id'],
                'from'   => Provider::getStripeBillingSubscriptionsTable(),
                'where'  => [
                    'global_process_id' => $Order->getHash()
                ]
            ]);
        } catch (\Exception $Exception) {
            QUI\System\Log::writeException($Exception);
            return false;
        }

        if (empty($result)) {
            return false;
        }

        return $result[0]['stripe_id'];
    }

    /**
     * Checks if the subscription is active at the payment provider side
     *
     * @param string|int $subscriptionId
     * @return bool
     */
    public function isSubscriptionActiveAtPaymentProvider($subscriptionId)
    {
        try {
            $data = Subscriptions::getSubscriptionDetails($subscriptionId);
        } catch (\Exception $Exception) {
            QUI\System\Log::writeException($Exception);
            return true;
        }

        if (empty($data)) {
            QUI\System\Log::addError(
                'Stripe subscription details are empty (ID #'.$subscriptionId.'). Please check manually.'
            );

            return true;
        }

        return $data['status'] === StripeSubscription::STATUS_ACTIVE;
    }

    /**
     * Checks if the subscription is active at QUIQQER
     *
     * @param string|int $subscriptionId - Payment provider subscription ID
     * @return bool
     */
    public function isSubscriptionActiveAtQuiqqer($subscriptionId)
    {
        try {
            $result = QUI::getDataBase()->fetch([
                'select' => ['active'],
                'from'   => Provider::getStripeBillingSubscriptionsTable(),
                'where'  => [
                    'stripe_id' => $subscriptionId
                ]
            ]);
        } catch (\Exception $Exception) {
            QUI\System\Log::writeException($Exception);
            return true;
        }

        if (empty($result)) {
            return false;
        }

        return !empty($result[0]['active']);
    }

    /**
     * Get IDs of all subscriptions
     *
     * @param bool $includeInactive (optional) - Include inactive subscriptions [default: false]
     * @return int[]
     */
    public function getSubscriptionIds($includeInactive = false)
    {
        $where = [];

        if (empty($includeInactive)) {
            $where['active'] = 1;
        }

        try {
            $result = QUI::getDataBase()->fetch([
                'select' => ['stripe_id'],
                'from'   => Provider::getStripeBillingSubscriptionsTable(),
                'where'  => $where
            ]);
        } catch (\Exception $Exception) {
            QUI\System\Log::writeException($Exception);
            return [];
        }

        return \array_column($result, 'stripe_id');
    }

    /**
     * Get global processing ID of a subscription
     *
     * @param string|int $subscriptionId
     * @return string|false
     */
    public function getSubscriptionGlobalProcessingId($subscriptionId)
    {
        try {
            $result = QUI::getDataBase()->fetch([
                'select' => ['global_process_id'],
                'from'   => Provider::getStripeBillingSubscriptionsTable(),
                'where'  => [
                    'stripe_id' => $subscriptionId
                ]
            ]);
        } catch (\Exception $Exception) {
            QUI\System\Log::writeException($Exception);
            return false;
        }

        if (empty($result)) {
            return false;
        }

        return $result[0]['global_process_id'];
    }

    /**
     * If the Payment method is a payment gateway, it can return a gateway display
     *
     * @param AbstractOrder $Order
     * @param QUI\ERP\Order\Controls\OrderProcess\Processing $Step
     * @return string
     *
     * @throws QUI\Exception
     */
    public function getGatewayDisplay(AbstractOrder $Order, $Step = null)
    {
        $type = $this->getPaymentMethodType();

        $Control = new PaymentDisplay([
            'paymentMethod' => $type,
            'infoText'      => $this->getPaymentStepInfo()
        ]);

        $Control->setAttribute('Order', $Order);

        $stepTitle = $this->getPaymentStepTitle();
        $Step->setTitle($stepTitle);

        $Engine = QUI::getTemplateManager()->getEngine();

        $Engine->assign([
            'stepTitle' => $stepTitle
        ]);

        $Step->setContent($Engine->fetch(dirname(__FILE__, 3).'/PaymentDisplay.Header.html'));

        return $Control->create();
    }

    /**
     * Does the payment ONLY support recurring payments (e.g. for subscriptions)?
     *
     * @return bool
     */
    public function supportsRecurringPaymentsOnly()
    {
        return true;
    }
}