<?php

namespace App\Listeners;

use App\Enums\CreditLedgerEntryType;
use App\Enums\CreditProductType;
use App\Models\CreditProduct;
use App\Models\User;
use App\Services\AiCreditService;

class GrantCreditsFromStripeWebhook
{
    public function __construct(private AiCreditService $credits) {}

    /**
     * @param  array{payload: array<string, mixed>}  $event
     */
    public function handle(object $event): void
    {
        $payload = $event->payload ?? [];

        match ($payload['type'] ?? null) {
            'checkout.session.completed' => $this->handleCheckoutSessionCompleted($payload),
            'invoice.paid' => $this->handleInvoicePaid($payload),
            default => null,
        };
    }

    /**
     * @param  array<string, mixed>  $payload
     */
    private function handleCheckoutSessionCompleted(array $payload): void
    {
        $session = $payload['data']['object'] ?? [];

        if (($session['mode'] ?? null) !== 'payment' || ($session['payment_status'] ?? null) !== 'paid') {
            return;
        }

        $product = $this->productFromMetadata($session['metadata'] ?? null, CreditProductType::ONE_TIME);
        $user = $this->userFromCustomer($session['customer'] ?? null);

        if (! $product || ! $user) {
            return;
        }

        $this->credits->grant(
            user: $user,
            credits: $product->credits,
            type: CreditLedgerEntryType::PURCHASE,
            product: $product,
            source: 'stripe_checkout_session:'.$session['id'],
            stripeCheckoutSessionId: $session['id'] ?? null,
            stripePaymentIntentId: $session['payment_intent'] ?? null,
            metadata: [
                'stripe_event_id' => $payload['id'] ?? null,
            ],
        );
    }

    /**
     * @param  array<string, mixed>  $payload
     */
    private function handleInvoicePaid(array $payload): void
    {
        $invoice = $payload['data']['object'] ?? [];
        $user = $this->userFromCustomer($invoice['customer'] ?? null);

        if (! $user) {
            return;
        }

        foreach (($invoice['lines']['data'] ?? []) as $line) {
            $priceId = $line['price']['id'] ?? null;

            if (! $priceId) {
                continue;
            }

            $product = CreditProduct::query()
                ->where('type', CreditProductType::MONTHLY)
                ->where('stripe_price_id', $priceId)
                ->first();

            if (! $product) {
                continue;
            }

            $this->credits->grant(
                user: $user,
                credits: $product->credits,
                type: CreditLedgerEntryType::SUBSCRIPTION_RENEWAL,
                product: $product,
                source: 'stripe_invoice:'.$invoice['id'].':price:'.$priceId,
                stripeInvoiceId: $invoice['id'] ?? null,
                stripePaymentIntentId: $invoice['payment_intent'] ?? null,
                stripeSubscriptionId: $invoice['subscription'] ?? null,
                metadata: [
                    'billing_reason' => $invoice['billing_reason'] ?? null,
                    'stripe_event_id' => $payload['id'] ?? null,
                ],
            );
        }
    }

    private function userFromCustomer(mixed $customer): ?User
    {
        if (! is_string($customer) || $customer === '') {
            return null;
        }

        return User::query()->where('stripe_id', $customer)->first();
    }

    private function productFromMetadata(mixed $metadata, CreditProductType $type): ?CreditProduct
    {
        if (! is_array($metadata) || ! is_string($metadata['credit_product_id'] ?? null)) {
            return null;
        }

        return CreditProduct::query()
            ->whereKey($metadata['credit_product_id'])
            ->where('type', $type)
            ->first();
    }
}
