<?php

namespace App\Services\Payment;

use App\Models\User;
use App\Models\Payment;
use App\Services\Core\ConfigurationManager;
use App\Services\Analytics\EventBus;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class ZarinpalService
{
    private const SANDBOX_URL = 'https://sandbox.zarinpal.com/pg/rest/WebGate/';
    private const PRODUCTION_URL = 'https://api.zarinpal.com/pg/rest/WebGate/';
    private const SANDBOX_PAYMENT_URL = 'https://sandbox.zarinpal.com/pg/StartPay/';
    private const PRODUCTION_PAYMENT_URL = 'https://www.zarinpal.com/pg/StartPay/';

    private ConfigurationManager $config;
    private EventBus $eventBus;
    private string $merchantId;
    private bool $sandboxMode;

    public function __construct(ConfigurationManager $config, EventBus $eventBus)
    {
        $this->config = $config;
        $this->eventBus = $eventBus;
        $this->merchantId = $config->get('payment.zarinpal.merchant_id', '');
        $this->sandboxMode = $config->get('payment.zarinpal.sandbox_mode', true);
    }

    /**
     * Create a new payment request
     */
    public function createPayment(int $amount, User $user, string $description = ''): ?string
    {
        if (empty($this->merchantId)) {
            Log::error('Zarinpal merchant ID not configured');
            return null;
        }

        $trackingCode = $this->generateTrackingCode();
        $callbackUrl = route('payment.callback', ['tracking_code' => $trackingCode]);

        $requestData = [
            'merchant_id' => $this->merchantId,
            'amount' => $amount,
            'description' => $description ?: 'پرداخت از طریق ربات تلگرام',
            'callback_url' => $callbackUrl,
            'metadata' => [
                'user_id' => $user->id,
                'tracking_code' => $trackingCode,
            ]
        ];

        try {
            $response = Http::timeout(30)
                ->post($this->getApiUrl() . 'PaymentRequest.json', $requestData);

            if (!$response->successful()) {
                Log::error('Zarinpal API request failed', [
                    'status' => $response->status(),
                    'response' => $response->body(),
                ]);
                return null;
            }

            $result = $response->json();
            
            if (!isset($result['data']['authority']) || $result['data']['code'] !== 100) {
                Log::error('Zarinpal payment request failed', [
                    'result' => $result,
                    'user_id' => $user->id,
                    'amount' => $amount,
                ]);
                return null;
            }

            $authority = $result['data']['authority'];

            // Store payment record
            $payment = Payment::create([
                'user_id' => $user->id,
                'tracking_code' => $trackingCode,
                'authority' => $authority,
                'amount' => $amount,
                'description' => $description,
                'gateway' => 'zarinpal',
                'status' => 'pending',
                'callback_url' => $callbackUrl,
                'expires_at' => now()->addMinutes(15), // 15 minutes expiry
            ]);

            // Emit payment created event
            $this->eventBus->emit('payment.created', [
                'payment_id' => $payment->id,
                'user_id' => $user->id,
                'amount' => $amount,
                'gateway' => 'zarinpal',
                'tracking_code' => $trackingCode,
                'timestamp' => now()->toISOString(),
            ]);

            return $this->getPaymentUrl() . $authority;

        } catch (\Exception $e) {
            Log::error('Zarinpal payment creation exception', [
                'error' => $e->getMessage(),
                'user_id' => $user->id,
                'amount' => $amount,
                'trace' => $e->getTraceAsString(),
            ]);
            return null;
        }
    }

    /**
     * Verify payment after callback
     */
    public function verifyPayment(string $authority, string $trackingCode): array
    {
        $payment = Payment::where('tracking_code', $trackingCode)
            ->where('authority', $authority)
            ->where('status', 'pending')
            ->first();

        if (!$payment) {
            return [
                'success' => false,
                'message' => 'تراکنش یافت نشد یا قبلاً پردازش شده است',
                'payment' => null,
            ];
        }

        // Check if payment is expired
        if ($payment->expires_at && $payment->expires_at->isPast()) {
            $payment->update(['status' => 'expired']);
            
            return [
                'success' => false,
                'message' => 'زمان پرداخت منقضی شده است',
                'payment' => $payment,
            ];
        }

        try {
            $verifyData = [
                'merchant_id' => $this->merchantId,
                'authority' => $authority,
                'amount' => $payment->amount,
            ];

            $response = Http::timeout(30)
                ->post($this->getApiUrl() . 'PaymentVerification.json', $verifyData);

            if (!$response->successful()) {
                Log::error('Zarinpal verification request failed', [
                    'status' => $response->status(),
                    'response' => $response->body(),
                    'tracking_code' => $trackingCode,
                ]);
                
                $payment->update(['status' => 'failed']);
                
                return [
                    'success' => false,
                    'message' => 'خطا در تأیید پرداخت',
                    'payment' => $payment,
                ];
            }

            $result = $response->json();

            if ($result['data']['code'] === 100) {
                // Payment successful
                $refId = $result['data']['ref_id'];
                
                $payment->update([
                    'status' => 'completed',
                    'reference_id' => $refId,
                    'verified_at' => now(),
                ]);

                // Update user wallet balance
                $user = $payment->user;
                $user->increment('wallet_balance', $payment->amount);

                // Emit payment completed event
                $this->eventBus->emit('payment.completed', [
                    'payment_id' => $payment->id,
                    'user_id' => $user->id,
                    'amount' => $payment->amount,
                    'reference_id' => $refId,
                    'gateway' => 'zarinpal',
                    'timestamp' => now()->toISOString(),
                ]);

                return [
                    'success' => true,
                    'message' => 'پرداخت با موفقیت انجام شد',
                    'payment' => $payment,
                    'reference_id' => $refId,
                ];

            } elseif ($result['data']['code'] === 101) {
                // Payment already verified
                return [
                    'success' => true,
                    'message' => 'این تراکنش قبلاً تأیید شده است',
                    'payment' => $payment,
                ];

            } else {
                // Payment failed
                $errorMessage = $this->getErrorMessage($result['data']['code']);
                
                $payment->update([
                    'status' => 'failed',
                    'error_code' => $result['data']['code'],
                    'error_message' => $errorMessage,
                ]);

                // Emit payment failed event
                $this->eventBus->emit('payment.failed', [
                    'payment_id' => $payment->id,
                    'user_id' => $payment->user_id,
                    'error_code' => $result['data']['code'],
                    'error_message' => $errorMessage,
                    'timestamp' => now()->toISOString(),
                ]);

                return [
                    'success' => false,
                    'message' => $errorMessage,
                    'payment' => $payment,
                ];
            }

        } catch (\Exception $e) {
            Log::error('Zarinpal verification exception', [
                'error' => $e->getMessage(),
                'tracking_code' => $trackingCode,
                'authority' => $authority,
                'trace' => $e->getTraceAsString(),
            ]);

            $payment->update(['status' => 'failed']);

            return [
                'success' => false,
                'message' => 'خطا در پردازش پرداخت',
                'payment' => $payment,
            ];
        }
    }

    /**
     * Get payment status by tracking code
     */
    public function getPaymentStatus(string $trackingCode): ?Payment
    {
        return Payment::where('tracking_code', $trackingCode)->first();
    }

    /**
     * Cancel a pending payment
     */
    public function cancelPayment(string $trackingCode): bool
    {
        $payment = Payment::where('tracking_code', $trackingCode)
            ->where('status', 'pending')
            ->first();

        if (!$payment) {
            return false;
        }

        $payment->update(['status' => 'cancelled']);

        // Emit payment cancelled event
        $this->eventBus->emit('payment.cancelled', [
            'payment_id' => $payment->id,
            'user_id' => $payment->user_id,
            'amount' => $payment->amount,
            'timestamp' => now()->toISOString(),
        ]);

        return true;
    }

    /**
     * Get user's payment history
     */
    public function getUserPayments(int $userId, int $limit = 20): \Illuminate\Database\Eloquent\Collection
    {
        return Payment::where('user_id', $userId)
            ->orderBy('created_at', 'desc')
            ->limit($limit)
            ->get();
    }

    /**
     * Generate unique tracking code
     */
    private function generateTrackingCode(): string
    {
        do {
            $code = 'PAY' . strtoupper(Str::random(8)) . time();
        } while (Payment::where('tracking_code', $code)->exists());

        return $code;
    }

    /**
     * Get API URL based on sandbox mode
     */
    private function getApiUrl(): string
    {
        return $this->sandboxMode ? self::SANDBOX_URL : self::PRODUCTION_URL;
    }

    /**
     * Get payment URL based on sandbox mode
     */
    private function getPaymentUrl(): string
    {
        return $this->sandboxMode ? self::SANDBOX_PAYMENT_URL : self::PRODUCTION_PAYMENT_URL;
    }

    /**
     * Get error message for Zarinpal error codes
     */
    private function getErrorMessage(int $code): string
    {
        return match($code) {
            -9 => 'خطای اعتبارسنجی',
            -10 => 'ترمینال یافت نشد',
            -11 => 'درخواست یافت نشد',
            -12 => 'امکان ویرایش درخواست میسر نمی باشد',
            -15 => 'ترمینال شما به حالت تعلیق در آمده با بخش امور پذیرندگان تماس بگیرید',
            -16 => 'سطح تأیید پذیرنده پایین تر از سطح نقره ای می باشد',
            -30 => 'اجازه دسترسی به تسویه اشتراکی شناور ندارید',
            -31 => 'حساب بانکی تسویه را به پنل اضافه کنید مقادیر وارد شده واسه تسهیم درست نمی باشد',
            -32 => 'Wages is not valid, Total wages(floating) has been overload max amount.',
            -33 => 'درصد های وارد شده درست نمی باشد',
            -34 => 'مبلغ از کل تراکنش بیشتر است',
            -35 => 'تعداد افراد دریافت کننده تسهیم بیش از حد مجاز است',
            -40 => 'Invalid extra params',
            -50 => 'مبلغ پرداخت شده با مقدار مبلغ در تأیید شده متفاوت است',
            -51 => 'پرداخت ناموفق',
            -52 => 'خطای غیر منتظره با پشتیبانی تماس بگیرید',
            -53 => 'اتوریتی برای این مرچنت کد نمی باشد',
            -54 => 'اتوریتی نامعتبر است',
            100 => 'پرداخت تأیید شد',
            101 => 'پرداخت قبلا تأیید شده',
            default => 'خطای نامشخص در پرداخت (کد: ' . $code . ')',
        };
    }
}