<?php

namespace Modules\PaystackGateway\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\Subscription;
use App\Models\SubscriptionPlan;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Modules\PaystackGateway\Services\PaystackService;
use App\Services\CreditService;
use App\Services\WalletService; // Added

class PaystackSubscriptionController extends Controller
{
    protected PaystackService $paystackService;
    protected CreditService $creditService;
    protected WalletService $walletService; // Added

    public function __construct(PaystackService $paystackService, CreditService $creditService, WalletService $walletService)
    {
        $this->paystackService = $paystackService;
        $this->creditService = $creditService;
        $this->walletService = $walletService; // Added
    }

    public function initializePayment(Request $request, SubscriptionPlan $plan)
    {
        // --- Test with a Direct Eloquent Query ---
        $slugFromRoute = $request->route('subscriptionPlan'); // Get the raw slug or resolved model
        if (is_object($slugFromRoute) && $slugFromRoute instanceof SubscriptionPlan) {
            // If RMB resolved it (even to an empty model), get the slug it tried to use
            $slugValueToQuery = $slugFromRoute->slug ?? $request->segment(count($request->segments()));
        } else {
            $slugValueToQuery = (string) $slugFromRoute;
        }
        $directPlan = \App\Models\SubscriptionPlan::where('slug', $slugValueToQuery)->first();
        Log::debug("Paystack Initialize: Direct Eloquent query for slug '{$slugValueToQuery}'", [
            'found_plan_id_direct_query' => $directPlan ? $directPlan->id : 'NOT FOUND BY DIRECT QUERY',
            'direct_plan_attributes' => $directPlan ? $directPlan->getAttributes() : null,
        ]);
        // --- End Test with a Direct Eloquent Query ---

        // Log the incoming plan object immediately to see what Route Model Binding resolved
        if (!$plan->exists) { // Check if the model instance is "empty" (not found by RMB)
            Log::error("Paystack Initialize: Route Model Binding failed to find a SubscriptionPlan for the provided slug.", [
                'route_param_slug_from_request' => $request->route('subscriptionPlan'),
                'plan_object_class_on_failure' => get_class($plan) // Log the class of the $plan object
            ]);
            // Optionally, redirect or abort if plan not found, as further processing will fail.
            return redirect()->route('subscription.plans')->with('error', 'The selected subscription plan could not be found.');
        }
        Log::debug("Paystack Initialize: Entered initializePayment method.", [
            'route_param_slug' => $request->route('subscriptionPlan') instanceof SubscriptionPlan ? $request->route('subscriptionPlan')->slug : $request->route('subscriptionPlan'), // Get the raw slug from route
            'plan_id_resolved' => $plan->id ?? 'PLAN ID IS NULL',
            'plan_slug_resolved' => $plan->slug ?? 'PLAN SLUG IS NULL',
            'plan_name_resolved' => $plan->name ?? 'PLAN NAME IS NULL',
            'plan_price_resolved' => $plan->price ?? 'PLAN PRICE IS NULL',
            'plan_currency_resolved' => $plan->currency ?? 'PLAN CURRENCY IS NULL',
            'is_plan_model_empty' => $plan->exists === false, // Check if it's an empty model instance
            'plan_object_attributes' => $plan ? $plan->getAttributes() : null
        ]);

        if (setting('paystack_enabled', '0') != '1') {
            return redirect()->route('subscription.plans')->with('error', 'Paystack payments are currently disabled.');
        }

        /** @var User $user */
        $user = Auth::user();

        // Check for existing active subscription (similar to PayPal)
        $activeSubscription = $user->currentSubscription();

        if ($activeSubscription) {
            if ($activeSubscription->subscription_plan_id == $plan->id && $activeSubscription->payment_gateway == 'paystackgateway') {
                 return redirect()->route('subscription.plans')->with('info', 'You are already subscribed to this plan via Paystack.');
            } elseif ($activeSubscription->subscription_plan_id != $plan->id) {
                // User has an active subscription to a DIFFERENT plan. Allow them to proceed.
                // Inform them that their current plan will be cancelled if the new subscription is successful.
                session()->flash('info_plan_switch', "You are switching from '{$activeSubscription->plan->name}' to '{$plan->name}'. Your current plan will be cancelled upon successful activation of the new plan.");
            }
        }

        try {
            // $amountInKobo = $plan->price * 100; // Paystack service will handle amount based on plan

            // Create a pending subscription record
            $pendingSubscription = null;
            $subscriptionDataToCreate = [
                'user_id' => $user->id,
                'subscription_plan_id' => $plan->id,
                'payment_gateway' => 'paystackgateway',
                'gateway_transaction_id' => 'TEMP_PAYSTACK_INIT-' . Str::random(12) . '-' . time(), // Temporary initial reference
                'status' => 'pending_payment',
                'price_at_purchase' => $plan->price,
                'currency_at_purchase' => $plan->currency,
            ];
            Log::debug("Paystack Initialize: Attempting to create Subscription with data:", $subscriptionDataToCreate);

            try {
                $pendingSubscription = Subscription::create($subscriptionDataToCreate);
            } catch (\Illuminate\Database\QueryException $qe) {
                Log::critical("Paystack Initialize: DATABASE QUERY EXCEPTION during Subscription::create()", [
                    'temp_reference_attempted' => $subscriptionDataToCreate['gateway_transaction_id'],
                    'user_id' => $user->id, 
                    'plan_id' => $plan->id,
                    'error' => $qe->getMessage(),
                    'sql' => $qe->getSql(),
                    'bindings' => $qe->getBindings(),
                    'trace' => method_exists(Str::class, 'limit') ? Str::limit($qe->getTraceAsString(), 1000) : substr($qe->getTraceAsString(), 0, 1000)
                ]);
                return redirect()->route('subscription.plans')->with('error', 'Critical error: Failed to initialize your subscription record due to a database issue. Please contact support. (Ref: INIT_DB_EX)');
            } catch (\Exception $e) {
                Log::critical("Paystack Initialize: GENERAL EXCEPTION during Subscription::create()", [
                    'temp_reference_attempted' => $subscriptionDataToCreate['gateway_transaction_id'],
                    'user_id' => $user->id, 
                    'plan_id' => $plan->id,
                    'error' => $e->getMessage(),
                    'trace' => method_exists(Str::class, 'limit') ? Str::limit($e->getTraceAsString(), 1000) : substr($e->getTraceAsString(), 0, 1000)
                ]);
                return redirect()->route('subscription.plans')->with('error', 'Critical error: Failed to initialize your subscription record. Please contact support. (Ref: INIT_GEN_EX)');
            }

            if (!$pendingSubscription || !$pendingSubscription->id) {
                Log::error("Paystack Initialize: FAILED to create pending subscription record (Subscription::create() returned null or invalid object).", [
                    'temp_reference_attempted' => $subscriptionDataToCreate['gateway_transaction_id'],
                    'user_id' => $user->id, 
                    'plan_id' => $plan->id,
                    'data_attempted' => $subscriptionDataToCreate
                ]);
                return redirect()->route('subscription.plans')->with('error', 'Failed to initialize your subscription record. Please try again or contact support. (Ref: POST_CREATE_NULL)');
            } else {
                $createdSubscriptionId = $pendingSubscription->id; // Store ID for re-fetch
                Log::info("Paystack Initialize: Successfully CREATED pending subscription record in DB.", [
                    'id' => $pendingSubscription->id, 
                    'initial_temp_reference_stored' => $pendingSubscription->gateway_transaction_id,
                    'user_id' => $pendingSubscription->user_id, 
                    'plan_id' => $pendingSubscription->subscription_plan_id, 
                    'status_stored' => $pendingSubscription->status
                ]);
            }

            // Call the service method for initializing a SUBSCRIPTION transaction,
            // passing the ID of our local pending subscription.
            $paymentDetails = $this->paystackService->initializeSubscriptionTransaction($user, $plan, $pendingSubscription->id);

            if ($paymentDetails && !empty($paymentDetails['authorization_url']) && !empty($paymentDetails['reference'])) {
                // Update the local subscription with the actual reference from Paystack
                $pendingSubscription->gateway_transaction_id = $paymentDetails['reference'];
                if ($pendingSubscription->save()) {
                    Log::info("Paystack Initialize: Successfully updated local subscription with Paystack's reference.", [
                        'local_subscription_id' => $pendingSubscription->id, 
                        'paystack_reference' => $paymentDetails['reference']
                    ]);
                } else {
                    Log::error("Paystack Initialize: FAILED to SAVE Paystack's reference to local subscription.", [
                        'local_subscription_id' => $pendingSubscription->id, 
                        'attempted_paystack_reference' => $paymentDetails['reference']
                    ]);
                    $pendingSubscription->update(['status' => 'failed']); // Mark as failed
                    return redirect()->route('subscription.plans')->with('error', 'Failed to record payment reference. Please contact support.');
                }
                return redirect()->away($paymentDetails['authorization_url']);
            }
            Log::error('Paystack initialize subscription transaction failed or missing data from service.', ['response_from_service' => $paymentDetails, 'user_id' => $user->id, 'plan_id' => $plan->id]);
            $pendingSubscription->update(['status' => 'failed']); // Mark as failed
            return redirect()->route('subscription.plans')->with('error', 'Could not initiate Paystack payment. Please try again.');

        } catch (\Exception $e) {
            Log::error("Paystack Initialize Payment Error for user {$user->id}, plan {$plan->id}: " . $e->getMessage());
            return redirect()->route('subscription.plans')->with('error', 'An error occurred while initiating payment: ' . $e->getMessage());
        }
    }

    public function handleCallback(Request $request)
    {
        $reference = $request->query('reference');

        if (!$reference) {
            return redirect()->route('subscription.plans')->with('error', 'Invalid Paystack callback. Reference missing.');
        }

        try {
            $verificationData = $this->paystackService->verifyTransaction($reference);

            if ($verificationData && isset($verificationData['status']) && $verificationData['status'] === true && isset($verificationData['data']['status']) && $verificationData['data']['status'] === 'success') {
                $paystackData = $verificationData['data'];
                $metadata = $paystackData['metadata'] ?? [];
                $localSubscriptionIdFromMeta = $metadata['subscription_id'] ?? null;
                $referenceFromQuery = $request->query('reference'); // This is the $reference used for verification

                $subscription = null;

                if ($localSubscriptionIdFromMeta) {
                    $subscription = Subscription::find($localSubscriptionIdFromMeta);
                }

                // Diagnostic: Check if ANY subscription exists with this reference, regardless of status
                if (!$subscription && $referenceFromQuery) {
                    $anySubscriptionWithReference = Subscription::where('gateway_transaction_id', $referenceFromQuery)
                                                                ->where('payment_gateway', 'paystackgateway')
                                                                ->first();
                    if ($anySubscriptionWithReference) {
                        Log::info("Paystack Callback: A subscription (ID: {$anySubscriptionWithReference->id}) was found with reference '{$referenceFromQuery}', but its status is '{$anySubscriptionWithReference->status}'. Expected 'pending_payment'.");
                    } else {
                        Log::info("Paystack Callback: NO subscription record found at all with reference '{$referenceFromQuery}'.");
                    }
                }

                // If not found by metadata ID, or if metadata ID was not present, try finding by the reference
                // This assumes the reference was stored in gateway_transaction_id during initialization
                if (!$subscription && $referenceFromQuery) {
                    Log::info("Paystack Callback: Subscription not found by metadata ID '{$localSubscriptionIdFromMeta}'. Attempting lookup by reference '{$referenceFromQuery}'.");
                    $subscription = Subscription::where('gateway_transaction_id', $referenceFromQuery)
                                                ->where('payment_gateway', 'paystackgateway') // Ensure it's a Paystack sub
                                                ->where('status', 'pending_payment') // Specifically look for pending
                                                ->first();
                }

                if ($subscription && $subscription->status === 'pending_payment') { // Ensure it's the correct pending subscription
                    // Deactivate any other existing subscriptions for this user
                    $subscription->user->subscriptions()
                        ->where('id', '!=', $subscription->id)
                        ->whereIn('status', ['active', 'trialing'])
                        ->update(['status' => 'cancelled', 'ends_at' => now(), 'cancelled_at' => now()]);

                    // Determine the new status based on the plan's trial period
                    $plan = $subscription->plan;
                    if ($plan && $plan->trial_period_days > 0) {
                        $subscription->status = 'trialing';
                        Log::info("Paystack Callback: Setting subscription ID {$subscription->id} to 'trialing'.");
                    } else {
                        $subscription->status = 'active';
                        Log::info("Paystack Callback: Setting subscription ID {$subscription->id} to 'active'.");
                    }
                    // The updateFromPaystackData method will now save this status along with date calculations.
                    $subscription->updateFromPaystackData($paystackData); // Implement this method in Subscription model
                    
                    // Award credits (similar to SubscriptionController)
                    // $plan is already fetched above
                    if (function_exists('setting') && setting('credits_system_enabled', '0') == '1' && $plan->credits_awarded_on_purchase > 0) {
                        $this->creditService->awardCredits($subscription->user, $plan->credits_awarded_on_purchase, 'award_subscription_purchase', "Credits for {$plan->name} subscription", $subscription);
                    }
                     // Assign target role if defined
                    if (!empty($plan->target_role) && class_exists(\Spatie\Permission\Models\Role::class) && \Spatie\Permission\Models\Role::where('name', $plan->target_role)->where('guard_name', 'web')->exists()) {
                        $subscription->user->syncRoles([$plan->target_role]);
                    }

                    return redirect()->route('dashboard')->with('success', 'Subscription successfully activated via Paystack!');
                }
                Log::warning('Paystack Callback: Local subscription not found or not in a pending state.', [
                    'reference' => $referenceFromQuery,
                    'local_id_from_meta' => $localSubscriptionIdFromMeta,
                    'subscription_found_id' => $subscription ? $subscription->id : null,
                    'status_if_found' => $subscription ? $subscription->status : 'N/A'
                ]);
                return redirect()->route('subscription.plans')->with('error', 'Subscription record mismatch or already processed. Please contact support.');
            }
            Log::error('Paystack transaction verification failed or payment not successful.', ['reference' => $reference, 'response' => $verificationData]);
            return redirect()->route('subscription.plans')->with('error', $verificationData['message'] ?? 'Paystack payment verification failed.');

        } catch (\Exception $e) {
            Log::error("Paystack Callback Error for reference {$reference}: " . $e->getMessage());
            return redirect()->route('subscription.plans')->with('error', 'An error occurred during payment verification: ' . $e->getMessage());
        }
    }

    // --- Wallet Deposit Methods ---

    public function initializeWalletDeposit(Request $request)
    {
        if (setting('paystack_enabled', '0') != '1' || setting('allow_wallet_deposits', '0') != '1') {
            return redirect()->route('user.wallet.deposit.form')->with('error', 'Paystack deposits are currently disabled.');
        }

        $request->validate(['amount' => 'required|numeric|min:1']);
        $amount = (float) $request->input('amount');
        /** @var User $user */
        $user = Auth::user();

        try {
            $reference = 'DEPOSIT-' . Str::random(8) . '-' . time();
            $amountInKobo = $amount * 100; // Paystack expects amount in kobo
            $currency = strtoupper(setting('currency_code', 'USD')); // Use global USD default

            // Optionally create a pending WalletTransaction record here if needed for tracking before redirect

            $callbackUrl = route('wallet.paystack.depositCallback');
            $metadata = [
                'user_id' => $user->id,
                'deposit_amount' => $amount,
                'currency' => $currency,
                'type' => 'wallet_deposit',
                'custom_fields' => [
                    ['display_name' => "Wallet Deposit", 'variable_name' => "transaction_type", 'value' => "Wallet Top-up"]
                ]
            ];

            $paymentData = $this->paystackService->initializeTransaction($amountInKobo, $user->email, $reference, $callbackUrl, $currency, $metadata);

            if ($paymentData && isset($paymentData['status']) && $paymentData['status'] === true && isset($paymentData['data']['authorization_url'])) {
                return redirect()->away($paymentData['data']['authorization_url']);
            }

            Log::error('Paystack initialize wallet deposit failed.', ['response' => $paymentData, 'user_id' => $user->id, 'amount' => $amount]);
            return redirect()->route('user.wallet.deposit.form')->with('error', $paymentData['message'] ?? 'Could not initiate Paystack deposit. Please try again.');

        } catch (\Exception $e) {
            Log::error("Paystack Initialize Wallet Deposit Error for user {$user->id}, amount {$amount}: " . $e->getMessage());
            return redirect()->route('user.wallet.deposit.form')->with('error', 'An error occurred while initiating deposit: ' . $e->getMessage());
        }
    }

    public function handleWalletDepositCallback(Request $request)
    {
        $reference = $request->query('reference');

        if (!$reference) {
            return redirect()->route('user.wallet.deposit.form')->with('error', 'Invalid Paystack callback for deposit. Reference missing.');
        }

        try {
            $verificationData = $this->paystackService->verifyTransaction($reference);

            if ($verificationData && isset($verificationData['status']) && $verificationData['status'] === true && isset($verificationData['data']['status']) && $verificationData['data']['status'] === 'success') {
                $paystackData = $verificationData['data'];
                $amountDeposited = $paystackData['amount'] / 100; // Amount is in Kobo
                $currency = $paystackData['currency'] ?? 'NGN';
                $userIdFromMeta = $paystackData['metadata']['user_id'] ?? null;

                if (!$userIdFromMeta) {
                    Log::error('Paystack Wallet Deposit Callback: User ID missing from metadata.', ['reference' => $reference]);
                    return redirect()->route('user.wallet.deposit.form')->with('error', 'User identification failed for deposit.');
                }

                $user = User::find($userIdFromMeta);

                if (!$user) {
                    Log::error('Paystack Wallet Deposit Callback: User not found for ID from metadata.', ['reference' => $reference, 'user_id_from_meta' => $userIdFromMeta]);
                    return redirect()->route('user.wallet.deposit.form')->with('error', 'User account not found for deposit.');
                }

                // User successfully identified from metadata
                $this->walletService->deposit($user, $amountDeposited, $currency, 'paystackgateway', $reference, "Wallet deposit via Paystack");
                return redirect()->route('user.wallet.history')->with('success', 'Successfully deposited ' . $currency . ' ' . number_format($amountDeposited, 2) . ' to your wallet.');
            }
            Log::error('Paystack wallet deposit verification failed or payment not successful.', ['reference' => $reference, 'response' => $verificationData]);
            return redirect()->route('user.wallet.deposit.form')->with('error', $verificationData['message'] ?? 'Paystack payment verification failed for deposit.');
        } catch (\Exception $e) {
            Log::error("Paystack Wallet Deposit Callback Error for reference {$reference}: " . $e->getMessage());
            return redirect()->route('user.wallet.deposit.form')->with('error', 'An error occurred during deposit verification: ' . $e->getMessage());
        }
    }
}