<?php

namespace App\Services;

use App\Models\Transaction;
use App\Models\User;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use InvalidArgumentException;
use RuntimeException;

class TilopayClient
{
    public function createPayment(Transaction $transaction, User $user, array $billing = []): array
    {
        $apiUser = config('services.tilopay.api_user');
        $apiPassword = config('services.tilopay.api_password');
        $apiKey = config('services.tilopay.api_key');

        if (! $apiUser || ! $apiPassword || ! $apiKey) {
            // Modo de simulación local
            return [
                'url' => url('/api/payments/tilopay/mock-checkout') . '?' . http_build_query([
                    'amount' => number_format((float) $transaction->amount, 0, ',', '.'),
                    'order' => $transaction->provider_order_number,
                    'returnData' => base64_encode(json_encode([
                        'transaction_id' => $transaction->id,
                        'user_id' => $user->id,
                        'order_number' => $transaction->provider_order_number,
                    ], JSON_THROW_ON_ERROR))
                ])
            ];
        }

        $response = Http::baseUrl($this->apiUrl())
            ->withToken($this->accessToken())
            ->acceptJson()
            ->asJson()
            ->post('/processPayment', $this->paymentPayload($transaction, $user, $billing))
            ->throw()
            ->json();

        if (! is_array($response) || empty($response['url'])) {
            throw new RuntimeException('Tilopay no devolvió una URL de pago válida.');
        }

        return $response;
    }

    private function accessToken(): string
    {
        $cacheKey = 'tilopay.access_token.'.sha1((string) config('services.tilopay.api_user'));
        $cachedToken = Cache::get($cacheKey);

        if (is_string($cachedToken) && $cachedToken !== '') {
            return $cachedToken;
        }

        $apiUser = config('services.tilopay.api_user');
        $apiPassword = config('services.tilopay.api_password');

        if (! $apiUser || ! $apiPassword) {
            throw new InvalidArgumentException('Faltan las credenciales de Tilopay.');
        }

        $response = Http::baseUrl($this->apiUrl())
            ->acceptJson()
            ->asJson()
            ->post('/login', [
                'apiuser' => $apiUser,
                'password' => $apiPassword,
            ])
            ->throw()
            ->json();

        $token = data_get($response, 'access_token');

        if (! is_string($token) || $token === '') {
            throw new RuntimeException('Tilopay no devolvió un token válido.');
        }

        $ttl = max(60, ((int) data_get($response, 'expires_in', 3600)) - 60);
        Cache::put($cacheKey, $token, now()->addSeconds($ttl));

        return $token;
    }

    private function paymentPayload(Transaction $transaction, User $user, array $billing): array
    {
        $key = config('services.tilopay.api_key');

        if (! $key) {
            throw new InvalidArgumentException('Falta la llave de comercio de Tilopay.');
        }

        $billTo = $this->billingData($user, $billing);

        return [
            'redirect' => config('services.tilopay.redirect_url'),
            'key' => $key,
            'amount' => number_format((float) $transaction->amount, 2, '.', ''),
            'currency' => config('services.tilopay.currency', 'CRC'),
            'orderNumber' => $transaction->provider_order_number,
            'capture' => '1',
            'billToFirstName' => $billTo['first_name'],
            'billToLastName' => $billTo['last_name'],
            'billToAddress' => $billTo['address'],
            'billToAddress2' => $billTo['address2'],
            'billToCity' => $billTo['city'],
            'billToState' => $billTo['state'],
            'billToZipPostCode' => $billTo['zip'],
            'billToCountry' => $billTo['country'],
            'billToTelephone' => $billTo['telephone'],
            'billToEmail' => $billTo['email'],
            'shipToFirstName' => $billTo['first_name'],
            'shipToLastName' => $billTo['last_name'],
            'shipToAddress' => $billTo['address'],
            'shipToAddress2' => $billTo['address2'],
            'shipToCity' => $billTo['city'],
            'shipToState' => $billTo['state'],
            'shipToZipPostCode' => $billTo['zip'],
            'shipToCountry' => $billTo['country'],
            'shipToTelephone' => $billTo['telephone'],
            'subscription' => '0',
            'platform' => config('services.tilopay.platform', 'WincarApp'),
            'returnData' => base64_encode(json_encode([
                'transaction_id' => $transaction->id,
                'user_id' => $user->id,
                'order_number' => $transaction->provider_order_number,
            ], JSON_THROW_ON_ERROR)),
            'hashVersion' => config('services.tilopay.hash_version', 'V2'),
            'token_version' => 'v2',
        ];
    }

    private function billingData(User $user, array $billing): array
    {
        $name = trim((string) $user->name);
        $nameParts = preg_split('/\s+/', $name, 2) ?: [];

        return [
            'first_name' => $billing['first_name'] ?? $nameParts[0] ?? 'Cliente',
            'last_name' => $billing['last_name'] ?? $nameParts[1] ?? 'Wincar',
            'address' => $billing['address'] ?? config('services.tilopay.defaults.address', 'San Jose'),
            'address2' => $billing['address2'] ?? config('services.tilopay.defaults.address2', 'San Jose'),
            'city' => $billing['city'] ?? config('services.tilopay.defaults.city', 'San Jose'),
            'state' => $billing['state'] ?? config('services.tilopay.defaults.state', 'CR-SJ'),
            'zip' => $billing['zip'] ?? config('services.tilopay.defaults.zip', '10101'),
            'country' => $billing['country'] ?? config('services.tilopay.defaults.country', 'CR'),
            'telephone' => $billing['telephone'] ?? $user->phone ?? config('services.tilopay.defaults.telephone', '88888888'),
            'email' => $billing['email'] ?? $user->email,
        ];
    }

    private function apiUrl(): string
    {
        return rtrim((string) config('services.tilopay.api_url', 'https://app.tilopay.com/api/v1'), '/');
    }
}
