<?php

namespace App\Services;

use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use RuntimeException;

class FedapayService
{
    /** @var array<int, string> */
    private array $paidStatuses = ['approved', 'paid', 'completed', 'successful', 'success'];

    /** @var array<int, string> */
    private array $failedStatuses = ['failed', 'canceled', 'cancelled', 'declined', 'expired'];

    public function isEnabled(): bool
    {
        return (bool) config('payment.fedapay.enabled')
            && filled(config('payment.fedapay.secret_key'));
    }

    /** @return array{transaction_id:string,reference:?string,redirect_url:string,raw_status:?string} */
    public function createTransaction(Order $order, User $user): array
    {
        $response = $this->client()->post('/v1/transactions', [
            'description' => 'Paiement commande ' . $order->number,
            'amount' => (int) round((float) $order->total),
            'currency' => ['iso' => (string) config('payment.fedapay.currency_iso', 'XOF')],
            'callback_url' => $this->resolveReturnUrl($order),
            'custom_metadata' => ['order_id' => $order->id],
            'metadata' => ['order_id' => $order->id],
            'customer' => $this->buildCustomerPayload($user),
        ]);

        if ($response->failed()) {
            Log::error('Fedapay transaction creation failed', [
                'order_id' => $order->id,
                'status' => $response->status(),
                'body' => $response->json(),
            ]);
            throw new RuntimeException('Impossible d’initier le paiement Fedapay pour le moment.');
        }

        $payload = $response->json();
        $transactionId = $this->firstNonEmpty([
            data_get($payload, 'v1/transaction.id'),
            data_get($payload, 'data.id'),
            data_get($payload, 'id'),
        ]);

        if (! $transactionId) {
            throw new RuntimeException('Réponse Fedapay invalide: transaction introuvable.');
        }

        $redirectUrl = $this->firstNonEmpty([
            data_get($payload, 'v1/token.url'),
            data_get($payload, 'token.url'),
            data_get($payload, 'v1/transaction.url'),
            data_get($payload, 'data.url'),
            data_get($payload, 'url'),
        ]);

        if (! $redirectUrl) {
            $tokenResponse = $this->client()->post('/v1/transactions/' . $transactionId . '/token');
            if ($tokenResponse->ok()) {
                $tokenPayload = $tokenResponse->json();
                $redirectUrl = $this->firstNonEmpty([
                    data_get($tokenPayload, 'v1/token.url'),
                    data_get($tokenPayload, 'data.url'),
                    data_get($tokenPayload, 'url'),
                ]);
            }
        }

        if (! $redirectUrl) {
            throw new RuntimeException('URL de redirection Fedapay introuvable.');
        }

        return [
            'transaction_id' => (string) $transactionId,
            'reference' => $this->firstNonEmpty([
                data_get($payload, 'v1/transaction.reference'),
                data_get($payload, 'data.reference'),
            ]),
            'redirect_url' => (string) $redirectUrl,
            'raw_status' => $this->normalizeStatus($this->firstNonEmpty([
                data_get($payload, 'v1/transaction.status'),
                data_get($payload, 'data.status'),
            ])),
        ];
    }

    /** @return array{raw_status:?string,reference:?string,is_paid:bool,is_failed:bool} */
    public function fetchTransactionStatus(string $transactionId): array
    {
        $response = $this->client()->get('/v1/transactions/' . $transactionId);
        if ($response->failed()) {
            Log::warning('Fedapay status fetch failed', [
                'transaction_id' => $transactionId,
                'status' => $response->status(),
                'body' => $response->json(),
            ]);
            throw new RuntimeException('Impossible de vérifier le statut du paiement.');
        }

        $payload = $response->json();
        $rawStatus = $this->normalizeStatus($this->firstNonEmpty([
            data_get($payload, 'v1/transaction.status'),
            data_get($payload, 'data.status'),
            data_get($payload, 'status'),
        ]));

        return [
            'raw_status' => $rawStatus,
            'reference' => $this->firstNonEmpty([
                data_get($payload, 'v1/transaction.reference'),
                data_get($payload, 'data.reference'),
                data_get($payload, 'reference'),
            ]),
            'is_paid' => in_array($rawStatus, $this->paidStatuses, true),
            'is_failed' => in_array($rawStatus, $this->failedStatuses, true),
        ];
    }

    public function isValidWebhookSignature(string $rawBody, ?string $signature): bool
    {
        $secret = (string) config('payment.fedapay.webhook_secret');
        if ($secret === '' || ! $signature) {
            return true;
        }

        $computed = hash_hmac('sha256', $rawBody, $secret);

        return hash_equals($computed, $signature);
    }

    private function client()
    {
        return Http::baseUrl((string) config('payment.fedapay.base_url'))
            ->acceptJson()
            ->asJson()
            ->withToken((string) config('payment.fedapay.secret_key'));
    }

    /** @return array<string, mixed> */
    private function buildCustomerPayload(User $user): array
    {
        $nameParts = preg_split('/\s+/', trim((string) $user->name)) ?: [];
        $firstname = $nameParts[0] ?? 'Client';
        $lastname = count($nameParts) > 1 ? implode(' ', array_slice($nameParts, 1)) : 'Caddies+';

        $payload = [
            'firstname' => $firstname,
            'lastname' => $lastname,
            'email' => $user->email,
        ];

        $phone = preg_replace('/\D+/', '', (string) ($user->phone ?? ''));
        if ($phone) {
            $payload['phone_number'] = [
                'number' => $phone,
                'country' => (string) config('payment.fedapay.country_code', 'bj'),
            ];
        }

        return $payload;
    }

    private function resolveReturnUrl(Order $order): string
    {
        $configured = (string) config('payment.fedapay.return_url');
        if ($configured !== '') {
            return $configured;
        }

        // Retour navigateur (GET) — ne pas utiliser l’URL du webhook (POST uniquement).
        return url('/api/v1/payment/return?' . http_build_query(['order_id' => $order->id]));
    }

    /** @param array<int, mixed> $values */
    private function firstNonEmpty(array $values): ?string
    {
        foreach ($values as $value) {
            if (filled($value)) {
                return (string) $value;
            }
        }

        return null;
    }

    private function normalizeStatus(?string $status): ?string
    {
        if (! $status) {
            return null;
        }

        return strtolower(trim($status));
    }
}

