<?php

namespace Modules\StripeGateway\Services;

use App\Models\SubscriptionPlan;
use App\Models\User;
use Illuminate\Support\Facades\Log;
use Stripe\Stripe;
use Stripe\Customer;
use Stripe\Product;
use Stripe\Price;
use Stripe\Checkout\Session;
use Stripe\Exception\ApiErrorException;

class StripeService
{
    const METADATA_USER_ID = 'user_id';
    const METADATA_LOCAL_PLAN_ID = 'local_plan_id';

    protected string $secretKey;
    protected string $webhookSecret;

    public function __construct()
    {
        if (!function_exists('setting')) {
            throw new \Exception("Settings helper function not found. Ensure it's globally available.");
        }
        $this->secretKey = setting('stripe_secret_key') ?? ''; // Default to empty string if null
        $this->webhookSecret = setting('stripe_webhook_secret') ?? ''; // Default to empty string if null

        if ($this->secretKey) {
            Stripe::setApiKey($this->secretKey);
            Stripe::setAppInfo(
                config('app.name', 'Laravel App'), // Your application's name
                // app()->version(), // Optional: Your application's version
                // url('/') // Optional: Your application's URL
            );
        } else {
            Log::error('StripeService: Secret key is not configured.');
        }
    }

    protected function ensureStripeClientIsAvailable()
    {
        if (!$this->secretKey) {
            throw new \Exception('Stripe Service is not configured. Missing secret key.');
        }
    }

    public function getOrCreateStripeCustomer(User $user): Customer
    {
        $this->ensureStripeClientIsAvailable();

        if ($user->stripe_customer_id) {
            try {
                return Customer::retrieve($user->stripe_customer_id);
            } catch (ApiErrorException $e) {
                Log::warning("StripeService: Could not retrieve existing customer {$user->stripe_customer_id}. Will create a new one. Error: " . $e->getMessage());
            }
        }

        $customer = Customer::create([
            'email' => $user->email,
            'name' => $user->name,
            'metadata' => [
            self::METADATA_USER_ID => $user->id,
            ],
        ]);

        $user->stripe_customer_id = $customer->id;
        $user->save();

        return $customer;
    }

    public function getOrCreateStripeProduct(SubscriptionPlan $localPlan): Product
    {
        $this->ensureStripeClientIsAvailable();

        if ($localPlan->stripe_product_id) {
            try {
                return Product::retrieve($localPlan->stripe_product_id);
            } catch (ApiErrorException $e) {
                Log::warning("StripeService: Could not retrieve existing product {$localPlan->stripe_product_id}. Will create a new one. Error: " . $e->getMessage());
            }
        }

        $product = Product::create([
            'name' => $localPlan->name,
            'description' => $localPlan->description,
            'metadata' => [
            self::METADATA_LOCAL_PLAN_ID => $localPlan->id,
            ],
        ]);

        $localPlan->stripe_product_id = $product->id;
        $localPlan->save();

        return $product;
    }

    public function getOrCreateStripePrice(SubscriptionPlan $localPlan, Product $stripeProduct): Price
    {
        $this->ensureStripeClientIsAvailable();

        if ($localPlan->stripe_price_id) {
            try {
                $price = Price::retrieve($localPlan->stripe_price_id);
                // Basic check if price matches, more complex scenarios might need recreation
                if ((int)round($price->unit_amount) === (int)round($localPlan->price * 100) &&
                    $price->recurring->interval === $localPlan->interval &&
                    $price->currency === strtolower($localPlan->currency ?? setting('currency_code', 'usd'))) {
                    return $price;
                }
                Log::info("StripeService: Existing price {$localPlan->stripe_price_id} details mismatch. Creating a new one.");
            } catch (ApiErrorException $e) {
                Log::warning("StripeService: Could not retrieve existing price {$localPlan->stripe_price_id}. Will create a new one. Error: " . $e->getMessage());
            }
        }

        $price = Price::create([
            'product' => $stripeProduct->id,
            'unit_amount' => (int)round($localPlan->price * 100), // Amount in cents
            'currency' => strtolower($localPlan->currency ?? setting('currency_code', 'usd')),
            'recurring' => [
                'interval' => $localPlan->interval, // 'day', 'week', 'month', or 'year'
                'interval_count' => $localPlan->interval_count,
            ],
            'metadata' => [
                self::METADATA_LOCAL_PLAN_ID => $localPlan->id,
            ],
        ]);

        $localPlan->stripe_price_id = $price->id;
        $localPlan->save();

        return $price;
    }

    // Add the missing method here
    public function createSubscriptionCheckoutSession(User $user, SubscriptionPlan $localPlan): ?string
    {
        $this->ensureStripeClientIsAvailable();

        try {
            $stripeCustomer = $this->getOrCreateStripeCustomer($user);
            $stripeProduct = $this->getOrCreateStripeProduct($localPlan);
            $stripePrice = $this->getOrCreateStripePrice($localPlan, $stripeProduct);

            $checkoutSession = Session::create([
                'customer' => $stripeCustomer->id,
                'payment_method_types' => ['card'],
                'line_items' => [[
                    'price' => $stripePrice->id,
                    'quantity' => 1,
                ]],
                'mode' => 'subscription',
                'success_url' => route('subscription.stripe.success', [], true) . '?session_id={CHECKOUT_SESSION_ID}', // Define this route
                'cancel_url' => route('subscription.stripe.cancel', [], true),    // Define this route
                'subscription_data' => [
                    'trial_period_days' => $localPlan->trial_period_days > 0 ? $localPlan->trial_period_days : null,
                    'metadata' => [
                        self::METADATA_USER_ID => (string) $user->id,
                        self::METADATA_LOCAL_PLAN_ID => (string) $localPlan->id,
                    ]
                ],
                'metadata' => [ // Metadata for the checkout session itself
                    self::METADATA_USER_ID => (string) $user->id,
                    self::METADATA_LOCAL_PLAN_ID => (string) $localPlan->id,
                ]
            ]);

            return $checkoutSession->url;

        } catch (ApiErrorException $e) {
            Log::error("StripeService: Error creating checkout session for user {$user->id}, plan {$localPlan->id}: " . $e->getMessage(), ['exception' => $e]);
            throw new \Exception('Failed to create Stripe checkout session: ' . $e->getUserMessage()); // Throw user-friendly message
        } catch (\Exception $e) {
            Log::error("StripeService: General error creating checkout session for user {$user->id}, plan {$localPlan->id}: " . $e->getMessage(), ['exception' => $e]);
            throw $e; // Re-throw general exceptions
        }
    }

    public function retrieveCheckoutSession(string $sessionId): ?\Stripe\Checkout\Session // Use FQCN
    {
        $this->ensureStripeClientIsAvailable();
        try {
            // It's good practice to expand related objects if you need them,
            // e.g., 'payment_intent' for one-time payments if you need its details.
            return Session::retrieve($sessionId, ['expand' => ['payment_intent']]);
        } catch (ApiErrorException $e) {
            Log::error("StripeService: Error retrieving checkout session {$sessionId}: " . $e->getMessage(), [
                'exception_class' => get_class($e),
                'stripe_code' => $e->getStripeCode(),
                'exception' => $e
            ]);
            return null;
        } catch (\Exception $e) {
            Log::error("StripeService: General error retrieving checkout session {$sessionId}: " . $e->getMessage(), ['exception' => $e]);
            return null;
        }
    }

    public function createWalletDepositCheckoutSession(User $user, float $amount, string $currency): \Stripe\Checkout\Session // Use FQCN
    {
        $this->ensureStripeClientIsAvailable();

        try {
            $stripeCustomer = $this->getOrCreateStripeCustomer($user);

            $checkoutSession = Session::create([
                'customer' => $stripeCustomer->id,
                'payment_method_types' => ['card'],
                'line_items' => [[
                    'price_data' => [
                        'currency' => strtolower($currency),
                        'product_data' => [
                            'name' => 'Wallet Deposit',
                            'description' => 'Add funds to your wallet.',
                        ],
                        'unit_amount' => (int)round($amount * 100), // Amount in cents
                    ],
                    'quantity' => 1,
                ]],
                'mode' => 'payment', // For one-time payments
                'success_url' => route('wallet.stripe.depositSuccess', [], true) . '?session_id={CHECKOUT_SESSION_ID}',
                'cancel_url' => route('wallet.stripe.depositCancel', [], true),
                'metadata' => [ // Metadata for the checkout session itself
                    self::METADATA_USER_ID => (string) $user->id,
                    'type' => 'wallet_deposit', // Custom metadata to identify this session type
                    'amount' => $amount,
                    'currency' => $currency,
                ]
            ]);

            return $checkoutSession;

        } catch (ApiErrorException $e) {
            Log::error("StripeService: Error creating wallet deposit checkout session for user {$user->id}, amount {$amount}: " . $e->getMessage(), ['exception' => $e]);
            $userMessage = $e->getMessage(); 
            if (method_exists($e, 'getUserMessage') && $e->getUserMessage()) { 
                $userMessage = $e->getUserMessage();
            } elseif ($e->getStripeCode()) { 
                $userMessage .= ' (Stripe Code: ' . $e->getStripeCode() . ')';
            }
            throw new \Exception('Failed to create Stripe wallet deposit session: ' . $userMessage);
        } catch (\Exception $e) {
            Log::error("StripeService: General error creating wallet deposit checkout session for user {$user->id}, amount {$amount}: " . $e->getMessage(), ['exception' => $e]);
            throw $e; // Re-throw general exceptions
        }
    }


    public function constructWebhookEvent(string $payload, string $signatureHeader): \Stripe\Event
    {
        $this->ensureStripeClientIsAvailable();
        if (empty($this->webhookSecret)) { // Check if empty, as it's defaulted to ''
            Log::error('StripeService: Webhook secret is not configured. Cannot process webhook.');
            throw new \Exception('Stripe webhook secret not configured.');
        }

        try {
            $event = \Stripe\Webhook::constructEvent(
                $payload, $signatureHeader, $this->webhookSecret
            );
        } catch(\UnexpectedValueException $e) {
            Log::error('Stripe Webhook Error: Invalid payload.', ['exception' => $e->getMessage()]);
            throw $e;
        } catch(\Stripe\Exception\SignatureVerificationException $e) {
            Log::error('Stripe Webhook Error: Invalid signature.', ['exception' => $e->getMessage()]);
            throw $e;
        }

        return $event;
    }

    public function handleWebhookEvent(\Stripe\Event $event)
    {
        // This method can be removed if all logic is in StripeWebhookController,
        // or it can contain the switch statement if you prefer to keep event handling logic in the service.
        // For now, let's assume the controller handles the switch.
        // Handle the event
        switch ($event->type) {
            case 'checkout.session.completed':
                // Logic to fulfill the subscription, e.g., update your local subscription record.
                // The $event->data->object will contain the checkout session.
                // You might have stored your local subscription ID in metadata.
                Log::info('Stripe Webhook: checkout.session.completed', ['event_id' => $event->id, 'session_id' => $event->data->object->id]);
                // $this->fulfillSubscriptionFromCheckoutSession($event->data->object);
                break;
            case 'invoice.payment_succeeded':
                // Logic for successful recurring payments. Update subscription end date, award credits, etc.
                Log::info('Stripe Webhook: invoice.payment_succeeded', ['event_id' => $event->id, 'invoice_id' => $event->data->object->id]);
                // $this->handleSuccessfulPayment($event->data->object);
                break;
            case 'invoice.payment_failed':
                // Logic for failed recurring payments. Notify user, mark subscription as past_due, etc.
                Log::info('Stripe Webhook: invoice.payment_failed', ['event_id' => $event->id, 'invoice_id' => $event->data->object->id]);
                // $this->handleFailedPayment($event->data->object);
                break;
            case 'customer.subscription.deleted':
                // Logic for when a subscription is cancelled (e.g., by user in Stripe portal or by admin).
                Log::info('Stripe Webhook: customer.subscription.deleted', ['event_id' => $event->id, 'subscription_id' => $event->data->object->id]);
                // $this->handleSubscriptionCancellation($event->data->object);
                break;
            // ... handle other event types
            default:
                Log::info('Stripe Webhook: Received unhandled event type ' . $event->type, ['event_id' => $event->id]);
        }

        return true; // Indicate successful handling to Stripe
    }
}
