<?php

namespace App\Http\Controllers;

use App\Repos\CreditRepo;
use App\Repos\PlanRepo;
use App\Repos\SubscriptionRepo;
use App\Repos\UserRepo;
use App\Services\ResponseService;
use App\Services\StripeService;
use App\Services\ValidationService;
use Illuminate\Http\Request;
use Throwable;

class StripeController extends Controller
{
    public function checkout(Request $request)
    {
        try {
            $rules = [
                'priceId' => ['required', 'string'],
            ];
            ValidationService::validate($request->all(), $rules);
            $user = $request->user();
            if (!$user->stripe_id) {
                $customer = StripeService::createCustomer($user->name, $user->email);
                $user = UserRepo::updateByIdOrFail(
                    $user->id,
                    ['stripe_id' => $customer->id]
                );
            }
            $activeSubscription = $user->activeSubscription;
            if ($activeSubscription) {
                StripeService::cancelSubscription($activeSubscription->stripe_id);
            }
            $url = StripeService::createSession($user->stripe_id, $request->priceId);
            $result = compact('url');
            return ResponseService::ok($result);
        } catch (Throwable $error) {
            return ResponseService::handleError($error);
        }
    }

    public function webhook(Request $request)
    {
        try {
            $sigHeader = $request->header('stripe-signature');
            $event = StripeService::webhookEvent($request->getContent(), $sigHeader);
            switch ($event->type) {
                case 'invoice.paid':
                    $invoice = $event->data->object;
                    $subscription = StripeService::retriveSubscription($invoice->subscription);
                    $user = UserRepo::findByStripeIdOrFail($subscription->customer);
                    $price = $subscription->items->data[0]->price;
                    $plan = PlanRepo::findByStripePriceIdOrFail($price->id);
                    UserRepo::updateByIdOrFail($user->id, [
                        'plan_id' => $plan->id,
                    ]);
                    CreditRepo::updateById($user->credit->id, [
                        'public_page' => $plan->public_page_limit,
                        'page_creation' => $plan->page_creation_limit,
                    ]);
                    SubscriptionRepo::createOrUpdateByStripeId($subscription->id, [
                        'user_id' => $user->id,
                        'plan_id' => $plan->id,
                        'stripe_id' => $subscription->id,
                        'price' => $price->unit_amount / 100,
                        'expire_at' => $subscription->current_period_end,
                    ]);
                    break;
                case 'customer.subscription.deleted':
                    $subscription = $event->data->object;
                    $user = UserRepo::findByStripeIdOrFail($subscription->customer);
                    SubscriptionRepo::updateByStripeIdOrFail($subscription->id, [
                        'canceled_at' => now(),
                        'expire_at' => now(),
                    ]);
                    $plan = PlanRepo::firstByNameOrFail('free');
                    UserRepo::updateByIdOrFail($user->id, [
                        'plan_id' => $plan->id,
                    ]);
                    CreditRepo::updateById($user->credit->id, [
                        'public_page' => 0,
                        'page_creation' => 0,
                    ]);
                    break;
            }
            $result = ['handled' => true];
            return ResponseService::ok($result);
        } catch (Throwable $error) {
            return ResponseService::handleError($error);
        }
    }

    public function cancelSubscription(Request $request)
    {
        try {
            $user = $request->user();
            StripeService::cancelSubscription($user->subscription->stripe_id);
            $result = ['canceled' => true];
            return ResponseService::ok($result);
        } catch (Throwable $error) {
            return ResponseService::handleError($error);
        }
    }
}
