<?php

namespace App\Services\Payments\Gateways;

use App\Models\UserPaymentMethod;
use App\Services\Payments\Contracts\PaymentGateway;
use App\Services\Payments\GatewayResult;
use Illuminate\Support\Facades\Http;

class StripePaymentGateway implements PaymentGateway
{
    public function supports(string $provider): bool
    {
        return in_array($provider, ['stripe', 'apple_pay', 'google_pay'], true);
    }

    public function charge(
        UserPaymentMethod $method,
        float $amount,
        string $currency,
        array $metadata = [],
    ): GatewayResult {
        $secretKey = (string) config('lineup.payments.stripe.secret_key');
        if ($secretKey === '') {
            return new GatewayResult(
                status: GatewayResult::STATUS_FAILED,
                message: 'Stripe secret key manquante.',
            );
        }

        $details = is_array($method->details) ? $method->details : [];
        $paymentMethodRef = $details['payment_method_id']
            ?? $details['reference']
            ?? $details['device_account']
            ?? null;

        if (!is_string($paymentMethodRef) || trim($paymentMethodRef) === '') {
            return new GatewayResult(
                status: GatewayResult::STATUS_FAILED,
                message: 'Aucun PaymentMethod Stripe valide n est enregistre.',
            );
        }

        $paymentMethodRef = trim($paymentMethodRef);
        $normalizedRef = strtolower($paymentMethodRef);
        if (
            $normalizedRef === 'pm_xxx'
            || str_starts_with($normalizedRef, 'pm_demo_')
            || str_starts_with($normalizedRef, 'pm_test_')
        ) {
            return new GatewayResult(
                status: GatewayResult::STATUS_FAILED,
                message: 'Identifiant Stripe invalide (placeholder). Renseignez un vrai PaymentMethod ID Stripe.',
            );
        }

        if (!str_starts_with($paymentMethodRef, 'pm_')) {
            return new GatewayResult(
                status: GatewayResult::STATUS_FAILED,
                message: 'Format PaymentMethod Stripe invalide. Attendu: pm_...',
            );
        }

        $configuredCustomerId = isset($details['customer_id']) && is_string($details['customer_id']) && $details['customer_id'] !== ''
            ? $details['customer_id']
            : null;

        $paymentMethodResponse = Http::timeout(15)
            ->withBasicAuth($secretKey, '')
            ->acceptJson()
            ->get('https://api.stripe.com/v1/payment_methods/' . $paymentMethodRef);

        $paymentMethodPayload = $paymentMethodResponse->json() ?: [];
        if (!$paymentMethodResponse->successful()) {
            $stripeCode = (string) ($paymentMethodPayload['error']['code'] ?? '');
            $stripeMessage = (string) ($paymentMethodPayload['error']['message'] ?? '');

            if ($stripeCode === 'resource_missing') {
                return new GatewayResult(
                    status: GatewayResult::STATUS_FAILED,
                    message: 'PaymentMethod Stripe introuvable (mode test/live ou compte Stripe incorrect). Reconnectez votre carte.',
                    payload: is_array($paymentMethodPayload) ? $paymentMethodPayload : [],
                );
            }

            return new GatewayResult(
                status: GatewayResult::STATUS_FAILED,
                message: $stripeMessage !== '' ? $stripeMessage : 'Impossible de verifier le PaymentMethod Stripe.',
                payload: is_array($paymentMethodPayload) ? $paymentMethodPayload : [],
            );
        }

        $paymentMethodCustomerId = $paymentMethodPayload['customer'] ?? null;
        $paymentMethodCustomerId = is_string($paymentMethodCustomerId) && $paymentMethodCustomerId !== ''
            ? $paymentMethodCustomerId
            : null;

        $customerId = $configuredCustomerId;
        if ($customerId === null && $paymentMethodCustomerId !== null) {
            $customerId = $paymentMethodCustomerId;
        }

        if ($customerId !== null && $paymentMethodCustomerId !== null && $paymentMethodCustomerId !== $customerId) {
            return new GatewayResult(
                status: GatewayResult::STATUS_FAILED,
                message: 'Le PaymentMethod Stripe est lie a un autre client. Reconnectez votre moyen de paiement.',
                payload: is_array($paymentMethodPayload) ? $paymentMethodPayload : [],
            );
        }

        if ($customerId !== null && $paymentMethodCustomerId === null) {
            $attachResponse = Http::timeout(15)
                ->asForm()
                ->withBasicAuth($secretKey, '')
                ->acceptJson()
                ->post('https://api.stripe.com/v1/payment_methods/' . $paymentMethodRef . '/attach', [
                    'customer' => $customerId,
                ]);

            $attachPayload = $attachResponse->json() ?: [];
            if (!$attachResponse->successful()) {
                $attachMessage = (string) ($attachPayload['error']['message'] ?? '');

                return new GatewayResult(
                    status: GatewayResult::STATUS_FAILED,
                    message: $attachMessage !== '' ? $attachMessage : 'Impossible de lier le PaymentMethod Stripe au client.',
                    payload: is_array($attachPayload) ? $attachPayload : [],
                );
            }
        }

        $params = [
            'amount' => (int) round($amount * 100),
            'currency' => strtolower($currency),
            'confirm' => 'true',
            'payment_method' => $paymentMethodRef,
            'description' => sprintf('Paiement mission LineUp (%s)', $metadata['mission_id'] ?? 'n/a'),
        ];

        if ($customerId !== null) {
            $params['customer'] = $customerId;
            $params['off_session'] = 'true';
        }

        foreach ($this->flatMetadata($metadata) as $key => $value) {
            $params["metadata[{$key}]"] = $value;
        }

        $response = Http::timeout(15)
            ->asForm()
            ->withBasicAuth($secretKey, '')
            ->post('https://api.stripe.com/v1/payment_intents', $params);

        $payload = $response->json() ?: [];
        if (!$response->successful()) {
            return new GatewayResult(
                status: GatewayResult::STATUS_FAILED,
                message: $payload['error']['message'] ?? 'Erreur Stripe lors de la creation du paiement.',
                payload: is_array($payload) ? $payload : [],
            );
        }

        $status = (string) ($payload['status'] ?? '');
        if ($status === 'succeeded') {
            return new GatewayResult(
                status: GatewayResult::STATUS_PAID,
                providerReference: $payload['id'] ?? null,
                payload: is_array($payload) ? $payload : [],
            );
        }

        if (in_array($status, ['processing', 'requires_capture'], true)) {
            return new GatewayResult(
                status: GatewayResult::STATUS_PENDING,
                providerReference: $payload['id'] ?? null,
                payload: is_array($payload) ? $payload : [],
            );
        }

        return new GatewayResult(
            status: GatewayResult::STATUS_FAILED,
            providerReference: $payload['id'] ?? null,
            message: $payload['last_payment_error']['message'] ?? 'Le paiement Stripe a echoue.',
            payload: is_array($payload) ? $payload : [],
        );
    }

    public function payout(
        UserPaymentMethod $method,
        float $amount,
        string $currency,
        array $metadata = [],
    ): GatewayResult {
        $secretKey = (string) config('lineup.payments.stripe.secret_key');
        if ($secretKey === '') {
            return new GatewayResult(
                status: GatewayResult::STATUS_FAILED,
                message: 'Stripe secret key manquante.',
            );
        }

        $details = is_array($method->details) ? $method->details : [];
        $destination = $details['destination_account']
            ?? $details['account_id']
            ?? $details['reference']
            ?? $details['device_account']
            ?? null;

        if (!is_string($destination) || $destination === '') {
            return new GatewayResult(
                status: GatewayResult::STATUS_FAILED,
                message: 'Aucun compte destination Stripe Connect trouve pour ce moyen de paiement.',
            );
        }

        $params = [
            'amount' => (int) round($amount * 100),
            'currency' => strtolower($currency),
            'destination' => $destination,
            'description' => sprintf('Retrait LineUp (%s)', $metadata['payout_id'] ?? 'n/a'),
        ];

        foreach ($this->flatMetadata($metadata) as $key => $value) {
            $params["metadata[{$key}]"] = $value;
        }

        $response = Http::timeout(15)
            ->asForm()
            ->withBasicAuth($secretKey, '')
            ->post('https://api.stripe.com/v1/transfers', $params);

        $payload = $response->json() ?: [];
        if (!$response->successful()) {
            return new GatewayResult(
                status: GatewayResult::STATUS_FAILED,
                message: $payload['error']['message'] ?? 'Erreur Stripe lors du transfert.',
                payload: is_array($payload) ? $payload : [],
            );
        }

        return new GatewayResult(
            status: GatewayResult::STATUS_PAID,
            providerReference: $payload['id'] ?? null,
            payload: is_array($payload) ? $payload : [],
        );
    }

    private function flatMetadata(array $metadata): array
    {
        $flat = [];
        foreach ($metadata as $key => $value) {
            if (!is_scalar($value) && $value !== null) {
                continue;
            }
            $flat[(string) $key] = (string) $value;
        }

        return $flat;
    }
}
