<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\UserPaymentMethod;
use App\Services\Payments\MissionSettlementProcessor;
use App\Services\Payments\PaymentGatewayManager;
use App\Services\Payments\PayoutProcessor;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class PaymentWebhookController extends Controller
{
    public function stripe(
        Request $request,
        MissionSettlementProcessor $missionSettlementProcessor,
        PayoutProcessor $payoutProcessor,
    ): JsonResponse {
        $payloadRaw = $request->getContent();
        $event = json_decode($payloadRaw, true);

        if (!is_array($event)) {
            return response()->json(['message' => 'Charge utile JSON invalide'], 422);
        }

        if (!$this->verifyStripeSignature($request, $payloadRaw)) {
            return response()->json(['message' => 'Signature Stripe invalide'], 400);
        }

        $type = (string) ($event['type'] ?? '');
        $object = $event['data']['object'] ?? [];
        $reference = $object['id'] ?? null;

        if ($type === 'payment_intent.succeeded' && is_string($reference)) {
            $missionSettlementProcessor->markPaymentPaidByReference($reference, [
                'stripe_event_id' => $event['id'] ?? null,
            ]);
        }

        if (in_array($type, ['payment_intent.payment_failed', 'payment_intent.canceled'], true) && is_string($reference)) {
            $failureMessage = $object['last_payment_error']['message']
                ?? $object['cancellation_reason']
                ?? 'Le paiement Stripe a echoue.';

            $missionSettlementProcessor->markPaymentFailedByReference(
                providerReference: $reference,
                reason: is_string($failureMessage) ? $failureMessage : 'Le paiement Stripe a echoue.',
                metadata: ['stripe_event_id' => $event['id'] ?? null],
            );
        }

        if (in_array($type, ['transfer.paid', 'transfer.created'], true) && is_string($reference)) {
            $payoutProcessor->markPayoutPaidByReference($reference, [
                'stripe_event_id' => $event['id'] ?? null,
            ]);
        }

        if (in_array($type, ['transfer.failed', 'transfer.reversed'], true) && is_string($reference)) {
            $payoutProcessor->markPayoutFailedByReference(
                providerReference: $reference,
                reason: 'Le transfert Stripe a echoue ou a ete annule.',
                metadata: ['stripe_event_id' => $event['id'] ?? null],
            );
        }

        return response()->json(['received' => true], 202);
    }

    public function paypal(
        Request $request,
        PaymentGatewayManager $gatewayManager,
        MissionSettlementProcessor $missionSettlementProcessor,
        PayoutProcessor $payoutProcessor,
    ): JsonResponse {
        $event = $request->json()->all();
        if (!is_array($event) || empty($event)) {
            return response()->json(['message' => 'Charge utile JSON invalide'], 422);
        }

        if (!$gatewayManager->shouldSimulate()) {
            $headers = [];
            foreach ($request->headers->all() as $key => $values) {
                $headers[strtolower($key)] = is_array($values) ? ($values[0] ?? '') : (string) $values;
            }

            $isValid = $gatewayManager->paypalGateway()->verifyWebhookSignature($headers, $event);
            if (!$isValid) {
                return response()->json(['message' => 'Signature PayPal invalide'], 400);
            }
        }

        $eventType = (string) ($event['event_type'] ?? '');
        $resource = is_array($event['resource'] ?? null) ? $event['resource'] : [];

        $paymentReference = $resource['id'] ?? null;
        if ($eventType === 'PAYMENT.CAPTURE.COMPLETED' && is_string($paymentReference)) {
            $missionSettlementProcessor->markPaymentPaidByReference($paymentReference, [
                'paypal_event_id' => $event['id'] ?? null,
            ]);
        }

        if (in_array($eventType, ['PAYMENT.CAPTURE.DENIED', 'PAYMENT.CAPTURE.REFUNDED'], true) && is_string($paymentReference)) {
            $missionSettlementProcessor->markPaymentFailedByReference(
                providerReference: $paymentReference,
                reason: 'La capture PayPal a echoue ou a ete remboursee.',
                metadata: ['paypal_event_id' => $event['id'] ?? null],
            );
        }

        $payoutReference = $resource['payout_batch_id']
            ?? $resource['payout_batch']['batch_header']['payout_batch_id']
            ?? null;

        if (in_array($eventType, ['PAYMENT.PAYOUTSBATCH.SUCCESS', 'PAYMENT.PAYOUTS-ITEM.SUCCEEDED'], true) && is_string($payoutReference)) {
            $payoutProcessor->markPayoutPaidByReference($payoutReference, [
                'paypal_event_id' => $event['id'] ?? null,
            ]);
        }

        if (in_array($eventType, ['PAYMENT.PAYOUTSBATCH.DENIED', 'PAYMENT.PAYOUTS-ITEM.DENIED'], true) && is_string($payoutReference)) {
            $payoutProcessor->markPayoutFailedByReference(
                providerReference: $payoutReference,
                reason: 'Le retrait PayPal a echoue.',
                metadata: ['paypal_event_id' => $event['id'] ?? null],
            );
        }

        if (in_array($eventType, ['VAULT.PAYMENT-TOKEN.CREATED', 'VAULT.PAYMENT-TOKEN.APPROVED'], true)) {
            $this->syncPaypalVaultPaymentMethod($resource);
        }

        Log::info('lineup.payments.webhook.paypal.received', [
            'event_type' => $eventType,
            'event_id' => $event['id'] ?? null,
            'resource_id' => $resource['id'] ?? null,
        ]);

        return response()->json(['received' => true], 202);
    }

    private function syncPaypalVaultPaymentMethod(array $resource): void
    {
        $vaultId = $resource['id'] ?? null;
        $vaultStatus = $resource['status'] ?? null;
        $setupTokenId = $resource['setup_token_id']
            ?? $resource['payment_source']['token']['id']
            ?? $resource['payment_source']['paypal']['experience_context']['order_id']
            ?? null;
        $customId = $resource['customer']['merchant_customer_id']
            ?? $resource['payment_source']['paypal']['attributes']['customer']['merchant_customer_id']
            ?? null;
        $payerId = $resource['customer']['payer_id']
            ?? $resource['customer']['id']
            ?? $resource['payment_source']['paypal']['payer_id']
            ?? null;
        $payerEmail = $resource['customer']['email_address']
            ?? $resource['payment_source']['paypal']['email_address']
            ?? null;

        if (!is_string($vaultId) || $vaultId === '') {
            return;
        }

        $candidate = UserPaymentMethod::query()
            ->where('provider', 'paypal')
            ->where('type', UserPaymentMethod::TYPE_PAY_IN)
            ->get()
            ->first(function (UserPaymentMethod $method) use ($setupTokenId, $customId, $payerId, $payerEmail): bool {
                $details = is_array($method->details) ? $method->details : [];

                if (is_string($setupTokenId) && $setupTokenId !== '') {
                    $knownSetupToken = $details['setup_token_id'] ?? $details['setup_order_id'] ?? null;
                    if (is_string($knownSetupToken) && $knownSetupToken === $setupTokenId) {
                        return true;
                    }
                }

                if (is_string($customId) && $customId !== '') {
                    $knownCustomId = $details['custom_id'] ?? null;
                    if (is_string($knownCustomId) && $knownCustomId === $customId) {
                        return true;
                    }
                }

                if (is_string($payerId) && $payerId !== '') {
                    return ($details['payer_id'] ?? null) === $payerId;
                }

                if (is_string($payerEmail) && $payerEmail !== '') {
                    return strtolower((string) ($details['email'] ?? '')) === strtolower($payerEmail);
                }

                return false;
            });

        if (!$candidate) {
            return;
        }

        $details = is_array($candidate->details) ? $candidate->details : [];
        $details['vault_id'] = $vaultId;
        $details['vault_token'] = $vaultId;
        $details['vault_token_type'] = 'PAYPAL_VAULT_ID';

        if (is_string($vaultStatus) && $vaultStatus !== '') {
            $details['vault_status'] = $vaultStatus;
        }
        if (is_string($setupTokenId) && $setupTokenId !== '') {
            $details['setup_token_id'] = $setupTokenId;
            $details['setup_order_id'] = $details['setup_order_id'] ?? $setupTokenId;
        }
        if (is_string($customId) && $customId !== '') {
            $details['custom_id'] = $customId;
        }
        if (is_string($payerId) && $payerId !== '') {
            $details['payer_id'] = $payerId;
        }
        if (is_string($payerEmail) && $payerEmail !== '') {
            $details['email'] = $payerEmail;
        }

        $candidate->update([
            'details' => $details,
            'is_active' => true,
            'verified_at' => now(),
        ]);
    }

    private function verifyStripeSignature(Request $request, string $payload): bool
    {
        $secret = (string) config('lineup.payments.stripe.webhook_secret');
        if ($secret === '' || config('lineup.payments.simulate', true)) {
            return true;
        }

        $header = (string) $request->header('Stripe-Signature', '');
        if ($header === '') {
            return false;
        }

        $parts = [];
        foreach (explode(',', $header) as $segment) {
            [$key, $value] = array_pad(explode('=', trim($segment), 2), 2, null);
            if ($key !== null && $value !== null) {
                $parts[$key][] = $value;
            }
        }

        $timestamp = $parts['t'][0] ?? null;
        $signatures = $parts['v1'] ?? [];
        if (!is_string($timestamp) || empty($signatures)) {
            return false;
        }

        $signedPayload = $timestamp . '.' . $payload;
        $expected = hash_hmac('sha256', $signedPayload, $secret);

        foreach ($signatures as $signature) {
            if (hash_equals($expected, $signature)) {
                return true;
            }
        }

        return false;
    }
}
