<?php

namespace App\Http\Controllers;

use App\Models\HealthInstitution;
use App\Models\DigitalMachine;
use App\Models\InstitutionPayment;
use App\Models\MachinePayment;
use App\Models\PaymentConfiguration;
use App\Services\PaymentService;
use App\Services\EmailService;
use App\Services\NotificationService;
use App\Services\SMSService;
use App\Http\Requests\ProcessPaymentRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * PaymentController
 *
 * Handles payment processing for institution and machine listings.
 * Integrates with MTN MoMo PaymentService for collection.
 *
 * Requirements: 2.1-2.5, 3.3-3.4, 9.1-9.5
 */
class PaymentController extends Controller
{
    protected PaymentService $paymentService;
    protected EmailService $emailService;
    protected SMSService $smsService;
    protected NotificationService $notificationService;

    public function __construct(
        PaymentService $paymentService,
        EmailService $emailService,
        SMSService $smsService,
        NotificationService $notificationService
    ) {
        $this->paymentService = $paymentService;
        $this->emailService = $emailService;
        $this->smsService = $smsService;
        $this->notificationService = $notificationService;
    }

    // =========================================================================
    // HEALTH MARKETOR METHODS
    // =========================================================================

    /**
     * Process payment for institution listing.
     *
     * Requirements: 2.3, 9.1
     */
    public function institutionPayment(ProcessPaymentRequest $request, HealthInstitution $institution)
    {
        abort_unless(
            $institution->isOwnedBy(auth()->user()) ||
            auth()->user()->hasAnyRole(['Super Manager', 'Manager']),
            403
        );

        // Check if payment service is ready
        if (!$this->paymentService->canCollect()) {
            return back()->with('error', 'Payment service is not available. Please try again later.');
        }

        DB::beginTransaction();
        try {
            $validated = $request->validated();
            $reference = InstitutionPayment::generateReference();

            // Calculate coverage period
            $paymentConfig = PaymentConfiguration::getForInstitutions();
            $coverageEnd = $paymentConfig->calculateCoverageEnd($validated['payment_frequency']);

            $paymentMethod = $validated['payment_method'] ?? 'mobile';
            $storedMethod  = $paymentMethod === 'card' ? 'mtn_momo_card' : 'mtn_momo_mobile';

            // Create payment record
            $payment = InstitutionPayment::create([
                'health_institution_id' => $institution->id,
                'user_id'               => auth()->id(),
                'amount'                => $validated['amount'],
                'currency'              => $validated['currency'],
                'payment_frequency'     => $validated['payment_frequency'],
                'payment_method'        => $storedMethod,
                'reference'             => $reference,
                'phone_number'          => $validated['phone_number'] ?? null,
                'payer_message'         => $validated['payer_message'] ?? 'Payment for institution listing',
                'payee_note'            => $validated['payee_note'] ?? 'Health Directory Listing',
                'status'                => 'pending',
                'initiated_at'          => now(),
                'coverage_start'        => now(),
                'coverage_end'          => $coverageEnd,
            ]);

            // Initiate MTN MoMo collection (mobile or card)
            if ($paymentMethod === 'card') {
                $result = $this->paymentService->initiateCardCollection(
                    cardNumber: $validated['card_number'],
                    expiry: $validated['card_expiry'],
                    cvv: $validated['card_cvv'],
                    amount: $validated['amount'],
                    reference: $reference,
                    payerMessage: $validated['payer_message'] ?? 'Payment for institution listing',
                    payeeNote: 'Health Directory - ' . $institution->name
                );
            } else {
                $result = $this->paymentService->initiateCollection(
                    phoneNumber: $validated['phone_number'],
                    amount: $validated['amount'],
                    reference: $reference,
                    payerMessage: $validated['payer_message'] ?? 'Payment for institution listing',
                    payeeNote: 'Health Directory - ' . $institution->name
                );
            }

            if ($result['success']) {
                $payment->update([
                    'transaction_id' => $result['transaction_id'] ?? null,
                    'status' => 'processing',
                    'status_message' => 'Payment request sent to phone',
                ]);

                // Update institution status
                $institution->update([
                    'listing_status' => 'pending_payment',
                    'payment_status' => 'pending',
                ]);

                DB::commit();

                return redirect()->route('payments.status', $payment)
                    ->with('info', 'Payment request sent! Please check your phone and approve the payment.');
            }

            // Payment initiation failed
            $payment->update([
                'status' => 'failed',
                'failure_reason' => $result['message'] ?? 'Payment initiation failed',
            ]);

            DB::rollBack();

            // Notify user of failure — best-effort, never blocks response
            try {
                $this->notificationService->sendHealthDirectoryPaymentFailed(
                    $payment->fresh()->load('user'),
                    'institution',
                    $result['message'] ?? 'Payment initiation failed'
                );
            } catch (\Throwable $e) {
                Log::error('Failed to send institution payment failure notification', [
                    'payment_id' => $payment->id,
                    'error'      => $e->getMessage(),
                ]);
            }

            return back()->with('error', $result['message'] ?? 'Failed to initiate payment. Please try again.');

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Institution payment failed', [
                'institution_id' => $institution->id,
                'error' => $e->getMessage()
            ]);

            return back()->with('error', 'Payment processing failed. Please try again.');
        }
    }

    /**
     * Process payment for machine listing.
     *
     * Requirements: 3.3, 3.4
     */
    public function machinePayment(ProcessPaymentRequest $request, DigitalMachine $machine)
    {
        abort_unless(
            $machine->isOwnedBy(auth()->user()) ||
            auth()->user()->hasAnyRole(['Super Manager', 'Manager']),
            403
        );

        // Check if payment service is ready
        if (!$this->paymentService->canCollect()) {
            return back()->with('error', 'Payment service is not available. Please try again later.');
        }

        DB::beginTransaction();
        try {
            $validated = $request->validated();
            $reference = MachinePayment::generateReference();

            // Calculate coverage period
            $paymentConfig = PaymentConfiguration::getForMachines();
            $coverageEnd = $paymentConfig->calculateCoverageEnd($validated['payment_frequency']);

            $paymentMethod = $validated['payment_method'] ?? 'mobile';
            $storedMethod  = $paymentMethod === 'card' ? 'mtn_momo_card' : 'mtn_momo_mobile';

            // Create payment record
            $payment = MachinePayment::create([
                'digital_machine_id' => $machine->id,
                'user_id'            => auth()->id(),
                'amount'             => $validated['amount'],
                'currency'           => $validated['currency'],
                'payment_frequency'  => $validated['payment_frequency'],
                'payment_method'     => $storedMethod,
                'reference'          => $reference,
                'phone_number'       => $validated['phone_number'] ?? null,
                'payer_message'      => $validated['payer_message'] ?? 'Payment for machine listing',
                'payee_note'         => $validated['payee_note'] ?? 'Health Machine Marketing',
                'status'             => 'pending',
                'initiated_at'       => now(),
                'coverage_start'     => now(),
                'coverage_end'       => $coverageEnd,
            ]);

            // Initiate MTN MoMo collection (mobile or card)
            if ($paymentMethod === 'card') {
                $result = $this->paymentService->initiateCardCollection(
                    cardNumber: $validated['card_number'],
                    expiry: $validated['card_expiry'],
                    cvv: $validated['card_cvv'],
                    amount: $validated['amount'],
                    reference: $reference,
                    payerMessage: $validated['payer_message'] ?? 'Payment for machine listing',
                    payeeNote: 'Health Machine - ' . $machine->name
                );
            } else {
                $result = $this->paymentService->initiateCollection(
                    phoneNumber: $validated['phone_number'],
                    amount: $validated['amount'],
                    reference: $reference,
                    payerMessage: $validated['payer_message'] ?? 'Payment for machine listing',
                    payeeNote: 'Health Machine - ' . $machine->name
                );
            }

            if ($result['success']) {
                $payment->update([
                    'transaction_id' => $result['transaction_id'] ?? null,
                    'status' => 'processing',
                    'status_message' => 'Payment request sent to phone',
                ]);

                DB::commit();

                return redirect()->route('payments.status', $payment)
                    ->with('info', 'Payment request sent! Please check your phone and approve the payment.');
            }

            $payment->update([
                'status' => 'failed',
                'failure_reason' => $result['message'] ?? 'Payment initiation failed',
            ]);

            DB::rollBack();

            // Notify user of failure — best-effort, never blocks response
            try {
                $this->notificationService->sendHealthDirectoryPaymentFailed(
                    $payment->fresh()->load('user'),
                    'machine',
                    $result['message'] ?? 'Payment initiation failed'
                );
            } catch (\Throwable $e) {
                Log::error('Failed to send machine payment failure notification', [
                    'payment_id' => $payment->id,
                    'error'      => $e->getMessage(),
                ]);
            }

            return back()->with('error', $result['message'] ?? 'Failed to initiate payment.');

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Machine payment failed', [
                'machine_id' => $machine->id,
                'error' => $e->getMessage()
            ]);

            return back()->with('error', 'Payment processing failed. Please try again.');
        }
    }

    /**
     * Check payment status (AJAX).
     */
    public function checkStatus(Request $request, $paymentId)
    {
        $paymentType = $request->get('type', 'institution');

        if ($paymentType === 'institution') {
            $payment = InstitutionPayment::findOrFail($paymentId);
        } else {
            $payment = MachinePayment::findOrFail($paymentId);
        }

        // If still pending/processing, check with MTN MoMo
        if ($payment->isPending()) {
            $result = $this->paymentService->checkTransactionStatus(
                transactionId: $payment->reference,
                type: 'collection'
            );

            if ($result['success']) {
                if ($result['status'] === 'SUCCESSFUL') {
                    $payment->markSuccessful($payment->transaction_id);

                    // Send confirmation
                    $this->sendPaymentConfirmation($payment);

                    return response()->json([
                        'success' => true,
                        'status' => 'successful',
                        'message' => 'Payment completed successfully!',
                    ]);
                } elseif ($result['status'] === 'FAILED') {
                    $payment->markFailed($result['message'] ?? 'Payment failed');

                    return response()->json([
                        'success' => false,
                        'status' => 'failed',
                        'message' => $result['message'] ?? 'Payment failed',
                    ]);
                }
            }
        }

        return response()->json([
            'success' => true,
            'status' => $payment->status,
            'message' => $payment->status_message,
        ]);
    }

    /**
     * Display payment status page.
     */
    public function status(Request $request, $paymentId)
    {
        $paymentType = $request->get('type', 'institution');

        if ($paymentType === 'institution') {
            $payment = InstitutionPayment::with('institution')->findOrFail($paymentId);
        } else {
            $payment = MachinePayment::with('machine')->findOrFail($paymentId);
        }

        return view('payments.status', compact('payment', 'paymentType'));
    }

    /**
     * Display payment history for Health Marketor.
     *
     * Requirements: 9.2, 9.3
     */
    public function history(Request $request)
    {
        $institutionPayments = InstitutionPayment::where('user_id', auth()->id())
            ->with('institution')
            ->latest()
            ->get();

        $machinePayments = MachinePayment::where('user_id', auth()->id())
            ->with('machine')
            ->latest()
            ->get();

        $stats = [
            'total_paid' => $institutionPayments->where('status', 'successful')->sum('amount') +
                           $machinePayments->where('status', 'successful')->sum('amount'),
            'total_transactions' => $institutionPayments->count() + $machinePayments->count(),
            'pending' => $institutionPayments->whereIn('status', ['pending', 'processing'])->count() +
                        $machinePayments->whereIn('status', ['pending', 'processing'])->count(),
        ];

        return view('payments.history', compact('institutionPayments', 'machinePayments', 'stats'));
    }

    // =========================================================================
    // ADMIN METHODS
    // =========================================================================

    /**
     * Admin: Display all payments.
     */
    public function adminIndex(Request $request)
    {
        abort_unless(auth()->user()->can('view payments'), 403);

        $institutionPayments = InstitutionPayment::with(['institution', 'user'])
            ->when($request->status, fn($q, $status) => $q->where('status', $status))
            ->when($request->search, function ($q, $search) {
                $q->where('reference', 'like', "%{$search}%")
                    ->orWhere('transaction_id', 'like', "%{$search}%");
            })
            ->latest()
            ->paginate(15, ['*'], 'institution_page');

        $machinePayments = MachinePayment::with(['machine', 'user'])
            ->when($request->status, fn($q, $status) => $q->where('status', $status))
            ->when($request->search, function ($q, $search) {
                $q->where('reference', 'like', "%{$search}%")
                    ->orWhere('transaction_id', 'like', "%{$search}%");
            })
            ->latest()
            ->paginate(15, ['*'], 'machine_page');

        $stats = [
            'institution_total' => InstitutionPayment::successful()->sum('amount'),
            'machine_total' => MachinePayment::successful()->sum('amount'),
            'pending_count' => InstitutionPayment::pending()->count() + MachinePayment::pending()->count(),
            'failed_count' => InstitutionPayment::failed()->count() + MachinePayment::failed()->count(),
        ];

        return view('payments.admin.index', compact(
            'institutionPayments',
            'machinePayments',
            'stats'
        ));
    }

    /**
     * Admin: Configure payment settings.
     *
     * Requirements: 2.1, 2.2
     */
    public function configure()
    {
        abort_unless(auth()->user()->can('configure payments'), 403);

        $institutionConfig = PaymentConfiguration::getForInstitutions();
        $machineConfig = PaymentConfiguration::getForMachines();
        $frequencies = PaymentConfiguration::getFrequencyOptions();

        return view('payments.admin.configure', compact(
            'institutionConfig',
            'machineConfig',
            'frequencies'
        ));
    }

    /**
     * Admin: Save payment configuration.
     *
     * Requirements: 2.1, 2.2
     */
    public function saveConfiguration(Request $request)
    {
        abort_unless(auth()->user()->can('configure payments'), 403);

        $request->validate([
            // Institution config
            'institution_is_free' => 'boolean',
            'institution_amount_ssp' => 'required_if:institution_is_free,false|numeric|min:0',
            'institution_amount_usd' => 'nullable|numeric|min:0',
            'institution_frequencies' => 'array',
            'institution_default_frequency' => 'string',
            'institution_grace_period' => 'integer|min:0|max:30',

            // Machine config
            'machine_is_free' => 'boolean',
            'machine_amount_ssp' => 'required_if:machine_is_free,false|numeric|min:0',
            'machine_amount_usd' => 'nullable|numeric|min:0',
            'machine_frequencies' => 'array',
            'machine_default_frequency' => 'string',
            'machine_grace_period' => 'integer|min:0|max:30',
        ]);

        // Update institution config
        $institutionConfig = PaymentConfiguration::firstOrCreate(
            ['service_type' => 'institution']
        );
        $institutionConfig->update([
            'is_free' => $request->boolean('institution_is_free'),
            'amount_ssp' => $request->institution_amount_ssp ?? 0,
            'amount_usd' => $request->institution_amount_usd,
            'frequencies' => $request->institution_frequencies ?? ['yearly'],
            'default_frequency' => $request->institution_default_frequency ?? 'yearly',
            'grace_period_days' => $request->institution_grace_period ?? 7,
        ]);

        // Update machine config
        $machineConfig = PaymentConfiguration::firstOrCreate(
            ['service_type' => 'machine']
        );
        $machineConfig->update([
            'is_free' => $request->boolean('machine_is_free'),
            'amount_ssp' => $request->machine_amount_ssp ?? 0,
            'amount_usd' => $request->machine_amount_usd,
            'frequencies' => $request->machine_frequencies ?? ['yearly'],
            'default_frequency' => $request->machine_default_frequency ?? 'yearly',
            'grace_period_days' => $request->machine_grace_period ?? 7,
        ]);

        return back()->with('success', 'Payment configuration saved successfully.');
    }

    // =========================================================================
    // CALLBACK HANDLERS (called by MomoCallbackController)
    // =========================================================================

    /**
     * Handle institution payment callback from MTN MoMo.
     *
     * Requirements: 1.3, 9.1
     */
    public function handleInstitutionCallback(string $reference, string $status, array $data = []): void
    {
        $payment = InstitutionPayment::where('reference', $reference)->first();

        if (!$payment) {
            Log::warning('PaymentController: InstitutionPayment not found for callback', ['reference' => $reference]);
            return;
        }

        if ($status === 'SUCCESSFUL') {
            $payment->markSuccessful($data['financialTransactionId'] ?? $data['transaction_id'] ?? '');
            Log::info('Institution payment confirmed via callback', ['reference' => $reference]);
        } elseif (in_array($status, ['FAILED', 'REJECTED'])) {
            $reason = $data['reason'] ?? 'Payment ' . strtolower($status);
            $payment->markFailed($reason);

            try {
                $this->notificationService->sendHealthDirectoryPaymentFailed(
                    $payment->fresh()->load('user'),
                    'institution',
                    $reason
                );
            } catch (\Throwable $e) {
                Log::error('Failed to send institution payment failure notification via callback', [
                    'payment_id' => $payment->id,
                    'error'      => $e->getMessage(),
                ]);
            }

            Log::info('Institution payment failed via callback', ['reference' => $reference, 'status' => $status]);
        }
    }

    /**
     * Handle machine payment callback from MTN MoMo.
     *
     * Requirements: 1.3, 9.2
     */
    public function handleMachineCallback(string $reference, string $status, array $data = []): void
    {
        $payment = MachinePayment::where('reference', $reference)->first();

        if (!$payment) {
            Log::warning('PaymentController: MachinePayment not found for callback', ['reference' => $reference]);
            return;
        }

        if ($status === 'SUCCESSFUL') {
            $payment->markSuccessful($data['financialTransactionId'] ?? $data['transaction_id'] ?? '');
            Log::info('Machine payment confirmed via callback', ['reference' => $reference]);
        } elseif (in_array($status, ['FAILED', 'REJECTED'])) {
            $reason = $data['reason'] ?? 'Payment ' . strtolower($status);
            $payment->markFailed($reason);

            try {
                $this->notificationService->sendHealthDirectoryPaymentFailed(
                    $payment->fresh()->load('user'),
                    'machine',
                    $reason
                );
            } catch (\Throwable $e) {
                Log::error('Failed to send machine payment failure notification via callback', [
                    'payment_id' => $payment->id,
                    'error'      => $e->getMessage(),
                ]);
            }

            Log::info('Machine payment failed via callback', ['reference' => $reference, 'status' => $status]);
        }
    }

    // =========================================================================
    // HELPER METHODS
    // =========================================================================

    /**
     * Send payment confirmation notification.
     *
     * Requirements: 9.4
     */
    protected function sendPaymentConfirmation($payment): void
    {
        $user = $payment->user;
        $entity = $payment->institution ?? $payment->machine;

        // Send email confirmation
        if ($this->emailService->isReady() && $user) {
            $this->emailService->sendTemplate(
                $user->email,
                'Payment Confirmation - Health Directory',
                'emails.payment-confirmation',
                [
                    'userName' => $user->name,
                    'amount' => $payment->formatted_amount,
                    'entityName' => $entity->name ?? 'Your listing',
                    'coverageEnd' => $payment->coverage_end?->format('F j, Y'),
                    'reference' => $payment->reference,
                ]
            );
        }

        // Send SMS confirmation
        if ($user?->phone && $this->smsService->isAvailable()) {
            $this->smsService->sendTemplate(
                $user->phone,
                'Payment of {amount} confirmed for {name}. Reference: {ref}',
                [
                    'amount' => $payment->formatted_amount,
                    'name' => $entity->name ?? 'your listing',
                    'ref' => $payment->reference,
                ]
            );
        }
    }

    /**
     * Send payment renewal reminder.
     *
     * Requirements: 9.4
     */
    public function sendRenewalReminder(Request $request)
    {
        abort_unless(auth()->user()->hasAnyRole(['Super Manager', 'Manager']), 403);

        $type = $request->get('type', 'institution');
        $id = $request->get('id');

        if ($type === 'institution') {
            $entity = HealthInstitution::findOrFail($id);
        } else {
            $entity = DigitalMachine::findOrFail($id);
        }

        $user = $entity->user;

        if (!$user) {
            return back()->with('error', 'No owner assigned to this listing.');
        }

        // Send renewal reminder email
        if ($this->emailService->isReady()) {
            $this->emailService->sendTemplate(
                $user->email,
                'Renewal Reminder - Health Directory',
                'emails.renewal-reminder',
                [
                    'userName' => $user->name,
                    'entityName' => $entity->name,
                    'expiresAt' => $entity->expires_at?->format('F j, Y'),
                    'renewalUrl' => route($type === 'institution' ? 'institutions.renew' : 'machines.renew', $entity),
                ]
            );
        }

        // Send SMS reminder
        if ($user->phone && $this->smsService->isAvailable()) {
            $this->smsService->sendTemplate(
                $user->phone,
                'Your listing for {name} expires on {date}. Renew now to keep it active.',
                [
                    'name' => $entity->name,
                    'date' => $entity->expires_at?->format('M j, Y') ?? 'soon',
                ]
            );
        }

        return back()->with('success', 'Renewal reminder sent successfully.');
    }
}
