<?php

namespace App\Http\Controllers\User;

use App\Http\Controllers\Controller;
use App\Models\Payment;
use App\Models\Referral;
use App\Models\Subscription;
use HopekellDev\Paystack\Paystack;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\View\View;

class PaymentController extends Controller
{
    public function index(Request $request): View
    {
        $payments = Payment::where('user_id', $request->user()->id)
            ->latest()
            ->paginate(15);

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

    public function show(Request $request, string $encryptedId): View
    {
        $subscription = $this->getSubscription($request, $encryptedId);

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

    public function checkout(Request $request, string $encryptedId): JsonResponse
    {
        $subscription = $this->getSubscription($request, $encryptedId);

        if ($subscription->status !== 'pending') {
            return response()->json([
                'status' => false,
                'message' => 'This subscription is no longer pending.',
            ], 422);
        }

        if ((float) $subscription->amount <= 0) {
            $this->activateSubscription($subscription, null);

            return response()->json([
                'status' => true,
                'message' => 'Free plan activated successfully.',
                'redirect' => route('user.subscriptions.index'),
            ]);
        }

        $payment = $subscription->payment;

        if (!$payment || $payment->status !== 'pending') {
            $payment = Payment::create([
                'user_id' => $request->user()->id,
                'reference' => 'PSK-' . strtoupper(Str::random(16)),
                'gateway' => 'paystack',
                'amount' => $subscription->amount,
                'currency' => $subscription->currency ?? 'NGN',
                'status' => 'pending',
                'purpose' => 'subscription',
                'meta' => [
                    'subscription_id' => $subscription->id,
                    'plan_id' => $subscription->plan_id,
                ],
            ]);

            $subscription->update([
                'payment_id' => $payment->id,
            ]);
        }

        $response = Paystack::transactions()->initialize([
            'email' => $request->user()->email,
            'amount' => (int) round($payment->amount * 100),
            'currency' => $payment->currency ?? 'NGN',
            'reference' => $payment->reference,
            'callback_url' => route('user.payments.callback'),
            'metadata' => [
                'payment_id' => $payment->id,
                'subscription_id' => $subscription->id,
                'user_id' => $request->user()->id,
                'plan_id' => $subscription->plan_id,
            ],
        ]);

        $payload = $this->normalizePaystackResponse($response);

        if (!($payload['status'] ?? false)) {
            $payment->update([
                'status' => 'failed',
                'meta' => array_merge($payment->meta ?? [], [
                    'paystack_initialize_response' => $payload,
                ]),
            ]);

            return response()->json([
                'status' => false,
                'message' => $payload['message'] ?? 'Unable to initialize Paystack checkout.',
            ], 422);
        }

        $authorizationUrl = data_get($payload, 'data.authorization_url');

        if (!$authorizationUrl) {
            return response()->json([
                'status' => false,
                'message' => 'Paystack did not return a checkout URL.',
            ], 422);
        }

        $payment->update([
            'status' => 'pending',
            'meta' => array_merge($payment->meta ?? [], [
                'authorization_url' => $authorizationUrl,
                'access_code' => data_get($payload, 'data.access_code'),
                'paystack_initialize_response' => $payload,
            ]),
        ]);

        return response()->json([
            'status' => true,
            'message' => 'Checkout initialized successfully.',
            'checkout_url' => $authorizationUrl,
        ]);
    }

    public function status(Request $request, string $encryptedId): JsonResponse
    {
        $subscription = $this->getSubscription($request, $encryptedId);

        if ($subscription->status === 'active') {
            return response()->json([
                'paid' => true,
                'message' => 'Subscription activated successfully.',
                'redirect' => route('user.subscriptions.index'),
            ]);
        }

        $payment = $subscription->payment;

        if (!$payment || $payment->status !== 'pending') {
            return response()->json([
                'paid' => false,
                'message' => 'Payment is still pending.',
            ]);
        }

        $this->verifyPayment($payment);

        $subscription->refresh();

        return response()->json([
            'paid' => $subscription->status === 'active',
            'message' => $subscription->status === 'active'
                ? 'Subscription activated successfully.'
                : 'Payment is still pending.',
            'redirect' => route('user.subscriptions.index'),
        ]);
    }

    public function cancel(Request $request, string $encryptedId): JsonResponse
    {
        $subscription = $this->getSubscription($request, $encryptedId);

        if ($subscription->status !== 'pending') {
            return response()->json([
                'status' => false,
                'message' => 'Only pending subscriptions can be cancelled.',
            ], 422);
        }

        DB::transaction(function () use ($subscription) {
            if ($subscription->payment && $subscription->payment->status === 'pending') {
                $subscription->payment->update([
                    'status' => 'cancelled',
                ]);
            }

            $subscription->update([
                'status' => 'cancelled',
            ]);
        });

        return response()->json([
            'status' => true,
            'message' => 'Pending subscription cancelled successfully.',
            'redirect' => route('user.subscriptions.index'),
        ]);
    }

    public function callback(Request $request): RedirectResponse
    {
        $reference = $request->query('reference');

        if (!$reference) {
            return redirect()
                ->route('user.subscriptions.index')
                ->with('error', 'Payment reference missing.');
        }

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

        if (!$payment) {
            return redirect()
                ->route('user.subscriptions.index')
                ->with('error', 'Payment record not found.');
        }

        $this->verifyPayment($payment);

        return redirect()
            ->route('user.subscriptions.index')
            ->with('success', 'Payment verification completed.');
    }

    private function getSubscription(Request $request, string $encryptedId): Subscription
    {
        try {
            $id = decrypt($encryptedId);
        } catch (\Throwable $e) {
            abort(404);
        }

        return Subscription::with(['plan', 'payment', 'user'])
            ->where('id', $id)
            ->where('user_id', $request->user()->id)
            ->firstOrFail();
    }

    private function verifyPayment(Payment $payment): void
    {
        if ($payment->status === 'success') {
            return;
        }

        $response = Paystack::transactions()->verify($payment->reference);
        $payload = $this->normalizePaystackResponse($response);

        if (!($payload['status'] ?? false)) {
            return;
        }

        $data = data_get($payload, 'data');

        if (data_get($data, 'status') !== 'success') {
            return;
        }

        DB::transaction(function () use ($payment, $payload) {
            $payment->update([
                'status' => 'success',
                'paid_at' => now(),
                'meta' => array_merge($payment->meta ?? [], [
                    'paystack_verify_response' => $payload,
                ]),
            ]);

            $subscriptionId = data_get($payment->meta, 'subscription_id');

            $subscription = Subscription::where('id', $subscriptionId)
                ->where('user_id', $payment->user_id)
                ->first();

            if ($subscription && $subscription->status === 'pending') {
                $this->activateSubscription($subscription, $payment);
            }
        });
    }

    private function activateSubscription(Subscription $subscription, ?Payment $payment): void
    {
        Subscription::where('user_id', $subscription->user_id)
            ->where('id', '!=', $subscription->id)
            ->where('status', 'active')
            ->update([
                'status' => 'expired',
            ]);

        $subscription->update([
            'payment_id' => $payment?->id ?? $subscription->payment_id,
            'starts_at' => now(),
            'expires_at' => now()->addMonths(6),
            'status' => 'active',
        ]);

        $this->rewardReferral($subscription);
    }

    private function rewardReferral(Subscription $subscription): void
    {
        if ((float) $subscription->amount <= 0) {
            return;
        }

        $previousPaidSubscription = Subscription::where('user_id', $subscription->user_id)
            ->where('id', '!=', $subscription->id)
            ->where('status', 'active')
            ->where('amount', '>', 0)
            ->exists();

        if ($previousPaidSubscription) {
            return;
        }

        $referral = Referral::with('referrer')
            ->where('referred_id', $subscription->user_id)
            ->where('status', 'pending')
            ->first();

        if (!$referral || !$referral->referrer) {
            return;
        }

        $reward = 500;

        $referral->referrer->increment('wallet_balance', $reward);

        $referral->update([
            'reward_type' => 'first_subscription',
            'reward_value' => $reward,
            'status' => 'rewarded',
            'rewarded_at' => now(),
        ]);
    }

    private function normalizePaystackResponse($response): array
    {
        if (is_array($response)) {
            return $response;
        }

        if (is_object($response) && method_exists($response, 'json')) {
            return $response->json() ?? [];
        }

        if (is_object($response) && method_exists($response, 'toArray')) {
            return $response->toArray();
        }

        return [];
    }
}