<?php

namespace App\Traits;

use App\Helpers\DateHelper;
use Laravel\Cashier\Billable;
use App\Helpers\InstanceHelper;
use App\Exceptions\StripeException;
use Illuminate\Support\Facades\Log;

trait Subscription
{
    use Billable;

    /**
     * Process the upgrade payment.
     *
     * @param string $payment_method
     * @param string $planName
     * @return bool|string
     */
    public function subscribe(string $payment_method, string $planName)
    {
        $plan = InstanceHelper::getPlanInformationFromConfig($planName);

        return $this->stripeCall(function () use ($payment_method, $plan) {
            $this->newSubscription($plan['name'], $plan['id'])
                        ->create($payment_method, [
                            'email' => auth()->user()->email,
                        ]);

            return true;
        });
    }

    /**
     * Check if the account is currently subscribed to a plan.
     *
     * @return bool
     */
    public function isSubscribed()
    {
        if ($this->has_access_to_paid_version_for_free) {
            return true;
        }

        return $this->subscribed(config('monica.paid_plan_monthly_friendly_name'))
            || $this->subscribed(config('monica.paid_plan_annual_friendly_name'));
    }

    /**
     * Get the subscription the account is subscribed to.
     *
     * @return \Laravel\Cashier\Subscription|null
     */
    public function getSubscribedPlan()
    {
        $subscription = $this->subscription(config('monica.paid_plan_monthly_friendly_name'));

        if (! $subscription) {
            $subscription = $this->subscription(config('monica.paid_plan_annual_friendly_name'));
        }

        return $subscription;
    }

    /**
     * Get the id of the plan the account is subscribed to.
     *
     * @return string
     */
    public function getSubscribedPlanId()
    {
        $plan = $this->getSubscribedPlan();

        if (! is_null($plan)) {
            return $plan->stripe_plan;
        }

        return '';
    }

    /**
     * Get the friendly name of the plan the account is subscribed to.
     *
     * @return string|null
     */
    public function getSubscribedPlanName(): ?string
    {
        $plan = $this->getSubscribedPlan();

        return is_null($plan) ? null : $plan->name;
    }

    /**
     * Cancel the plan the account is subscribed to.
     *
     * @return bool|string
     */
    public function subscriptionCancel()
    {
        $plan = $this->getSubscribedPlan();

        if (! is_null($plan)) {
            return $this->stripeCall(function () use ($plan) {
                $plan->cancelNow();

                return true;
            });
        }

        return false;
    }

    /**
     * Check if the account has invoices linked to this account.
     *
     * @return bool
     */
    public function hasInvoices()
    {
        return $this->subscriptions()->count() > 0;
    }

    /**
     * Get the next billing date for the account.
     *
     * @return string
     */
    public function getNextBillingDate()
    {
        // Weird method to get the next billing date from Laravel Cashier
        // see https://stackoverflow.com/questions/41576568/get-next-billing-date-from-laravel-cashier
        return $this->stripeCall(function () {
            $subscriptions = $this->asStripeCustomer()['subscriptions'];
            if (! $subscriptions || count($subscriptions->data) <= 0) {
                return '';
            }
            $timestamp = $subscriptions->data[0]['current_period_end'];

            return DateHelper::getFullDate($timestamp);
        });
    }

    /**
     * Call stripe.
     *
     * @param callable $callback
     * @return mixed
     */
    private function stripeCall($callback)
    {
        try {
            return $callback();
        } catch (\Stripe\Exception\CardException $e) {
            // Since it's a decline, \Stripe\Error\Card will be caught
            $body = $e->getJsonBody();
            $err = $body['error'];
            $errorMessage = trans('settings.stripe_error_card', ['message' => $err['message']]);
            Log::error('Stripe card decline error: '.(string) $e, $e->getJsonBody() ?: []);
        } catch (\Stripe\Exception\RateLimitException $e) {
            // Too many requests made to the API too quickly
            $errorMessage = trans('settings.stripe_error_rate_limit');
            Log::error('Stripe RateLimit error: '.(string) $e, $e->getJsonBody() ?: []);
        } catch (\Stripe\Exception\InvalidRequestException $e) {
            // Invalid parameters were supplied to Stripe's API
            $errorMessage = trans('settings.stripe_error_invalid_request');
            Log::error('Stripe InvalidRequest error: '.(string) $e, $e->getJsonBody() ?: []);
        } catch (\Stripe\Exception\AuthenticationException $e) {
            // Authentication with Stripe's API failed
            // (maybe you changed API keys recently)
            $errorMessage = trans('settings.stripe_error_authentication');
            Log::error('Stripe Authentication error: '.(string) $e, $e->getJsonBody() ?: []);
        } catch (\Stripe\Exception\ApiConnectionException $e) {
            // Network communication with Stripe failed
            $errorMessage = trans('settings.stripe_error_api_connection_error');
            Log::error('Stripe ApiConnection error: '.(string) $e, $e->getJsonBody() ?: []);
        } catch (\Stripe\Exception\ApiErrorException $e) {
            $errorMessage = $e->getMessage();
            Log::error('Stripe error: '.(string) $e, $e->getJsonBody() ?: []);
        } catch (\Exception $e) {
            $errorMessage = $e->getMessage();
            Log::error('Stripe error: '.(string) $e);
        }

        throw new StripeException($errorMessage);
    }
}
