<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Jobs\ProcessPaymentWebhookJob;
use App\Models\Device;
use App\Models\PaymentWebhookEvent;
use App\Models\UserSubscription;
use App\Services\FirebasePushService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

class BillingController extends Controller
{
    private const PERIOD_DAYS = [
        'monthly'   => 31,
        'quarterly' => 92,
        'biannual'  => 183,
        'yearly'    => 366,
        'one-time'  => null, // null = lifetime, no expiry
    ];

    public function stripeCheckout(Request $request): JsonResponse
    {
        $request->validate([
            'plan'        => 'required|in:starter,growth,pro',
            'period'      => 'nullable|in:monthly,quarterly,biannual,yearly,one-time',
            'success_url' => 'required|string|min:5',
            'cancel_url'  => 'required|string|min:5',
        ]);

        $secret = config('services.stripe.secret');
        if (!$secret) {
            return response()->json(['error' => 'Stripe is not configured'], 503);
        }

        $planName = $request->string('plan')->value();
        $period   = $request->input('period', 'monthly');
        $plan     = config('quickropos.plans.' . $planName);
        $price    = (float) ($plan['pricing'][$period] ?? $plan['price'] ?? 0);

        // Append {CHECKOUT_SESSION_ID} so we can confirm without webhooks
        $successUrl = rtrim($request->string('success_url')->value(), '&?')
            . (str_contains($request->string('success_url')->value(), '?') ? '&' : '?')
            . 'session_id={CHECKOUT_SESSION_ID}';

        $response = Http::asForm()
            ->withToken($secret)
            ->post('https://api.stripe.com/v1/checkout/sessions', [
                'mode' => 'payment',
                'success_url' => $successUrl,
                'cancel_url'  => $request->string('cancel_url')->value(),
                'line_items[0][price_data][currency]'                => strtolower($plan['currency'] ?? 'usd'),
                'line_items[0][price_data][product_data][name]'      => 'Quickro ' . ucfirst($planName) . ' · ' . ucfirst($period),
                'line_items[0][price_data][unit_amount]'             => (int) round($price * 100),
                'line_items[0][quantity]'                            => 1,
                'metadata[user_id]'                                  => $request->user()->id,
                'metadata[plan]'                                     => $planName,
                'metadata[period]'                                   => $period,
                'metadata[email]'                                    => $request->user()->email,
            ]);

        return response()->json($response->json(), $response->status());
    }

    /**
     * Confirm a Stripe checkout session directly (no webhook needed).
     * Used when the app returns from checkout with a session_id.
     */
    public function confirmStripeSession(Request $request): JsonResponse
    {
        $request->validate(['session_id' => 'required|string']);

        $secret = config('services.stripe.secret');
        if (!$secret) {
            return response()->json(['error' => 'Stripe is not configured'], 503);
        }

        $sessionId = $request->string('session_id')->value();
        $response  = Http::withToken($secret)
            ->get('https://api.stripe.com/v1/checkout/sessions/' . $sessionId);

        if ($response->failed()) {
            return response()->json(['error' => 'Stripe session not found'], 404);
        }

        $session = $response->json();
        if (($session['payment_status'] ?? '') !== 'paid') {
            return response()->json(['error' => 'Payment not completed yet'], 402);
        }

        $user     = $request->user();
        $metadata = $session['metadata'] ?? [];

        // Security: ensure session belongs to authenticated user
        if ((string)($metadata['user_id'] ?? '') !== (string)$user->id) {
            return response()->json(['error' => 'Session does not belong to this account'], 403);
        }

        $plan   = $metadata['plan'] ?? null;
        $period = $metadata['period'] ?? 'monthly';

        if (!$plan || !config('quickropos.plans.' . $plan)) {
            return response()->json(['error' => 'Invalid plan in session metadata'], 422);
        }

        // Idempotent: already activated
        if (UserSubscription::where('transaction_id', $sessionId)->exists()) {
            $user->refresh();
            return response()->json([
                'plan'           => $user->currentPlan(),
                'plan_expires_at' => $user->plan_expires_at,
                'activated'      => false,
                'message'        => 'Already activated',
            ]);
        }

        $days      = self::PERIOD_DAYS[$period] ?? 31;
        $expiresAt = $days !== null ? now()->addDays($days) : null;

        // Extract amount and currency from the Stripe session
        $amountTotal = (int) ($session['amount_total'] ?? 0); // cents
        $currency    = strtoupper($session['currency'] ?? 'USD');
        $amountPaid  = $amountTotal > 0 ? round($amountTotal / 100, 2) : 0.00;

        UserSubscription::create([
            'user_id'       => $user->id,
            'plan'          => $plan,
            'period'        => $period,
            'source'        => 'stripe',
            'product_id'    => 'quickro_' . $plan . '_' . $period,
            'transaction_id' => $sessionId,
            'receipt_data'  => json_encode($session),
            'amount_paid'   => $amountPaid,
            'currency'      => $currency,
            'is_active'     => true,
            'starts_at'     => now(),
            'expires_at'    => $expiresAt,
        ]);

        $user->update([
            'plan'            => $plan,
            'plan_expires_at' => $expiresAt,
            'plan_source'     => 'stripe',
        ]);

        app(FirebasePushService::class)->sendFromTemplate('subscription_activated', $user);

        return response()->json([
            'plan'           => $plan,
            'plan_expires_at' => $expiresAt,
            'activated'      => true,
        ]);
    }


    public function paypalOrder(Request $request): JsonResponse
    {
        $request->validate([
            'plan'        => 'required|in:starter,growth,pro',
            'period'      => 'nullable|in:monthly,quarterly,biannual,yearly,one-time',
            'return_url'  => 'nullable|string|min:5',
            'cancel_url'  => 'nullable|string|min:5',
        ]);

        $clientId = config('services.paypal.client_id');
        $secret = config('services.paypal.secret');
        if (!$clientId || !$secret) {
            return response()->json(['error' => 'PayPal is not configured'], 503);
        }

        $base     = config('services.paypal.base_url', 'https://api-m.sandbox.paypal.com');
        $planName = $request->string('plan')->value();
        $period   = $request->input('period', 'monthly');
        $plan     = config('quickropos.plans.' . $planName);
        $price    = (float) ($plan['pricing'][$period] ?? $plan['price'] ?? 0);

        $returnUrl = $request->input('return_url',
            config('app.url') . '/?checkout=success&payment=paypal&plan=' . $planName);
        $cancelUrl = $request->input('cancel_url',
            config('app.url') . '/?checkout=cancel&payment=paypal');

        $tokenResponse = Http::asForm()
            ->withBasicAuth($clientId, $secret)
            ->post($base . '/v1/oauth2/token', ['grant_type' => 'client_credentials']);

        if ($tokenResponse->failed()) {
            return response()->json($tokenResponse->json(), $tokenResponse->status());
        }

        $accessToken = $tokenResponse->json('access_token');
        $orderResponse = Http::withToken($accessToken)
            ->post($base . '/v2/checkout/orders', [
                'intent' => 'CAPTURE',
                'purchase_units' => [[
                    'amount' => [
                        'currency_code' => strtoupper($plan['currency'] ?? 'USD'),
                        'value'         => number_format($price, 2, '.', ''),
                    ],
                    'custom_id'   => $request->user()->id . '|' . $planName . '|' . $period,
                    'description' => 'Quickro ' . ucfirst($planName) . ' · ' . ucfirst($period),
                ]],
                'application_context' => [
                    'brand_name'          => 'Quickro POS',
                    'user_action'         => 'PAY_NOW',
                    'return_url'          => $returnUrl,
                    'cancel_url'          => $cancelUrl,
                ],
            ]);

        return response()->json($orderResponse->json(), $orderResponse->status());
    }

    /**
     * Capture a PayPal order after user approval (no webhook needed).
     * Called with the token (order_id) returned in the return_url query param.
     */
    public function capturePaypalOrder(Request $request): JsonResponse
    {
        $request->validate(['order_id' => 'required|string']);

        $clientId = config('services.paypal.client_id');
        $secret   = config('services.paypal.secret');
        if (!$clientId || !$secret) {
            return response()->json(['error' => 'PayPal is not configured'], 503);
        }

        $base    = config('services.paypal.base_url', 'https://api-m.sandbox.paypal.com');
        $orderId = $request->string('order_id')->value();

        $tokenResponse = Http::asForm()
            ->withBasicAuth($clientId, $secret)
            ->post($base . '/v1/oauth2/token', ['grant_type' => 'client_credentials']);

        if ($tokenResponse->failed()) {
            return response()->json(['error' => 'PayPal auth failed'], 503);
        }

        $accessToken    = $tokenResponse->json('access_token');
        $captureResponse = Http::withToken($accessToken)
            ->post($base . '/v2/checkout/orders/' . $orderId . '/capture');

        if ($captureResponse->failed()) {
            return response()->json($captureResponse->json(), $captureResponse->status());
        }

        $capture     = $captureResponse->json();
        $status      = $capture['status'] ?? '';
        if ($status !== 'COMPLETED') {
            return response()->json(['error' => 'PayPal capture not completed: ' . $status], 402);
        }

        // Parse plan/period from custom_id: "user_id|plan|period"
        $customId = $capture['purchase_units'][0]['custom_id'] ?? '';
        $parts    = explode('|', $customId);
        $plan     = $parts[1] ?? null;
        $period   = $parts[2] ?? 'monthly';

        if (!$plan || !config('quickropos.plans.' . $plan)) {
            return response()->json(['error' => 'Invalid plan in order'], 422);
        }

        $user = $request->user();

        // Idempotent
        if (UserSubscription::where('transaction_id', $orderId)->exists()) {
            $user->refresh();
            return response()->json([
                'plan'           => $user->currentPlan(),
                'plan_expires_at' => $user->plan_expires_at,
                'activated'      => false,
            ]);
        }

        $days        = self::PERIOD_DAYS[$period] ?? 31;
        $expiresAt   = $days !== null ? now()->addDays($days) : null;
        $captureUnit = $capture['purchase_units'][0]['payments']['captures'][0] ?? [];
        $amountPaid  = (float) ($captureUnit['amount']['value'] ?? 0);
        $currency    = strtoupper($captureUnit['amount']['currency_code'] ?? 'USD');

        UserSubscription::create([
            'user_id'        => $user->id,
            'plan'           => $plan,
            'period'         => $period,
            'source'         => 'paypal',
            'product_id'     => 'quickro_' . $plan . '_' . $period,
            'transaction_id' => $orderId,
            'receipt_data'   => json_encode($capture),
            'amount_paid'    => $amountPaid,
            'currency'       => $currency,
            'is_active'      => true,
            'starts_at'      => now(),
            'expires_at'     => $expiresAt,
        ]);

        $user->update([
            'plan'            => $plan,
            'plan_expires_at' => $expiresAt,
            'plan_source'     => 'paypal',
        ]);

        app(FirebasePushService::class)->sendFromTemplate('subscription_activated', $user);

        return response()->json([
            'plan'           => $plan,
            'plan_expires_at' => $expiresAt,
            'activated'      => true,
        ]);
    }

    public function stripeWebhook(Request $request): JsonResponse
    {
        return $this->storeWebhook('stripe', $request->json()->all() ?: $request->all());
    }

    public function paypalWebhook(Request $request): JsonResponse
    {
        return $this->storeWebhook('paypal', $request->json()->all() ?: $request->all());
    }

    private function storeWebhook(string $provider, array $payload): JsonResponse
    {
        $event = PaymentWebhookEvent::create([
            'provider' => $provider,
            'event_id' => $payload['id'] ?? null,
            'event_type' => $payload['type'] ?? ($payload['event_type'] ?? null),
            'payload' => $payload,
            'status' => 'queued',
        ]);

        ProcessPaymentWebhookJob::dispatch($event->id);

        return response()->json(['queued' => true, 'event_id' => $event->id]);
    }

    /**
     * Public endpoint — returns whether crypto payments are enabled and
     * which wallet addresses the user should send funds to.
     */
    public function cryptoConfig(): JsonResponse
    {
        $enabled = (bool) config('services.crypto.enabled', false);

        $wallets = [];
        foreach (['btc', 'eth', 'usdt'] as $coin) {
            $addr = config('services.crypto.wallet_' . $coin, '');
            if ($addr !== '') {
                $wallets[$coin] = $addr;
            }
        }

        return response()->json([
            'enabled' => $enabled,
            'wallets' => $wallets,
        ]);
    }

    /**
     * Authenticated — records a pending crypto payment awaiting admin approval.
     * The plan is NOT activated until an admin approves it.
     */
    public function cryptoSubmit(Request $request): JsonResponse
    {
        $request->validate([
            'plan'        => 'required|in:starter,growth,pro',
            'period'      => 'required|in:monthly,quarterly,biannual,yearly,one-time',
            'tx_hash'     => 'required|string|max:255',
            'coin'        => 'required|string|max:10',
            'proof_image' => 'nullable|file|image|max:5120', // up to 5 MB
        ]);

        // Prevent duplicate submissions for the same tx_hash
        if (UserSubscription::where('tx_hash', $request->tx_hash)->exists()) {
            return response()->json(['error' => 'This transaction hash has already been submitted.'], 409);
        }

        // Store optional proof image
        $proofPath = null;
        if ($request->hasFile('proof_image') && $request->file('proof_image')->isValid()) {
            $proofPath = $request->file('proof_image')
                ->store('crypto_proofs', 'public');
        }

        $user       = $request->user();
        $days       = self::PERIOD_DAYS[$request->period] ?? null;
        $planConfig = config('quickropos.plans.' . $request->plan, []);
        $price      = (float) ($planConfig['pricing'][$request->period] ?? $planConfig['price'] ?? 0.00);

        UserSubscription::create([
            'user_id'        => $user->id,
            'plan'           => $request->plan,
            'period'         => $request->period,
            'source'         => 'crypto',
            'transaction_id' => $request->tx_hash,
            'tx_hash'        => $request->tx_hash,
            'currency'       => strtoupper($request->coin),
            'amount_paid'    => $price,
            'is_active'      => false,
            'payment_status' => 'pending',
            'proof_image'    => $proofPath,
            'starts_at'      => now(),
            'expires_at'     => $days !== null ? now()->addDays($days) : null,
        ]);

        return response()->json(['submitted' => true, 'status' => 'pending']);
    }

    private function notifyUser(int|string $userId, string $title, string $body, array $data = []): void
    {
        $tokens = Device::where('user_id', $userId)
            ->whereNotNull('fcm_token')
            ->where('fcm_token', '!=', '')
            ->where('is_revoked', false)
            ->pluck('fcm_token');

        if ($tokens->isEmpty()) {
            return;
        }

        $push = app(FirebasePushService::class);
        foreach ($tokens as $token) {
            $push->sendToToken($token, $title, $body, $data);
        }
    }
}
