<?php

namespace App\Domain\Financial\Services;

use App\Domain\Financial\Enums\PaymentStatus;
use App\Domain\Financial\Events\PaymentReceived;
use App\Domain\Financial\Models\Invoice;
use App\Domain\Financial\Models\Payment;
use App\Domain\Shared\Models\SystemSetting;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class PaymobService
{
    private ?string $apiKey;
    private ?string $integrationId;
    private ?string $iframeId;
    private ?string $hmacSecret;

    private PaymentService $payments;

    public function __construct(?int $academyId = null, ?PaymentService $payments = null)
    {
        $this->payments = $payments ?? app(PaymentService::class);
        $this->resolveCredentials($academyId);
    }

    public static function forAcademy(int $academyId): static
    {
        return new static($academyId);
    }

    public function isConfigured(): bool
    {
        return !empty($this->apiKey) && !empty($this->integrationId) && !empty($this->hmacSecret);
    }

    public function createPaymentIntention(Invoice $invoice, User $payer): array
    {
        if (!$this->isConfigured()) {
            throw new \RuntimeException('Paymob is not configured for this academy');
        }

        $authToken = $this->authenticate();
        $orderId = $this->createOrder($authToken, $invoice);
        $paymentKey = $this->getPaymentKey($authToken, $orderId, $invoice, $payer);

        $iframeUrl = $this->iframeId
            ? "https://accept.paymob.com/api/acceptance/iframes/{$this->iframeId}?payment_token={$paymentKey}"
            : null;

        return [
            'payment_key' => $paymentKey,
            'iframe_url' => $iframeUrl,
            'order_id' => (string) $orderId,
        ];
    }

    public function verifyCallback(array $data): bool
    {
        if (!$this->hmacSecret) {
            Log::warning('[Paymob] Cannot verify callback — HMAC secret not configured');
            return false;
        }

        $hmac = $data['hmac'] ?? '';
        if (empty($hmac)) {
            return false;
        }

        $concatenated = implode('', [
            $data['amount_cents'] ?? '',
            $data['created_at'] ?? '',
            $data['currency'] ?? '',
            $data['error_occured'] ?? '',
            $data['has_parent_transaction'] ?? '',
            $data['id'] ?? '',
            $data['integration_id'] ?? '',
            $data['is_3d_secure'] ?? '',
            $data['is_auth'] ?? '',
            $data['is_capture'] ?? '',
            $data['is_refunded'] ?? '',
            $data['is_standalone_payment'] ?? '',
            $data['is_voided'] ?? '',
            $data['order'] ?? '',
            $data['owner'] ?? '',
            $data['pending'] ?? '',
            $data['source_data_pan'] ?? '',
            $data['source_data_sub_type'] ?? '',
            $data['source_data_type'] ?? '',
            $data['success'] ?? '',
        ]);

        $calculatedHmac = hash_hmac('sha512', $concatenated, $this->hmacSecret);

        return hash_equals($calculatedHmac, $hmac);
    }

    public function processCallback(array $callbackData): ?Payment
    {
        $orderId = $callbackData['order'] ?? ($callbackData['obj']['order']['id'] ?? null);
        $success = filter_var($callbackData['success'] ?? ($callbackData['obj']['success'] ?? false), FILTER_VALIDATE_BOOLEAN);
        $amountCents = (int) ($callbackData['amount_cents'] ?? ($callbackData['obj']['amount_cents'] ?? 0));
        $transactionId = $callbackData['id'] ?? ($callbackData['obj']['id'] ?? null);

        if (!$orderId) {
            Log::warning('[Paymob] Callback missing order_id', ['data' => array_keys($callbackData)]);
            return null;
        }

        $payment = Payment::withoutGlobalScope('academy')
            ->where('status', PaymentStatus::Pending)
            ->whereJsonContains('gateway_data->paymob_order_id', (string) $orderId)
            ->first();

        if (!$payment) {
            Log::warning('[Paymob] No pending payment for order', ['order_id' => $orderId]);
            return null;
        }

        if ($payment->status === PaymentStatus::Confirmed) {
            return $payment;
        }

        if (!$success) {
            $payment->update([
                'status' => PaymentStatus::Failed,
                'failed_at' => now(),
                'gateway_data' => array_merge($payment->gateway_data ?? [], [
                    'paymob_transaction_id' => $transactionId,
                    'failure_reason' => $callbackData['data_message'] ?? ($callbackData['obj']['data']['message'] ?? 'unknown'),
                ]),
            ]);
            Log::info('[Paymob] Payment marked failed', ['payment_id' => $payment->id, 'order_id' => $orderId]);
            return null;
        }

        if ($amountCents <= 0) {
            Log::warning('[Paymob] Callback with zero amount', ['order_id' => $orderId]);
            return null;
        }

        $actor = $payment->created_by ? User::withoutGlobalScopes()->find($payment->created_by) : null;

        if (! $actor) {
            Log::error('[Paymob] Cannot confirm payment without an actor', ['payment_id' => $payment->id]);
            return null;
        }

        return DB::transaction(function () use ($payment, $transactionId, $amountCents, $actor) {
            $payment->update([
                'gateway_data' => array_merge($payment->gateway_data ?? [], [
                    'paymob_transaction_id' => $transactionId,
                    'confirmed_via' => 'webhook',
                ]),
            ]);

            // Settlement, the ledger entry, the invoice lock and the retry
            // guard all live in PaymentService now. The callback used to do
            // the invoice arithmetic itself, write no Transaction at all, and
            // credit twice when Paymob retried.
            return $this->payments->confirmPending($payment, $actor, $amountCents);
        });
    }

    private function resolveCredentials(?int $academyId): void
    {
        // Priority 1: Academy-specific system settings (if academy context exists)
        $this->apiKey = $this->resolveSetting('paymob_api_key', 'PAYMOB_API_KEY', $academyId);
        $this->integrationId = $this->resolveSetting('paymob_integration_id', 'PAYMOB_INTEGRATION_ID', $academyId);
        $this->iframeId = $this->resolveSetting('paymob_iframe_id', 'PAYMOB_IFRAME_ID', $academyId);
        $this->hmacSecret = $this->resolveSetting('paymob_hmac_secret', 'PAYMOB_HMAC_SECRET', $academyId);
    }

    private function resolveSetting(string $settingKey, string $envKey, ?int $academyId): ?string
    {
        // Try 1: Explicit academy ID lookup (bypasses global scope)
        if ($academyId) {
            $setting = SystemSetting::withoutGlobalScope('academy')
                ->where('academy_id', $academyId)
                ->where('key', $settingKey)
                ->first();
            if ($setting && !empty($setting->value)) {
                return $setting->castValue();
            }
        }

        // Try 2: Current academy via SystemSetting::get() (works in authenticated requests)
        $value = SystemSetting::get($settingKey);
        if (!empty($value)) {
            return $value;
        }

        // Try 3: config/services.php
        $configValue = config("services.paymob.{$settingKey}");
        if (!empty($configValue)) {
            return $configValue;
        }

        // Try 4: Environment variable
        $envValue = env($envKey);
        if (!empty($envValue)) {
            return $envValue;
        }

        return null;
    }

    private function authenticate(): string
    {
        $response = Http::timeout(15)->post('https://accept.paymob.com/api/auth/tokens', [
            'api_key' => $this->apiKey,
        ]);

        if (!$response->successful()) {
            Log::error('[Paymob] Auth failed', ['status' => $response->status(), 'body' => $response->body()]);
            throw new \RuntimeException('Paymob authentication failed');
        }

        return $response->json('token');
    }

    private function createOrder(string $authToken, Invoice $invoice): int
    {
        $response = Http::timeout(15)->post('https://accept.paymob.com/api/ecommerce/orders', [
            'auth_token' => $authToken,
            'delivery_needed' => false,
            'amount_cents' => $invoice->due_amount,
            'currency' => 'EGP',
            'merchant_order_id' => $invoice->uuid . '-' . time(),
            'items' => [],
        ]);

        if (!$response->successful()) {
            Log::error('[Paymob] Order creation failed', ['status' => $response->status(), 'body' => $response->body()]);
            throw new \RuntimeException('Paymob order creation failed');
        }

        return $response->json('id');
    }

    private function getPaymentKey(string $authToken, int $orderId, Invoice $invoice, User $payer): string
    {
        $phone = $payer->phone ?? '+201000000000';
        $name = $payer->name_ar ?? $payer->name ?? 'Customer';

        $response = Http::timeout(15)->post('https://accept.paymob.com/api/acceptance/payment_keys', [
            'auth_token' => $authToken,
            'amount_cents' => $invoice->due_amount,
            'expiration' => 3600,
            'order_id' => $orderId,
            'billing_data' => [
                'first_name' => $name,
                'last_name' => 'N/A',
                'email' => $payer->email ?? 'customer@elcaptain.app',
                'phone_number' => $phone,
                'apartment' => 'N/A',
                'floor' => 'N/A',
                'street' => 'N/A',
                'building' => 'N/A',
                'shipping_method' => 'N/A',
                'postal_code' => 'N/A',
                'city' => 'N/A',
                'country' => 'EG',
                'state' => 'N/A',
            ],
            'currency' => 'EGP',
            'integration_id' => (int) $this->integrationId,
        ]);

        if (!$response->successful()) {
            Log::error('[Paymob] Payment key failed', ['status' => $response->status(), 'body' => $response->body()]);
            throw new \RuntimeException('Paymob payment key generation failed');
        }

        return $response->json('token');
    }
}
