<?php

namespace App\Http\Controllers;

use App\Models\Payment;
use App\Models\PaymentMethod;
use App\Models\Subscription;
use App\Services\ActionNotificationService;
use Illuminate\Http\Request;

class PaymentController extends Controller
{
    /**
     * Display payment dashboard (admin).
     */
    public function index()
    {
        $payments = Payment::with('subscription', 'paymentMethod')
            ->latest()
            ->paginate(20);

        $stats = [
            'total' => Payment::sum('amount'),
            'pending' => Payment::where('status', 'pending')->sum('amount'),
            'completed' => Payment::where('status', 'completed')->sum('amount'),
            'failed' => Payment::where('status', 'failed')->count(),
        ];

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

    /**
     * Show payment details.
     */
    public function show(Payment $payment)
    {
        if (request()->routeIs('admin.payments.*')) {
            $payment->load(['subscription.user.company', 'subscription.package', 'paymentMethod']);

            return view('admin.payments.show', compact('payment'));
        }

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

    /**
     * Display checkout page.
     */
    public function checkout(Subscription $subscription)
    {
        $this->authorize('update', $subscription);

        if ($subscription->status !== 'pending' || $subscription->payment_status !== 'pending') {
            return redirect()->route('subscriptions.index')
                ->with('error', 'This subscription is no longer pending payment');
        }

        $paymentMethods = PaymentMethod::where('is_active', true)->get();

        return view('payments.checkout', compact('subscription', 'paymentMethods'));
    }

    /**
     * Process payment.
     */
    public function process(Request $request, ActionNotificationService $notifications)
    {
        $validated = $request->validate([
            'subscription_id' => 'required|exists:subscriptions,id',
            'payment_method_id' => 'required|exists:payment_methods,id',
        ]);

        $subscription = Subscription::findOrFail($validated['subscription_id']);
        $this->authorize('update', $subscription);

        $paymentMethod = PaymentMethod::findOrFail($validated['payment_method_id']);

        if (!$paymentMethod->is_active) {
            return redirect()->back()
                ->with('error', 'Selected payment method is not available');
        }

        // Create payment record
        $payment = $subscription->payments()->create([
            'payment_method_id' => $paymentMethod->id,
            'amount' => $subscription->trial_days > 0 ? 0 : $subscription->package->price,
            'status' => 'pending',
        ]);

        $notifications->notifyAction(
            'Payment Initiated',
            $request->user(),
            [
                'channel' => 'web',
                'subscription_id' => $subscription->id,
                'payment_id' => $payment->id,
                'amount' => $payment->amount,
                'provider' => $paymentMethod->provider,
            ]
        );

        // Process payment based on provider
        try {
            $result = match ($paymentMethod->provider) {
                'stripe' => $subscription->trial_days > 0
                    ? $this->processStripeTrialSetup($payment, $paymentMethod)
                    : $this->processStripePayment($payment, $paymentMethod),
                'paypal' => $this->processPayPalPayment($payment, $paymentMethod),
                'wise' => $this->processWisePayment($payment, $paymentMethod),
                default => throw new \Exception('Unknown payment provider'),
            };

            return $result;
        } catch (\Exception $e) {
            $payment->update([
                'status' => 'failed',
                'failure_reason' => $e->getMessage(),
            ]);

            return redirect()->route('payments.checkout', ['subscription' => $subscription->id])
                ->with('error', "Payment processing failed: {$e->getMessage()}");
        }
    }

    /**
     * Payment success callback.
     */
    public function success(Payment $payment, Request $request)
    {
        $this->authorize('view', $payment->subscription);

        if ($payment->status !== 'completed' && $payment->paymentMethod?->provider === 'stripe' && $request->filled('session_id')) {
            try {
                $this->confirmStripeCheckoutSession($payment, (string) $request->input('session_id'));
                $payment->refresh();
            } catch (\Throwable $e) {
                return redirect()->route('subscriptions.index')
                    ->with('error', 'Payment return detected, but Stripe has not confirmed it yet: ' . $e->getMessage());
            }
        }

        if ($payment->status !== 'completed') {
            return redirect()->route('subscriptions.index')
                ->with('error', 'Payment not confirmed');
        }

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

    /**
     * Payment cancellation callback.
     */
    public function cancel(Payment $payment, ActionNotificationService $notifications)
    {
        $this->authorize('update', $payment->subscription);

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

        $notifications->notifyAction(
            'Payment Cancelled',
            request()->user(),
            [
                'channel' => 'web',
                'payment_id' => $payment->id,
                'subscription_id' => $payment->subscription_id,
                'amount' => $payment->amount,
            ]
        );

        return redirect()->route('payments.checkout', ['subscription' => $payment->subscription_id])
            ->with('error', 'Payment was cancelled');
    }

    /**
     * Update payment status (admin).
     */
    public function updateStatus(Request $request, Payment $payment, ActionNotificationService $notifications)
    {
        $validated = $request->validate([
            'status' => 'required|in:pending,completed,failed,refunded',
        ]);

        $oldStatus = $payment->status;
        $payment->update($validated);

        // Update subscription status based on payment status
        if ($validated['status'] === 'completed' && $oldStatus !== 'completed') {
            $payment->subscription->update([
                'status' => 'active',
                'payment_status' => 'completed',
                'amount_paid' => $payment->amount,
            ]);
        } elseif ($validated['status'] === 'failed' || $validated['status'] === 'refunded') {
            $payment->subscription->update([
                'payment_status' => $validated['status'],
            ]);
        }

        $notifications->notifyAction(
            'Payment Status Updated',
            $request->user(),
            [
                'channel' => 'web',
                'payment_id' => $payment->id,
                'old_status' => $oldStatus,
                'new_status' => $validated['status'],
                'subscription_id' => $payment->subscription_id,
            ]
        );

        return redirect()->back()
            ->with('success', "Payment status updated to {$validated['status']}");
    }

    /**
     * Process Stripe payment.
     */
    protected function processStripePayment(Payment $payment, PaymentMethod $paymentMethod)
    {
        $credentials = $paymentMethod->getCredentials();
        $apiKey = $credentials['api_key'] ?? null;

        if (!$apiKey) {
            throw new \Exception('Stripe API key not configured');
        }

        if ($this->isDemoStripeMode($apiKey)) {
            $this->completePayment($payment, 'demo_payment_' . $payment->id, [
                'mode' => 'demo',
                'provider' => 'stripe',
            ]);

            return redirect()->route('payments.success', ['payment' => $payment->id])
                ->with('success', 'Demo Stripe payment completed successfully.');
        }

        \Stripe\Stripe::setApiKey($apiKey);

        try {
            $session = \Stripe\Checkout\Session::create([
                'mode' => 'payment',
                'customer_email' => $payment->subscription->user->email,
                'line_items' => [[
                    'quantity' => 1,
                    'price_data' => [
                        'currency' => 'usd',
                        'unit_amount' => (int) round($payment->amount * 100),
                        'product_data' => [
                            'name' => 'TrueTrucker - ' . $payment->subscription->package->name,
                            'description' => 'Subscription payment for ' . $payment->subscription->package->name,
                        ],
                    ],
                ]],
                'success_url' => route('payments.success', ['payment' => $payment->id]) . '?session_id={CHECKOUT_SESSION_ID}',
                'cancel_url' => route('payments.cancel', ['payment' => $payment->id]),
                'metadata' => [
                    'payment_id' => $payment->id,
                    'subscription_id' => $payment->subscription_id,
                ],
            ]);

            $payment->update([
                'transaction_id' => $session->id,
                'metadata' => array_merge($payment->metadata ?? [], [
                    'provider' => 'stripe',
                    'checkout_session_id' => $session->id,
                    'checkout_url' => $session->url,
                ]),
            ]);

            return redirect()->away($session->url);
        } catch (\Stripe\Exception\ApiErrorException $e) {
            throw new \Exception("Stripe error: {$e->getMessage()}");
        }
    }

    protected function processStripeTrialSetup(Payment $payment, PaymentMethod $paymentMethod)
    {
        $credentials = $paymentMethod->getCredentials();
        $apiKey = $credentials['api_key'] ?? null;

        if (!$apiKey) {
            throw new \Exception('Stripe API key not configured');
        }

        if ($this->isDemoStripeMode($apiKey)) {
            $this->completePayment($payment, 'demo_setup_' . $payment->id, [
                'mode' => 'demo-setup',
                'provider' => 'stripe',
            ]);

            return redirect()->route('payments.success', ['payment' => $payment->id])
                ->with('success', 'Demo Stripe payment method setup completed successfully.');
        }

        \Stripe\Stripe::setApiKey($apiKey);

        try {
            $subscription = $payment->subscription;
            $stripeService = app(\App\Services\StripeService::class);
            $session = $stripeService->createSetupSession($subscription, $payment);

            return redirect()->away($session->url);
        } catch (\Stripe\Exception\ApiErrorException $e) {
            throw new \Exception("Stripe error: {$e->getMessage()}");
        }
    }

    private function isDemoStripeMode(string $apiKey): bool
    {
        return app()->environment(['local', 'testing']) || str_contains($apiKey, 'demo');
    }

    private function confirmStripeCheckoutSession(Payment $payment, string $sessionId): void
    {
        $apiKey = $payment->paymentMethod?->getCredentials()['api_key'] ?? null;

        if (!$apiKey) {
            throw new \Exception('Stripe API key not configured');
        }

        if ($this->isDemoStripeMode($apiKey)) {
            $this->completePayment($payment, $sessionId, [
                'mode' => 'demo-confirmed',
                'checkout_session_id' => $sessionId,
            ]);
            return;
        }

        \Stripe\Stripe::setApiKey($apiKey);
        $session = \Stripe\Checkout\Session::retrieve($sessionId, ['expand' => ['setup_intent', 'subscription', 'payment_intent']]);

        $transactionId = $session->payment_intent ?? $session->setup_intent?->id ?? $session->id;
        $metadata = [
            'provider' => 'stripe',
            'checkout_session_id' => $session->id,
        ];

        if ($session->mode === 'setup' && $session->setup_intent) {
            $metadata['setup_intent_id'] = $session->setup_intent->id;
        }

        if ($session->customer) {
            $payment->subscription->update(['stripe_customer_id' => $session->customer]);
        }

        if ($session->subscription) {
            $payment->subscription->update(['stripe_subscription_id' => $session->subscription]);
            $metadata['stripe_subscription_id'] = $session->subscription;
        }

        if ($session->mode === 'setup' || ($session->payment_status ?? null) === 'paid') {
            $this->completePayment($payment, $transactionId, $metadata);
            return;
        }

        throw new \Exception('Stripe session is not complete yet.');
    }

    private function completePayment(Payment $payment, ?string $transactionId = null, array $metadata = []): void
    {
        $payment->update([
            'status' => 'completed',
            'transaction_id' => $transactionId ?? $payment->transaction_id,
            'failure_reason' => null,
            'processed_at' => now(),
            'metadata' => array_merge($payment->metadata ?? [], $metadata),
        ]);

        $subscription = $payment->subscription;
        $updateData = [
            'status' => 'active',
            'payment_status' => 'completed',
            'amount_paid' => (float) ($subscription->amount_paid ?? 0) + (float) $payment->amount,
        ];

        if (! $subscription->trial_days || ! $subscription->trial_ends_at) {
            $updateData['next_billing_date'] = now()->add($subscription->billing_cycle === 'weekly' ? \DateInterval::createFromDateString('1 week') : \DateInterval::createFromDateString('1 month'));
        }

        $subscription->update($updateData);
    }

    /**
     * Process PayPal payment.
     */
    protected function processPayPalPayment(Payment $payment, PaymentMethod $paymentMethod)
    {
        $credentials = $paymentMethod->getCredentials();

        if (!isset($credentials['client_id']) || !isset($credentials['client_secret'])) {
            throw new \Exception('PayPal credentials not configured');
        }

        // You would integrate with PayPal SDK here
        // For now, this is a placeholder
        throw new \Exception('PayPal integration not yet implemented');
    }

    /**
     * Process Wise payment.
     */
    protected function processWisePayment(Payment $payment, PaymentMethod $paymentMethod)
    {
        $credentials = $paymentMethod->getCredentials();

        if (!isset($credentials['api_token'])) {
            throw new \Exception('Wise API token not configured');
        }

        // You would integrate with Wise API here
        // For now, this is a placeholder
        throw new \Exception('Wise integration not yet implemented');
    }

    /**
     * Create Stripe subscription.
     */
    public function createStripeSubscription(Request $request, Subscription $subscription)
    {
        $this->authorize('update', $subscription);

        if (!$subscription->isPending()) {
            return redirect()->route('subscriptions.index')
                ->with('error', 'Subscription is not pending');
        }

        try {
            $stripeService = app(\App\Services\StripeService::class);

            // Create Stripe subscription
            $stripeSubscriptionId = $stripeService->createSubscription($subscription);

            // Update subscription record
            $subscription->update([
                'stripe_subscription_id' => $stripeSubscriptionId,
                'status' => 'active',
                'payment_status' => 'completed',
                'billing_cycle' => $subscription->package->billing_cycle ?? 'monthly',
                'next_billing_date' => now()->addMonth(),
            ]);

            return redirect()->route('subscriptions.show', ['subscription' => $subscription->id])
                ->with('success', 'Stripe subscription created successfully');
        } catch (\Exception $e) {
            \Log::error("Failed to create Stripe subscription: {$e->getMessage()}");

            return redirect()->back()
                ->with('error', "Failed to create subscription: {$e->getMessage()}");
        }
    }

    /**
     * Update subscription when driver count changes.
     */
    public function updateSubscriptionDriverChanges(Subscription $subscription)
    {
        $this->authorize('update', $subscription);

        try {
            $stripeService = app(\App\Services\StripeService::class);
            $billingService = app(\App\Services\BillingService::class);

            $stripeService->updateSubscriptionForDriverChange($subscription);
            $billingService->handleDriverCountChange($subscription);

            return back()->with('success', 'Subscription charges updated for new driver count');
        } catch (\Exception $e) {
            return back()->with('error', 'Failed to update subscription');
        }
    }

    /**
     * Retry failed payment.
     */
    public function retryPayment(Payment $payment)
    {
        $this->authorize('update', $payment->subscription);

        if ($payment->status !== 'failed') {
            return back()->with('error', 'Only failed payments can be retried');
        }

        try {
            $stripeService = app(\App\Services\StripeService::class);
            $stripeService->retryPayment($payment);

            return back()->with('success', 'Payment retry initiated');
        } catch (\Exception $e) {
            return back()->with('error', $e->getMessage());
        }
    }

    /**
     * Handle Stripe invoice webhook.
     */
    public function stripeWebhook(Request $request)
    {
        $payload = $request->getContent();
        $sig = $request->header('Stripe-Signature');

        try {
            $event = \Stripe\Webhook::constructEvent(
                $payload,
                $sig,
                config('services.stripe.webhook_secret')
            );

            $stripeService = app(\App\Services\StripeService::class);

            switch ($event->type) {
                case 'payment_intent.succeeded':
                    $this->handleStripePaymentSuccess($event->data->object);
                    break;
                case 'payment_intent.payment_failed':
                    $this->handleStripePaymentFailed($event->data->object);
                    break;
                case 'invoice.payment_succeeded':
                    $stripeService->handlePaymentSuccess($event->data->object->id);
                    break;
                case 'invoice.payment_failed':
                    $stripeService->handlePaymentFailure(
                        $event->data->object->id,
                        $event->data->object->last_payment_error?->message
                    );
                    break;
            }

            return response()->json(['success' => true]);
        } catch (\UnexpectedValueException $e) {
            return response()->json(['error' => 'Invalid payload'], 400);
        } catch (\Stripe\Exception\SignatureVerificationException $e) {
            return response()->json(['error' => 'Invalid signature'], 400);
        }
    }

    /**
     * Handle PayPal webhook.
     */
    public function paypalWebhook(Request $request)
    {
        // Implement PayPal webhook handling
        return response()->json(['success' => true]);
    }

    /**
     * Handle Wise webhook.
     */
    public function wiseWebhook(Request $request)
    {
        // Implement Wise webhook handling
        return response()->json(['success' => true]);
    }

    /**
     * Handle successful Stripe payment.
     */
    protected function handleStripePaymentSuccess($intent)
    {
        if (isset($intent->metadata->payment_id)) {
            $payment = Payment::find($intent->metadata->payment_id);

            if ($payment) {
                $payment->update([
                    'status' => 'completed',
                    'processed_at' => now(),
                ]);

                // Update subscription
                $payment->subscription->update([
                    'status' => 'active',
                    'payment_status' => 'completed',
                    'amount_paid' => $payment->amount,
                ]);
            }
        }
    }

    /**
     * Handle failed Stripe payment.
     */
    protected function handleStripePaymentFailed($intent)
    {
        if (isset($intent->metadata->payment_id)) {
            $payment = Payment::find($intent->metadata->payment_id);

            if ($payment) {
                $payment->update([
                    'status' => 'failed',
                    'failure_reason' => 'Payment declined by card processor',
                ]);
            }
        }
    }
}
