<?php

declare(strict_types=1);

namespace App\Controllers\Public;

use App\Core\BaseController;
use App\Core\Logger;
use App\Core\Request;
use App\Core\Response;
use App\Services\StripeService;
use App\Services\SubscriptionService;

/**
 * BillingWebhookController
 *
 * Receives Stripe's webhook deliveries. This — not the browser
 * redirect back from Checkout — is the authoritative trigger for
 * activating/renewing a tenant, because it's the one signal Stripe
 * guarantees is real (signature-verified) and guarantees arrives even
 * if the customer closes their browser tab right after paying.
 *
 * Route this at POST /webhooks/stripe, unauthenticated (Stripe can't
 * log in), CSRF-exempt (Stripe doesn't send your CSRF token) — verify
 * via verifyWebhookSignature() instead, which is the actual security
 * boundary here.
 */
final class BillingWebhookController extends BaseController
{
    public function handle(Request $request): Response
    {
        $payload = $request->rawBody();
        $signature = $request->header('Stripe-Signature') ?? '';

        if (!(new StripeService())->verifyWebhookSignature($payload, $signature)) {
            Logger::warning('Rejected Stripe webhook with invalid signature');

            return Response::json(['error' => 'invalid signature'], 400);
        }

        $event = json_decode($payload, true);
        if (!is_array($event) || !isset($event['type'])) {
            return Response::json(['error' => 'malformed payload'], 400);
        }

        $service = new SubscriptionService();
        $object = $event['data']['object'] ?? [];

        switch ($event['type']) {
            case 'checkout.session.completed':
                $businessLineId = (int) ($object['client_reference_id'] ?? 0);
                $customerId = (string) ($object['customer'] ?? '');
                $subscriptionId = (string) ($object['subscription'] ?? '');
                if ($businessLineId > 0 && $customerId !== '' && $subscriptionId !== '') {
                    $service->activateFromStripe($businessLineId, $customerId, $subscriptionId);
                }
                break;

            case 'invoice.paid':
                $businessLineId = $this->businessLineIdFromSubscriptionMetadata($object);
                if ($businessLineId > 0) {
                    $service->renewFromStripe($businessLineId, (string) $event['id']);
                }
                break;

            case 'invoice.payment_failed':
                $businessLineId = $this->businessLineIdFromSubscriptionMetadata($object);
                if ($businessLineId > 0) {
                    $service->markPastDue($businessLineId, (string) $event['id']);
                }
                break;

            case 'customer.subscription.deleted':
                $businessLineId = (int) ($object['metadata']['business_line_id'] ?? 0);
                if ($businessLineId > 0) {
                    $service->cancel($businessLineId);
                }
                break;
        }

        return Response::json(['received' => true]);
    }

    private function businessLineIdFromSubscriptionMetadata(array $invoiceObject): int
    {
        // An invoice event carries subscription metadata a layer down;
        // the subscription itself was created with business_line_id in
        // its metadata by StripeService::createCheckoutSession's
        // subscription_data.metadata.
        return (int) ($invoiceObject['subscription_details']['metadata']['business_line_id'] ?? 0);
    }
}
