<?php

namespace App\Listeners;

use App\Jobs\SendPaymentFailedReminderJob;
use App\Models\User;
use App\Models\UserNotificationPreference;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Laravel\Cashier\Events\WebhookReceived;

final class HandleStripeWebhook
{
    public function handle(WebhookReceived $event): void
    {
        $type = $event->payload['type'] ?? null;
        $stripeCustomerId = $event->payload['data']['object']['customer'] ?? null;

        if ($stripeCustomerId === null) {
            return;
        }

        $user = User::where('stripe_id', $stripeCustomerId)->first();

        if ($user === null) {
            return;
        }

        match ($type) {
            'customer.subscription.created',
            'customer.subscription.updated' => $this->bustPlanCache($user),
            'customer.subscription.deleted' => $this->handleSubscriptionDeleted($user),
            'invoice.payment_succeeded' => $this->handlePaymentSucceeded($user),
            'invoice.payment_failed' => $this->handlePaymentFailed($user),
            default => null,
        };
    }

    private function handleSubscriptionDeleted(User $user): void
    {
        UserNotificationPreference::query()
            ->where('user_id', $user->id)
            ->whereIn('channel', ['whatsapp', 'sms'])
            ->update(['enabled' => false]);

        $user->forceFill(['grace_period_until' => null])->save();

        $this->bustPlanCache($user);
    }

    private function handlePaymentSucceeded(User $user): void
    {
        $user->forceFill(['grace_period_until' => null])->save();
        $this->bustPlanCache($user);
    }

    private function handlePaymentFailed(User $user): void
    {
        // Idempotency: only the first failed-payment event in a grace window
        // transitions state + dispatches reminders. Stripe may fire this event
        // multiple times per billing cycle (once per failed retry attempt).
        if ($user->grace_period_until !== null) {
            return;
        }

        $user->forceFill(['grace_period_until' => Carbon::now()->addDays(5)])->save();

        SendPaymentFailedReminderJob::dispatch($user->id, 3)->delay(Carbon::now()->addDays(3));
        SendPaymentFailedReminderJob::dispatch($user->id, 5)->delay(Carbon::now()->addDays(5));

        $this->bustPlanCache($user);
    }

    private function bustPlanCache(User $user): void
    {
        Cache::tags(['plans'])->forget("plan_for_user_{$user->id}");
    }
}
