<?php

namespace Tobuli\Helpers\Payments\Gateways;

use App\Exceptions\PaymentsUnavailableException;
use App\Exceptions\PaymentsConfigurationException;
use App\Exceptions\PaymentsIssueException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
use Stripe\Balance;
use Stripe\Charge;
use Stripe\Customer;
use Stripe\Error\Card;
use Stripe\Plan;
use Stripe\Stripe;
use Stripe\Subscription;

class StripeGateway extends PaymentGateway implements PaymentGatewayInterface
{
    private $config;
    private $currency;

    public function __construct()
    {
        $this->config = settings('payments.stripe');
        $this->currency = strtolower($this->config['currency']);
        Stripe::setApiKey($this->config['secret_key']);
    }

    public function pay($user, $plan)
    {
        try {
            $customer = $this->createCustomer($user, request('stripeToken'));

            $charge = Charge::create([
                'customer' => $customer->id,
                'amount'   => $plan->price * 100,
                'currency' => $this->currency,
            ]);
        } catch (Card $e) {
            $body = $e->getJsonBody();
            throw new PaymentsIssueException($body['error']['message']);
        }

        $this->storeSubscription($user, $plan, $charge->id);

        return Redirect::route('payments.pay_callback', [
            'gateway'   => $this->gatewayName(),
            'charge_id' => $charge->id,
        ]);
    }

    public function payCallback(Request $request)
    {
        try {
            $retrieve = Charge::retrieve($request->charge_id);
        } catch (Card $e) {
            $body = $e->getJsonBody();
            throw new PaymentsIssueException($body['error']['message']);
        }

        $this->activateSubscription($request->charge_id);

        return Redirect::route('payments.success');
    }

    public function subscribe($user, $plan)
    {
        if ( ! empty($this->config['one_time_payment']))
            $this->pay($user, $plan);

        try {
            if ( ! $stripe_plan = $this->existingPlan($plan)) {
                $stripe_plan = $this->createPlan($plan);
            }

            $customer = $this->createCustomer($user, request('stripeToken'));

            $subscription = Subscription::create([
                'customer' => $customer->id,
                'items'    => [
                    [
                        'plan' => $stripe_plan,
                    ],
                ],
                'payment_behavior' => 'error_if_incomplete'
            ]);

        } catch (Card $e) {
            $body = $e->getJsonBody();
            throw new PaymentsIssueException($body['error']['message']);
        }

        $this->storeSubscription($user, $plan, $subscription->id);

        return Redirect::route('payments.subscribe_callback', [
            'gateway'         => $this->gatewayName(),
            'subscription_id' => $subscription->id,
        ]);
    }

    public function subscribeCallback(Request $request)
    {
        try {
            $subscription = Subscription::retrieve($request->subscription_id);
        } catch (Card $e) {
            $body = $e->getJsonBody();
            throw new PaymentsIssueException($body['error']['message']);
        }

        if ($subscription->status === Subscription::STATUS_PAST_DUE)
            $subscription->cancel();

        if ($subscription->status !== Subscription::STATUS_ACTIVE)
            throw new PaymentsUnavailableException();

        $this->activateSubscription($subscription->id);

        return Redirect::route('payments.success');
    }

    public function checkout($plan_id)
    {
        return view('front::Subscriptions.Gateways.stripe')->with([
            'plan_id'    => $plan_id,
            'gateway'    => $this->gatewayName(),
            'public_key' => $this->config['public_key'],
        ]);
    }

    public function isConfigCorrect(Request $request)
    {
        try {
            Stripe::setApiKey($request->secret_key);
            Plan::create([
                'amount'   => 1,
                'currency' => $request->currency,
                'interval' => 'day',
                'product'  => ['name' => 'test'],
            ]);
            Balance::retrieve();
        } catch (\Exception $e) {
            throw new PaymentsConfigurationException($e->getMessage());
        }

        return true;
    }

    public function isSubscriptionActive($subscription)
    {
        try {
            $subscription = Subscription::retrieve($subscription->gateway_id);
        } catch (\Exception $e) {
            return false;
        }

        if ($subscription->plan->active != true)
            return false;

        return true;
    }

    private function createCustomer($user, $token)
    {
        return Customer::create([
            'email'  => $user->email,
            'source' => $token,
        ]);
    }

    private function existingPlan($plan)
    {
        try {
            return Plan::retrieve($this->planID($plan));
        } catch (\Exception $e) {
            return null;
        }
    }

    private function createPlan($plan)
    {
        return Plan::create([
            'id'       => $this->planID($plan),
            'amount'   => $plan->price * 100,
            'currency' => $this->currency,
            'interval' => substr_replace($plan->duration_type, '', -1),
            'product'  => [
                'name' => $plan->title,
            ],
        ]);
    }

    private function planID($plan)
    {
        return md5("$plan->id:$plan->price:$plan->duration_type:$plan->duration_value");
    }

    public function cancelSubscription($subscription)
    {
        try {
            Subscription::retrieve($subscription->gateway_id)->cancel();
        } catch (\Exception $exception) {
            return false;
        }

        return true;
    }
}