<?php

namespace App\Services\Mentorship;

use App\Models\Mentorship\Payment;
use App\Models\Mentorship\Application;
use App\Models\Mentorship\Programme;
use App\Services\PaymentService as BasePaymentService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class PaymentService
{
    protected BasePaymentService $paymentService;

    public function __construct(BasePaymentService $paymentService)
    {
        $this->paymentService = $paymentService;
    }

    /**
     * Initiate payment for an application (mobile money or card).
     */
    public function initiatePayment(
        Application $application,
        string $phone,
        string $paymentMethod = 'mobile',
        ?string $cardNumber = null,
        ?string $cardExpiry = null,
        ?string $cardCvv = null
    ): array {
        DB::beginTransaction();
        
        try {
            // Validate application is in correct state
            if (!$application->payment_required) {
                throw new \Exception('This application does not require payment.');
            }

            if ($application->payment_status === 'paid') {
                throw new \Exception('This application has already been paid.');
            }

            // Generate unique reference with MENT-PAY- prefix
            $reference    = 'MENT-PAY-' . $application->id . '-' . time();
            $storedMethod = $paymentMethod === 'card' ? 'mtn_momo_card' : 'mtn_momo_mobile';

            // Create payment record
            $payment = Payment::create([
                'application_id' => $application->id,
                'programme_id'   => $application->programme_id,
                'user_id'        => $application->user_id,
                'amount'         => $application->payment_amount,
                'currency'       => $application->payment_currency,
                'payment_type'   => 'programme_fee',
                'description'    => 'Payment for ' . $application->programme->title,
                'payment_method' => $storedMethod,
                'phone_number'   => $phone ?: null,
                'reference'      => $reference,
                'status'         => Payment::STATUS_PENDING,
                'ip_address'     => request()->ip(),
                'user_agent'     => request()->userAgent(),
            ]);

            // Mark application as payment pending
            $application->markPaymentPending();

            // Initiate MTN MoMo collection (mobile or card)
            if ($paymentMethod === 'card') {
                $result = $this->paymentService->initiateCardCollection(
                    cardNumber: $cardNumber ?? '',
                    expiry: $cardExpiry ?? '',
                    cvv: $cardCvv ?? '',
                    amount: $application->payment_amount,
                    reference: $reference,
                    payerMessage: 'Payment for ' . $application->programme->title,
                    payeeNote: 'Mentorship programme fee'
                );
            } else {
                $result = $this->paymentService->initiateCollection(
                    phoneNumber: $phone,
                    amount: $application->payment_amount,
                    reference: $reference,
                    payerMessage: 'Payment for ' . $application->programme->title,
                    payeeNote: 'Mentorship programme fee'
                );
            }

            if ($result['success']) {
                // Update payment with transaction details
                $payment->update([
                    'transaction_id'       => $result['transaction_id'] ?? null,
                    'momo_transaction_id'  => $result['transaction_id'] ?? null,
                    'status'               => Payment::STATUS_PROCESSING,
                    'request_data'         => [
                        'phone'        => $phone,
                        'amount'       => $application->payment_amount,
                        'currency'     => $application->payment_currency,
                        'programme_id' => $application->programme_id,
                        'method'       => $paymentMethod,
                    ],
                ]);

                DB::commit();

                Log::info('Mentorship payment initiated', [
                    'payment_id'     => $payment->id,
                    'application_id' => $application->id,
                    'reference'      => $reference,
                    'amount'         => $application->payment_amount,
                    'method'         => $paymentMethod,
                ]);

                return [
                    'success'    => true,
                    'payment_id' => $payment->id,
                    'reference'  => $reference,
                    'message'    => 'Payment request sent to your phone. Please check and approve.',
                ];
            }

            // Payment initiation failed
            $payment->update([
                'status'        => Payment::STATUS_FAILED,
                'error_code'    => $result['error_code'] ?? 'INITIATION_FAILED',
                'error_message' => $result['message'] ?? 'Failed to initiate payment',
            ]);

            DB::commit();

            return [
                'success' => false,
                'message' => $result['message'] ?? 'Failed to initiate payment. Please try again.',
            ];
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to initiate mentorship payment', [
                'error'          => $e->getMessage(),
                'application_id' => $application->id,
            ]);
            throw $e;
        }
    }

    /**
     * Check payment status.
     */
    public function checkStatus(Payment $payment): array
    {
        try {
            // If already completed, return cached status
            if ($payment->isSuccessful()) {
                return [
                    'success' => true,
                    'status' => 'paid',
                    'message' => 'Payment completed successfully.',
                    'payment' => $payment,
                ];
            }

            // Check with payment service
            $result = $this->paymentService->checkTransactionStatus(
                transactionId: $payment->reference,
                type: 'collection'
            );

            if ($result['success']) {
                $status = $result['status'];

                // Update payment status based on response
                if ($status === 'SUCCESSFUL') {
                    $this->markAsSuccessful($payment, $result);
                } elseif ($status === 'FAILED') {
                    $this->markAsFailed($payment, $result);
                }

                return [
                    'success' => true,
                    'status' => strtolower($status),
                    'message' => $result['message'] ?? 'Status retrieved.',
                    'payment' => $payment->fresh(),
                ];
            }

            return [
                'success' => false,
                'status' => $payment->status,
                'message' => $result['message'] ?? 'Failed to check payment status.',
            ];
        } catch (\Exception $e) {
            Log::error('Failed to check mentorship payment status', [
                'error' => $e->getMessage(),
                'payment_id' => $payment->id,
            ]);
            throw $e;
        }
    }

    /**
     * Handle payment callback from MTN MoMo.
     */
    public function handleCallback(array $callbackData): Payment
    {
        DB::beginTransaction();
        
        try {
            $reference = $callbackData['reference'] ?? null;
            
            if (!$reference) {
                throw new \Exception('No reference in callback data');
            }

            $payment = Payment::where('reference', $reference)->first();

            if (!$payment) {
                throw new \Exception('Payment not found for reference: ' . $reference);
            }

            // Store callback data
            $payment->update([
                'callback_data' => $callbackData,
            ]);

            // Process based on status
            $status = $callbackData['status'] ?? null;

            if ($status === 'SUCCESSFUL') {
                $this->markAsSuccessful($payment, $callbackData);
            } elseif ($status === 'FAILED') {
                $this->markAsFailed($payment, $callbackData);
            }

            DB::commit();

            Log::info('Mentorship payment callback processed', [
                'payment_id' => $payment->id,
                'reference' => $reference,
                'status' => $status,
            ]);

            return $payment->fresh();
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to process mentorship payment callback', [
                'error' => $e->getMessage(),
                'callback_data' => $callbackData,
            ]);
            throw $e;
        }
    }

    /**
     * Mark payment as successful.
     */
    protected function markAsSuccessful(Payment $payment, array $data): void
    {
        $payment->markAsSuccessful(
            $data['transaction_id'] ?? $data['financialTransactionId'] ?? null,
            $data
        );

        // Update application
        $payment->application->markPaymentCompleted($payment->reference, $data);

        // Generate receipt number
        $payment->generateReceiptNumber();

        Log::info('Mentorship payment marked as successful', [
            'payment_id' => $payment->id,
            'application_id' => $payment->application_id,
        ]);
    }

    /**
     * Mark payment as failed.
     */
    protected function markAsFailed(Payment $payment, array $data): void
    {
        $payment->markAsFailed(
            $data['error_code'] ?? 'PAYMENT_FAILED',
            $data['error_message'] ?? $data['message'] ?? 'Payment failed'
        );

        Log::warning('Mentorship payment marked as failed', [
            'payment_id' => $payment->id,
            'application_id' => $payment->application_id,
            'error' => $data['error_message'] ?? 'Unknown error',
        ]);
    }

    /**
     * Retry a failed payment.
     */
    public function retry(Payment $payment, ?string $phone = null): array
    {
        if (!$payment->canBeRetried()) {
            return [
                'success' => false,
                'message' => 'This payment cannot be retried.',
            ];
        }

        // Record retry
        $payment->recordRetry('User requested retry');

        // Initiate new payment
        return $this->initiatePayment(
            $payment->application,
            $phone ?? $payment->phone_number
        );
    }

    /**
     * Get payments with filtering.
     */
    public function getPayments(array $filters = [], int $perPage = 15)
    {
        $query = Payment::with(['application', 'programme', 'user']);

        // Filter by status
        if (!empty($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        // Filter by programme
        if (!empty($filters['programme_id'])) {
            $query->where('programme_id', $filters['programme_id']);
        }

        // Filter by user
        if (!empty($filters['user_id'])) {
            $query->where('user_id', $filters['user_id']);
        }

        // Search
        if (!empty($filters['search'])) {
            $query->where(function ($q) use ($filters) {
                $q->where('reference', 'like', '%' . $filters['search'] . '%')
                    ->orWhere('transaction_id', 'like', '%' . $filters['search'] . '%');
            });
        }

        // Sort
        $sortBy = $filters['sort_by'] ?? 'created_at';
        $sortOrder = $filters['sort_order'] ?? 'desc';
        $query->orderBy($sortBy, $sortOrder);

        return $query->paginate($perPage);
    }

    /**
     * Get payment statistics.
     */
    public function getStatistics(?int $programmeId = null): array
    {
        $query = Payment::query();

        if ($programmeId) {
            $query->where('programme_id', $programmeId);
        }

        return [
            'total' => $query->count(),
            'successful' => $query->successful()->count(),
            'pending' => $query->pending()->count(),
            'failed' => $query->failed()->count(),
            'total_amount' => $query->successful()->sum('amount'),
            'pending_amount' => $query->pending()->sum('amount'),
        ];
    }

    /**
     * Generate receipt for a payment.
     */
    public function generateReceipt(Payment $payment): array
    {
        if (!$payment->receipt_number) {
            $payment->generateReceiptNumber();
        }

        return [
            'receipt_number' => $payment->receipt_number,
            'date' => $payment->completed_at?->format('d M Y H:i'),
            'programme' => $payment->programme->title,
            'applicant_name' => $payment->application->name,
            'applicant_email' => $payment->application->email,
            'amount' => $payment->formatted_amount,
            'payment_method' => 'MTN Mobile Money',
            'transaction_id' => $payment->transaction_id,
            'status' => $payment->status_label,
        ];
    }
}
