<?php

namespace App\Services;

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

class EasyKashService
{
    private const DEFAULT_PAY_URL = 'https://back.easykash.net/api/directpayv1/pay';

    /**
     * Create a Direct Payment link and return the hosted payment page URL.
     */
    public function createDirectPayLink(Order $order, User $user): string
    {
        $apiKey = trim((string) (config('services.easykash.api_key') ?: env('EASYKASH_API_KEY')));
        if ($apiKey === '') {
            throw new RuntimeException(
                'مفتاح EasyKash غير موجود في إعدادات السيرفر. أضف EASYKASH_API_KEY في .env ثم نفّذ: php artisan config:clear'
            );
        }

        $orderAmount = $this->resolvePaymentAmount($order);
        if ($orderAmount <= 0) {
            throw new RuntimeException('مبلغ الطلب غير صالح للدفع.');
        }

        $charge = PaymentCurrency::forEasyKashGateway(
            $orderAmount,
            $order->currency ?? config('services.easykash.currency', PaymentCurrency::EGP)
        );
        $amount = $charge['amount'];
        $currency = $charge['currency'];

        $this->assertAmountAboveGatewayMinimum($amount, $currency);

        $mobile = $this->formatMobileForApi($user->mobile);
        if ($mobile === '') {
            throw new RuntimeException('يرجى إدخال رقم موبايل مصري صحيح في إعدادات الحساب (مثال: 01012345678).');
        }

        if (strlen($mobile) !== 12 || !str_starts_with($mobile, '20')) {
            throw new RuntimeException(
                'رقم الموبايل غير صالح لبوابة الدفع (' . $mobile . '). من إعدادات الحساب استخدم 01XXXXXXXXX (11 رقم).'
            );
        }

        $email = trim((string) ($user->email ?? ''));
        if ($email === '') {
            $email = 'customer' . $user->id . '@imtmontessori.com';
        }

        $customerReference = $this->buildCustomerReference($order);

        $payload = [
            'amount' => $amount,
            'currency' => $currency,
            'paymentOptions' => config('services.easykash.payment_options', [2, 4, 5, 6]),
            'cashExpiry' => 48,
            'name' => $this->sanitizeCustomerName($user->full_name ?? 'Customer'),
            'email' => $email,
            'mobile' => $mobile,
            'redirectUrl' => $this->buildRedirectUrl($order),
            'customerReference' => $customerReference,
        ];

        $response = $this->postPaymentRequest($apiKey, $payload);

        $body = $response->json();
        if (!is_array($body)) {
            $body = [];
        }

        $rawBody = (string) $response->body();
        $apiError = $this->extractApiErrorMessage($rawBody);

        if (!$response->successful() || $apiError !== '') {
            Log::error('easykash.pay.failed', [
                'status' => $response->status(),
                'body' => $rawBody,
                'order_id' => $order->id,
                'customer_reference' => $customerReference,
                'api_key_configured' => strlen($apiKey) > 0,
                'order_currency' => $order->currency,
                'order_amount' => $orderAmount,
                'charge' => $charge,
                'payload' => $payload,
            ]);

            $hint = $apiError !== '' ? $apiError : $this->explainHttpFailure($response->status(), $payload, $rawBody);

            throw new RuntimeException(
                'تعذر بدء عملية الدفع (' . $currency . ' ' . $amount . '): ' . $hint,
                $response->status()
            );
        }

        $redirectUrl = $body['redirectUrl'] ?? $body['redirect_url'] ?? null;
        if (empty($redirectUrl) && is_string($response->body())) {
            // Some responses return a plain URL string
            $trimmed = trim($response->body(), "\" \n\r\t");
            if (filter_var($trimmed, FILTER_VALIDATE_URL)) {
                $redirectUrl = $trimmed;
            }
        }

        if (empty($redirectUrl)) {
            Log::error('easykash.pay.missing_redirect', [
                'body' => $response->body(),
                'order_id' => $order->id,
            ]);

            throw new RuntimeException('لم يتم استلام رابط الدفع من EasyKash.');
        }

        return $redirectUrl;
    }

    public function verifyCallbackSignature(array $payload): bool
    {
        $secret = config('services.easykash.hmac_secret') ?: env('EASYKASH_HMAC_SECRET');
        if ($secret === null || $secret === '') {
            return false;
        }

        $signatureHash = $payload['signatureHash'] ?? $payload['signature_hash'] ?? null;
        if ($signatureHash === null || $signatureHash === '') {
            return false;
        }

        $fields = [
            'ProductCode',
            'Amount',
            'ProductType',
            'PaymentMethod',
            'status',
            'easykashRef',
            'customerReference',
        ];

        $parts = [];
        foreach ($fields as $field) {
            $parts[] = (string) ($payload[$field] ?? '');
        }

        $dataStr = implode('', $parts);
        $calculated = hash_hmac('sha512', $dataStr, $secret);

        return hash_equals($calculated, $signatureHash);
    }

    public function isSuccessfulPaymentPayload(array $payload): bool
    {
        if ($this->isFailedPaymentPayload($payload)) {
            return false;
        }

        foreach (['status', 'payment_status', 'paymentStatus', 'PaymentStatus'] as $key) {
            if (!isset($payload[$key])) {
                continue;
            }

            $normalized = is_string($payload[$key]) ? strtolower(trim($payload[$key])) : $payload[$key];
            $successValues = ['paid', 'success', 'successful', 'completed', 'complete', 'captured', 'approved', 'succeeded'];

            if (is_string($normalized) && in_array($normalized, $successValues, true)) {
                return true;
            }

            if (is_string($normalized)) {
                return false;
            }
        }

        if (isset($payload['isFailureEvent'])) {
            return $payload['isFailureEvent'] === false;
        }

        if (!empty($payload['eventName'])) {
            $eventName = strtolower((string) $payload['eventName']);
            if (strpos($eventName, 'fail') !== false || strpos($eventName, 'reject') !== false) {
                return false;
            }
            if (strpos($eventName, 'success') !== false || strpos($eventName, 'paid') !== false || strpos($eventName, 'complete') !== false) {
                return true;
            }
        }

        return false;
    }

    public function isFailedPaymentPayload(array $payload): bool
    {
        foreach (['status', 'payment_status', 'paymentStatus'] as $key) {
            if (!isset($payload[$key])) {
                continue;
            }

            $value = is_string($payload[$key]) ? strtolower(trim($payload[$key])) : $payload[$key];
            if (is_string($value) && in_array($value, ['failed', 'fail', 'cancelled', 'canceled', 'declined', 'expired', 'error'], true)) {
                return true;
            }
        }

        if (!empty($payload['isFailureEvent'])) {
            return true;
        }

        if (!empty($payload['eventName'])) {
            $eventName = strtolower((string) $payload['eventName']);
            if (strpos($eventName, 'fail') !== false || strpos($eventName, 'reject') !== false || strpos($eventName, 'cancel') !== false) {
                return true;
            }
        }

        return false;
    }

    public function canTrustSuccessfulReturn(array $payload): bool
    {
        if (!$this->isSuccessfulPaymentPayload($payload)) {
            return false;
        }

        if (!empty($payload['signatureHash']) || !empty($payload['signature_hash'])) {
            return $this->verifyCallbackSignature($payload);
        }

        return true;
    }

    private function resolvePaymentAmount(Order $order): float
    {
        $amount = $order->total_amount ?? $order->amount ?? 0;

        return round((float) $amount, 2);
    }

    /**
     * EasyKash returns validation "Amount must be more than 1" for amounts <= min (per currency).
     */
    private function assertAmountAboveGatewayMinimum(float $amount, string $currency): void
    {
        $minExclusive = (float) config('services.easykash.min_amount_exclusive', 1);

        if ($amount > $minExclusive) {
            return;
        }

        throw new RuntimeException(
            'المبلغ (' . $amount . ' ' . $currency . ') أقل من الحد الأدنى لبوابة EasyKash (يجب أن يكون أكبر من '
            . $minExclusive . ' ' . $currency . '). للاختبار ضع سعر الدورة مثلاً 1.01 ' . $currency . ' أو أكثر.'
        );
    }

    /**
     * Unique per payment attempt (EasyKash rejects reused customerReference).
     * Encoded as orderId * 10000 + attempt so callbacks map back to the order.
     */
    public function buildCustomerReference(Order $order): int
    {
        $attempt = \App\Models\PaymentTransactionLog::query()
            ->where('order_id', $order->id)
            ->where('event', 'easykash_create_link')
            ->count() + 1;

        return (int) ($order->id * 10000 + $attempt);
    }

    public static function resolveOrderIdFromCustomerReference(int $customerReference): int
    {
        if ($customerReference >= 10000) {
            return (int) floor($customerReference / 10000);
        }

        return $customerReference;
    }

    private function buildRedirectUrl(Order $order): string
    {
        $base = rtrim((string) config('app.url'), '/');

        if ($base === '' || str_contains($base, 'localhost') || str_contains($base, '127.0.0.1')) {
            $base = rtrim((string) config('services.easykash.redirect_base_url', 'https://imtmontessori.com'), '/');
        }

        return $base . '/payments/easykash/return?order_id=' . $order->id;
    }

    private function resolvePayUrl(): string
    {
        $url = trim((string) (config('services.easykash.pay_url') ?: env('EASYKASH_PAY_URL', self::DEFAULT_PAY_URL)));

        if ($url === '' || !filter_var($url, FILTER_VALIDATE_URL)) {
            return self::DEFAULT_PAY_URL;
        }

        return $url;
    }

    private function postPaymentRequest(string $apiKey, array $payload): \Illuminate\Http\Client\Response
    {
        // Single Authorization header only — duplicate values trigger Cloudflare 400 Bad Request.
        $headers = [
            'Authorization' => $apiKey,
            'Accept' => 'application/json',
            'Content-Type' => 'application/json',
        ];

        $url = $this->resolvePayUrl();
        $response = Http::timeout(30)->withHeaders($headers)->post($url, $payload);

        if ($response->status() === 401) {
            throw new RuntimeException(
                'مفتاح EasyKash غير صحيح أو منتهي (401). حدّث EASYKASH_API_KEY من لوحة EasyKash ثم config:clear.'
            );
        }

        return $response;
    }

    private function sanitizeCustomerName(?string $name): string
    {
        $name = trim((string) $name);
        if ($name === '') {
            return 'Customer';
        }

        $ascii = preg_replace('/[^\p{L}\p{N}\s\-\.]/u', '', $name);
        $ascii = trim(preg_replace('/\s+/', ' ', (string) $ascii));

        return $ascii !== '' ? mb_substr($ascii, 0, 100) : 'Customer';
    }

    private function explainHttpFailure(int $status, array $payload, string $rawBody = ''): string
    {
        if (stripos($rawBody, 'cloudflare') !== false || stripos($rawBody, '<html') !== false) {
            return 'رفض Cloudflare لطلب الدفع (HTTP 400). حدّث ملفات التطبيق على السيرفر، تأكد من EASYKASH_API_KEY في .env، ثم php artisan config:clear. إن استمر الخطأ تواصل مع الاستضافة للسماح بالاتصال الصادر إلى back.easykash.net.';
        }

        if ($status === 400 && empty($payload['mobile'])) {
            return 'رقم الموبايل غير صالح في إعدادات الحساب.';
        }

        if ($status === 400) {
            $redirect = $payload['redirectUrl'] ?? '';
            if (str_contains((string) $redirect, 'localhost') || str_contains((string) $redirect, '127.0.0.1')) {
                return 'رابط العودة بعد الدفع يجب أن يكون على دومين الموقع الحقيقي (ضع APP_URL=https://imtmontessori.com في .env).';
            }

            return 'طلب غير مقبول من EasyKash (تحقق من مفتاح API، الموبايل 12 رقم، والمبلغ).';
        }

        return 'خطأ من بوابة الدفع (HTTP ' . $status . ').';
    }

    private function extractApiErrorMessage(?string $body): string
    {
        if ($body === null || $body === '') {
            return '';
        }

        if (stripos($body, '<html') !== false) {
            return '';
        }

        $decoded = json_decode($body, true);
        if (!is_array($decoded)) {
            return '';
        }

        if (!empty($decoded['error']) && is_string($decoded['error'])) {
            return $this->translateEasyKashError($decoded['error']);
        }

        if (!empty($decoded['message']) && is_string($decoded['message'])) {
            return $decoded['message'];
        }

        if (isset($decoded[0]) && is_array($decoded[0]) && !empty($decoded[0]['message'])) {
            return (string) $decoded[0]['message'];
        }

        foreach ($decoded as $item) {
            if (is_array($item) && !empty($item['message'])) {
                return (string) $item['message'];
            }
        }

        return '';
    }

    private function translateEasyKashError(string $error): string
    {
        if (stripos($error, 'customerReference') !== false && stripos($error, 'already used') !== false) {
            return 'تم استخدام هذا الطلب سابقاً في EasyKash. أعد المحاولة الآن (سيتم إنشاء مرجع دفع جديد تلقائياً).';
        }

        return $error;
    }

    /**
     * EasyKash expects Egyptian mobile without leading zero (e.g. 2010xxxxxxxx).
     */
    private function formatMobileForApi($mobile): string
    {
        return normalizeUserMobile('20', $mobile) ?? '';
    }
}
