<?php

namespace App\Services;

use App\Models\Payment;
use App\Models\Subscription;
use Stripe\Customer;
use Stripe\Invoice;
use Stripe\Price;
use Stripe\Product;
use Stripe\StripeClient;
use Stripe\Subscription as StripeSubscription;

class StripeService
{
    private StripeClient $stripe;
    private BillingService $billingService;

    public function __construct()
    {
        $this->stripe = new StripeClient(config('services.stripe.secret'));
        $this->billingService = app(BillingService::class);
    }

    /**
     * Create a Stripe customer for subscription
     */
    public function createCustomer(Subscription $subscription): string
    {
        $user = $subscription->user;
        $company = $user->company;

        $existing = $this->stripe->customers->search([
            'query' => 'email:"' . addslashes($user->email) . '"',
            'limit' => 1,
        ]);

        if (!empty($existing->data) && isset($existing->data[0])) {
            return $existing->data[0]->id;
        }

        $customer = $this->stripe->customers->create([
            'name' => $company->name,
            'email' => $user->email,
            'phone' => $company->phone,
            'metadata' => [
                'company_id' => $company->id,
                'user_id' => $user->id,
                'subscription_id' => $subscription->id,
            ],
        ]);

        return $customer->id;
    }

    /**
     * Create or get a Stripe Checkout session for saving a card for future off-session billing.
     */
    public function createSetupSession(Subscription $subscription, $payment)
    {
        if (!$subscription->stripe_customer_id) {
            $subscription->update(['stripe_customer_id' => $this->createCustomer($subscription)]);
        }

        $session = $this->stripe->checkout->sessions->create([
            'mode' => 'setup',
            'customer' => $subscription->stripe_customer_id,
            'payment_method_types' => ['card'],
            'setup_intent_data' => [
                'metadata' => [
                    'subscription_id' => $subscription->id,
                    'payment_id' => $payment->id,
                ],
            ],
            'success_url' => route('payments.success', ['payment' => $payment->id]) . '?session_id={CHECKOUT_SESSION_ID}',
            'cancel_url' => route('payments.cancel', ['payment' => $payment->id]),
            'metadata' => [
                'subscription_id' => $subscription->id,
                'payment_id' => $payment->id,
            ],
        ]);

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

        return $session;
    }

    /**
     * Create or get Stripe product for a package
     */
    public function getOrCreateProduct($package): string
    {
        $productName = "True Trucker - {$package->name} Package";

        // Search for existing product
        $products = $this->stripe->products->all([
            'limit' => 100,
            'metadata' => ['package_id' => $package->id],
        ]);

        if (count($products->data) > 0) {
            return $products->data[0]->id;
        }

        // Create new product
        $product = $this->stripe->products->create([
            'name' => $productName,
            'description' => $package->description,
            'metadata' => ['package_id' => $package->id],
        ]);

        return $product->id;
    }

    /**
     * Create Stripe subscription with recurring billing
     */
    public function createSubscription(Subscription $subscription): string
    {
        $package = $subscription->package;
        $billingCycle = $subscription->billing_cycle ?? $package->billing_cycle ?? 'monthly';

        // Ensure customer exists
        if (!$subscription->stripe_customer_id) {
            $customerId = $this->createCustomer($subscription);
            $subscription->update(['stripe_customer_id' => $customerId]);
        } else {
            $customerId = $subscription->stripe_customer_id;
        }

        // Get or create product
        $productId = $this->getOrCreateProduct($package);

        // Calculate initial amount based on driver count
        $driverCount = $subscription->user->company->drivers()->count();
        $amount = $this->billingService->calculateBillingAmount($package, $driverCount);
        $amountInCents = (int) ($amount * 100);

        try {
            // Create Stripe subscription with metered billing
            $stripeSubscription = $this->stripe->subscriptions->create([
                'customer' => $customerId,
                'items' => [
                    [
                        'price_data' => [
                            'currency' => 'usd',
                            'product' => $productId,
                            'unit_amount' => $amountInCents,
                            'recurring' => [
                                'interval' => $billingCycle === 'weekly' ? 'week' : 'month',
                                'interval_count' => 1,
                            ],
                        ],
                        'quantity' => 1,
                    ],
                ],
                'metadata' => [
                    'subscription_id' => $subscription->id,
                    'company_id' => $subscription->user->company_id,
                    'package_id' => $package->id,
                    'driver_count' => $driverCount,
                ],
                'billing_cycle_anchor' => now()->addMonth()->startOfMonth()->timestamp,
            ]);

            return $stripeSubscription->id;
        } catch (\Exception $e) {
            throw new \Exception("Failed to create Stripe subscription: {$e->getMessage()}");
        }
    }

    /**
     * Process a payment charge for subscription
     */
    public function chargeSubscription(Subscription $subscription): Payment
    {
        if (!$subscription->stripe_customer_id) {
            throw new \Exception('No Stripe customer associated with subscription');
        }

        $paymentMethod = $subscription->user->company->getActivePaymentMethod();
        if (!$paymentMethod) {
            throw new \Exception('No active Stripe payment method found');
        }

        $driverCount = $subscription->user->company->drivers()->count();
        $amount = $this->billingService->calculateBillingAmount($subscription->package, $driverCount);

        try {
            // Retrieve or create invoice for this billing period
            $invoice = $this->createInvoiceForBillingPeriod($subscription, $amount, $driverCount);

            // Record payment in database
            $payment = $subscription->payments()->create([
                'payment_method_id' => $paymentMethod->id,
                'amount' => $amount,
                'invoice_amount' => $amount,
                'driver_count' => $driverCount,
                'status' => 'pending',
                'transaction_id' => $invoice->id,
                'billing_period_start' => now()->startOfMonth(),
                'billing_period_end' => now()->endOfMonth(),
                'metadata' => [
                    'invoice_id' => $invoice->id,
                    'driver_count' => $driverCount,
                ],
            ]);

            return $payment;
        } catch (\Exception $e) {
            throw new \Exception("Failed to charge subscription: {$e->getMessage()}");
        }
    }

    /**
     * Create or find Stripe invoice for billing period
     */
    private function createInvoiceForBillingPeriod(Subscription $subscription, $amount, $driverCount): Invoice
    {
        $customerId = $subscription->stripe_customer_id;
        $amountInCents = (int) ($amount * 100);

        // Create invoice item
        $this->stripe->invoiceItems->create([
            'customer' => $customerId,
            'amount' => $amountInCents,
            'currency' => 'usd',
            'description' => "Driver Package - {$driverCount} driver(s) - {$subscription->package->name}",
            'metadata' => [
                'subscription_id' => $subscription->id,
                'driver_count' => $driverCount,
            ],
        ]);

        // Create and finalize invoice
        $invoice = $this->stripe->invoices->create([
            'customer' => $customerId,
            'auto_advance' => true,
            'collection_method' => 'charge_automatically',
            'metadata' => [
                'subscription_id' => $subscription->id,
                'billing_period' => date('Y-m'),
            ],
        ]);

        // Finalize invoice to prepare for payment
        return $this->stripe->invoices->finalizeInvoice($invoice->id);
    }

    /**
     * Update subscription amount when driver count changes
     */
    public function updateSubscriptionForDriverChange(Subscription $subscription): void
    {
        if (!$subscription->stripe_subscription_id) {
            return;
        }

        $driverCount = $subscription->user->company->drivers()->count();
        $amount = $this->billingService->calculateBillingAmount($subscription->package, $driverCount);

        // Update subscription metadata
        $this->stripe->subscriptions->update($subscription->stripe_subscription_id, [
            'metadata' => [
                'driver_count' => $driverCount,
                'last_updated' => now()->toDateTimeString(),
            ],
        ]);

        // Record metadata change for next billing
        $subscription->update([
            'last_driver_count' => $driverCount,
        ]);
    }

    /**
     * Handle successful payment webhook
     */
    public function handlePaymentSuccess($stripePaymentId): void
    {
        $invoice = $this->stripe->invoices->retrieve($stripePaymentId);

        $payment = Payment::where('transaction_id', $stripePaymentId)->firstOrFail();
        $payment->update([
            'status' => 'completed',
            'processed_at' => now(),
        ]);

        $subscription = $payment->subscription;
        $subscription->update([
            'payment_status' => 'completed',
            'amount_paid' => $subscription->amount_paid + $payment->amount,
            'next_billing_date' => now()->addMonth(),
        ]);
    }

    /**
     * Handle failed payment webhook
     */
    public function handlePaymentFailure($stripePaymentId, $failureReason): void
    {
        $payment = Payment::where('transaction_id', $stripePaymentId)->firstOrFail();
        $payment->update([
            'status' => 'failed',
            'failure_reason' => $failureReason,
            'processed_at' => now(),
        ]);
    }

    /**
     * Retry failed payment
     */
    public function retryPayment(Payment $payment): void
    {
        if ($payment->status !== 'failed') {
            throw new \Exception('Only failed payments can be retried');
        }

        $invoice = $this->stripe->invoices->retrieve($payment->transaction_id);

        // Attempt to pay the invoice
        $this->stripe->invoices->pay($payment->transaction_id);

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

    /**
     * Cancel Stripe subscription
     */
    public function cancelSubscription(Subscription $subscription): void
    {
        if (!$subscription->stripe_subscription_id) {
            return;
        }

        try {
            $this->stripe->subscriptions->cancel($subscription->stripe_subscription_id);
        } catch (\Exception $e) {
            // Log but don't fail
            \Log::warning("Failed to cancel Stripe subscription: {$e->getMessage()}");
        }
    }
}
