<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use Stripe\Stripe;
use Stripe\Checkout\Session;
use Stripe\Webhook;
use Illuminate\Support\Facades\Auth;
use App\Models\User; // Make sure to include the User model
use Carbon\Carbon;   // For date manipulation
use App\Subscription; // Make sure to include the User model
use App\Apperror; // Make sure to include the User model

use Stripe\PaymentIntent; // Added for handling PaymentIntents
use App\Models\Payment; // NEW: Add Payment model
use Illuminate\Support\Facades\DB; // For database transactions
use Illuminate\Support\Facades\Log;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;


class StripeController extends Controller
{
public function createLog(Request $request)
{
    try {
        // Validate input
        $request->validate([
            'log' => 'required|string', // Ensure it's a string
        ]);

        // Create a new log entry
        $log = new Apperror();
        $log->log = $request->input('log');
        $log->save();

        return response()->json([
            'message' => "Log saved successfully!",
            'data' => $log
        ], 201);

    } catch (\Exception $e) {
        return response()->json([
            'error' => 'Failed to save log.',
            'message' => $e->getMessage()
        ], 500);
    }
}
    
   public function createCheckoutSession(Request $request)
{
    // Get the plan type from the request (e.g., "monthly", "yearly", etc.)
    $planType = $request->query('plan_type');

    // Map the plan type to the corresponding Price ID from Stripe
    $priceId = $this->getPriceIdForPlan($planType);

    if (!$priceId) {
        return response()->json(['error' => 'Invalid plan type'], 400);
    }
    // Set Stripe API key
        Stripe::setApiKey(env('STRIPE_SECRET'));

    try {
        // Create the Stripe Checkout Session for the subscription
        $session = \Stripe\Checkout\Session::create([
            'payment_method_types' => ['card'],
            'line_items' => [[
                'price' => $priceId, // Use the mapped Price ID here
                'quantity' => 1,
            ]],
            'mode' => 'subscription',
            'success_url' => route('checkout.success') . '?session_id={CHECKOUT_SESSION_ID}',
            'cancel_url' => route('checkout.cancel'),
        ]);

        return response()->json([
            'checkout_url' => $session->url,
        ]);

    } catch (\Exception $e) {
        return response()->json(['error' => $e->getMessage()], 500);
    }
}

public function createCheckoutSessionWithTrial(Request $request)
{
    // Get the plan type from the request (e.g., "monthly" or "yearly")
    $planType = $request->query('plan_type');

    // Map the plan type to the corresponding Stripe Price ID using your helper
    $priceId = $this->getPriceIdForTrialPlan($planType);
    if (!$priceId) {
        return response()->json(['error' => 'Invalid plan type'], 400);
    }

    // Set your Stripe API key
    \Stripe\Stripe::setApiKey(env('STRIPE_SECRET'));

    // Prepare subscription data; add a 3-day trial for yearly plans only
    $subscriptionData = [];
    if ($planType === 'yearly') {
        $subscriptionData['trial_period_days'] = 3;
    }

    try {
        // Create the Stripe Checkout Session
        $sessionParams = [
            'payment_method_types' => ['card'],
            'line_items' => [[
                'price' => $priceId,
                'quantity' => 1,
            ]],
            'mode' => 'subscription',
            'success_url' => route('checkout.success') . '?session_id={CHECKOUT_SESSION_ID}',
            'cancel_url' => route('checkout.cancel'),
        ];

        // Add subscription_data only if trial_period_days was set
        if (!empty($subscriptionData)) {
            $sessionParams['subscription_data'] = $subscriptionData;
        }

        $session = \Stripe\Checkout\Session::create($sessionParams);

        return response()->json([
            'checkout_url' => $session->url,
        ]);
    } catch (\Exception $e) {
        return response()->json(['error' => $e->getMessage()], 500);
    }
}


private function getPriceIdForTrialPlan($planType)
{
    // Map plan type to corresponding Stripe Price IDs
    $priceIds = [
        'monthly'=>'price_1R7ysrRoe4Rt1rXEDwPkStWL',
        'yearly' => 'price_1R7D80Roe4Rt1rXEuipSxXtL',
    ];

    return $priceIds[$planType] ?? null;
}

private function getPriceIdForPlan($planType)
{
    // Map plan type to corresponding Stripe Price IDs
    $priceIds = [
        'weekly'=> 'price_1Qy2QORoe4Rt1rXEAvoeX1CA',
        'monthly'=>'price_1Qy1nkRoe4Rt1rXEmhmiA9OK',
        '6month' => 'price_1Q0NU1Roe4Rt1rXE89825N52',
        'yearly' => 'price_1Q0NUWRoe4Rt1rXEM78C0CjS',
        
    ];

    return $priceIds[$planType] ?? null;
}

   public function handleWebhook(Request $request)
{
    $payload = @file_get_contents('php://input');
    $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
    $event = null;

    try {
        $event = Webhook::constructEvent(
            $payload, $sig_header, env('STRIPE_WEBHOOK_SUBSCRIPTION_SECRET') // Use env() for your webhook secret
        );
    } catch (\UnexpectedValueException $e) {
        // Invalid payload
        return response()->json(['error' => 'Invalid payload'], 400);
    } catch (\Stripe\Exception\SignatureVerificationException $e) {
        // Invalid signature
        return response()->json(['error' => 'Invalid signature'], 400);
    }

    // Handle the event based on its type
    switch ($event->type) {
        case 'invoice.payment_succeeded':
            // Retrieve subscription details
            $invoice = $event->data->object;
            $subscriptionId = $invoice->subscription;
            $customerEmail = $invoice->customer_email ?? $invoice->customer->email; // Get customer email
            $customerId=$invoice->customer;


            // Find the user in the database by email
            $user = User::where('email', $customerEmail)->first();

            if ($user) {
                // Convert Stripe timestamp to Carbon date
                $currentPeriodEnd = $invoice->lines->data[0]->period->end;
                $expiredAt = Carbon::createFromTimestamp($currentPeriodEnd);


                // Update or create the subscription in the Subscription model
                $subscription = Subscription::updateOrCreate(
                    ['user_id' => $user->id],
                    [
                        'stripe_subscription_id' => $subscriptionId,
                        'stripe_price_id' => $invoice->lines->data[0]->price->id,
                        'status' => 'active',
                        'start_date' => Carbon::createFromTimestamp($invoice->lines->data[0]->period->start),
                        'end_date' => $expiredAt,
                        'is_recurring' => 'true', // Assuming recurring subscriptions
                    ]
                );

                // Update user's subscription details
                $user->customer_id = $customerId;
                $user->subscription_id = $subscriptionId;
                $user->premium = 1;
                $user->subscription_status = 'active';
                $user->subscription_ends_at = $expiredAt;
                $user->save();

                return response()->json(['message' => 'Subscription status updated successfully'], 200);
            } else {
                return response()->json(['error' => 'User not found'], 404);
            }

        case 'customer.subscription.deleted':
            // Handle subscription cancellation
            $subscriptionId = $event->data->object->id;

            $subscription = Subscription::where('stripe_subscription_id', $subscriptionId)->first();

            if ($subscription) {
                // Mark the subscription as canceled
                $subscription->status = 'canceled';
                $subscription->end_date = Carbon::now();
                $subscription->save();

                $user = User::where('id', $subscription->user_id)->first();
                // Find the associated user and update their subscription status
                $user->subscription_id = null;
                $user->premium = 0;
                $user->subscription_status = 'canceled';
                $user->subscription_ends_at = null;
                $user->save();

                return response()->json(['message' => 'Subscription canceled successfully'], 200);
            } else {
                return response()->json(['error' => 'Subscription not found'], 404);
            }
            // Find the user in the database by email
            // $user = User::where('subscription_id', $subscriptionId)->first();

            // if ($user) {
            //     // Update user's subscription details
            //     $user->subscription_id = null;
            //     $user->premium = 0;
            //     $user->subscription_status = 'canceled';
            //     $user->subscription_ends_at = null;

            //     $user->save();

            //     return response()->json(['message' => 'Subscription canceled successfully'], 200);
            // } else {
            //     return response()->json(['error' => 'User not found'], 404);
            // }

        default:
            return response()->json(['message' => 'Event type not handled'], 200);
    }
}

public function checkoutSuccess(Request $request)
    {
        // Optionally, retrieve subscription details from the request or session
        $subscriptionEndsAt = $request->query('subscription_ends_at', Carbon::now()->addMonth()); // Example value

        return view('checkout.success', [
            'subscriptionEndsAt' => $subscriptionEndsAt->format('F j, Y'), // Format date as needed
        ]);
    }

    public function checkoutCancel()
    {
        return view('checkout.cancel');
    }
    public function cancelSubscription(Request $request)
{
    // Get the authenticated user
    $user = User::where('subscription_id', $request->subscription_id)->first();

    // Check if the user has a valid subscription
    if (!$user->subscription_id) {
        return response()->json(['error' => 'No active subscription found'], 404);
    }

    // Set Stripe API keywe
    Stripe::setApiKey(env('STRIPE_SECRET'));

    try {
        // Retrieve the user's subscription from Stripe
        $subscription = \Stripe\Subscription::retrieve($request->subscription_id);

        // Cancel the subscription at the end of the billing period
        // $subscription->cancel(['at_period_end' => true]);

$updatedSubscription = \Stripe\Subscription::update($user->subscription_id, [
            'cancel_at_period_end' => true,
        ]);

        // Calculate the end date of the current billing period
        $subscriptionEndDate = Carbon::createFromTimestamp($subscription->current_period_end);

        // Update the user's subscription details in the database
        $user->subscription_status = 'canceled_at_period_end';  // Optional status to track future cancellation
        $user->subscription_ends_at = $subscriptionEndDate;
        $user->save();

        // Optionally, update the Subscription model if you have a separate model for it
        Subscription::where('user_id', $user->id)->update([
            'status' => 'canceled_at_period_end',  // Custom status to show it's pending cancellation
            'end_date' => $subscriptionEndDate,
        ]);

        return response()->json([
            'message' => 'Subscription will remain active until the end of the current billing period',
            'subscription_ends_at' => $subscriptionEndDate->toDateTimeString(), // Return the end date
        ], 200);
    } catch (\Exception $e) {
        return response()->json(['error' => $e->getMessage()], 500);
    }
}

public function saveSubscription(Request $request)
{
    $validatedData = $request->validate([
        'user_id' => 'required',
        'original_transaction_id' => 'required',
        'start_date' => 'required|date',
        'end_date' => 'required|date',
        'subscription_type' => 'required',
        'receipt_data' => 'required',
    ]);

    // Find the user
    $user = User::find($validatedData['user_id']);
    if (!$user) {
        return response()->json(['error' => 'User not found'], 404);
    }
// Convert from ISO 8601 to a MySQL-compatible format
    $start = Carbon::parse($validatedData['start_date']); 
    $end   = Carbon::parse($validatedData['end_date']);  
    // Create a new subscription record or update an existing one
    $subscription = Subscription::updateOrCreate(
        [
            'original_transaction_id' => $validatedData['original_transaction_id'],
            'user_id' => $user->id,
            'start_date' => $start,
            'end_date' => $end,
            'status' => 'active',
            'subscription_type' => $validatedData['subscription_type'],
            'receipt_data' => $validatedData['receipt_data']
        ]
    );

    // Update the user's premium status
    $user->subscription_status = 'active';
    $user->subscription_ends_at = $end;
    $user->premium = 1;
    $user->save();

    return response()->json(['message' => 'Subscription successfully activated'], 200);
}

public function getSubscription(Request $request)
{
    // 1. Validate input
    $validatedData = $request->validate([
        'user_id' => 'required|integer',
    ]);

    // 2. Find the subscription by user_id
    $subscription = Subscription::where('user_id', $validatedData['user_id'])->first();

    // 3. If no subscription is found, return an error
    if (!$subscription) {
        return response()->json(['error' => 'Subscription not found'], 404);
    }

    // 4. Return the subscription data in JSON
    return response()->json(['subscription' => $subscription], 200);
}

// scans for android
private function getPriceIdForScans($scanType)
{
    $scanPrices = [
        '20_scans' => 'price_1Qx7p0Roe4Rt1rXES9SeHb10',
        '50_scans' => 'price_1Qx7pPRoe4Rt1rXELXAlb8Mi',
        '100_scans' => 'price_1Qx7pgRoe4Rt1rXERD0ApRH1',
    ];

    return $scanPrices[$scanType] ?? null;
}
public function createScanCheckoutSession(Request $request)
{
    $validatedData = $request->validate([
        'scan_type' => 'required|string', // '20_scans', '50_scans', '100_scans'
    ]);

    $priceId = $this->getPriceIdForScans($validatedData['scan_type']);

    if (!$priceId) {
        return response()->json(['error' => 'Invalid scan type'], 400);
    }

    Stripe::setApiKey(env('STRIPE_SECRET'));

    try {
        $session = Session::create([
            'payment_method_types' => ['card'],
            'line_items' => [[
                'price' => $priceId,
                'quantity' => 1,
            ]],
            'mode' => 'payment', // One-time purchase mode
            'expand' => ['line_items'],
            'success_url' => route('checkout.success') . '?session_id={CHECKOUT_SESSION_ID}',
            'cancel_url' => route('checkout.cancel'),
            'metadata'   => [
      'scan_type' => $validatedData['scan_type'], // e.g. store this so you know
  ]
        ]);

        return response()->json([
            'checkout_url' => $session->url,
        ], 200);
    } catch (\Exception $e) {
        return response()->json(['error' => $e->getMessage()], 500);
    }
}
public function handleScanPurchaseWebhook(Request $request)
{
    // 1. Retrieve the raw payload and signature
    $payload    = @file_get_contents('php://input');
    $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'] ?? '';

    try {
        // 2. Construct the Event using your Webhook Secret
        $event = \Stripe\Webhook::constructEvent(
            $payload,
            $sig_header,
            env('STRIPE_WEBHOOK_SCANS_SECRET') // Make sure this matches you.env
        );
    } catch (\Exception $e) {
        \Log::error('Stripe Webhook Error: ' . $e->getMessage());
        return response()->json(['error' => 'Invalid payload or signature'], 400);
    }

    // 3. Check the event type
    if ($event->type === 'checkout.session.completed') {
        // The session object from Stripe
        $session = $event->data->object;

        // We only need the metadata here; no need to expand line_items if you
        // just want the 'scan_type' from metadata.
        $scanType = $session->metadata->scan_type ?? null;

        if (!$scanType) {
            \Log::error('Stripe Webhook: No scan_type found in metadata.');
            return response()->json(['error' => 'No scan_type in metadata'], 400);
        }

        // 4. Get the user. Typically, you'd match on $session->customer_details->email
        //    or some other known field in metadata. For example:
        $customerEmail = $session->customer_details->email;
        $user = \App\Models\User::where('email', $customerEmail)->first();

        if (!$user) {
            \Log::error('Stripe Webhook: User not found for email ' . $customerEmail);
            return response()->json(['error' => 'User not found'], 404);
        }

        // 5. Determine number of scans based on 'scan_type'
        // $scanCredits = match($scanType) {
        //     '20_scans'  => 20,
        //     '50_scans'  => 50,
        //     '100_scans' => 100,
        //     default  => 0,
        // };
            $scanMap = [
                '20_scans' => 20,
                '50_scans' => 50,
                '100_scans' => 100,
            ];
            
            $scanCredits = $scanMap[$scanType] ?? 0;

        if ($scanCredits === 0) {
            \Log::error("Stripe Webhook: Invalid scan_type: {$scanType}");
            return response()->json(['error' => 'Invalid scan_type'], 400);
        }

        // 6. Add the purchased scans to the user’s balance
        $user->recipes_scans_count += $scanCredits;
        $user->save();

        // 7. (Optional) Record the purchase details in a payments table
        \App\Payment::create([
            'user_id'             => $user->id,
            'amount'              => ($session->amount_total / 100) ?? 0,  // or from line_items if you prefer
            'currency'            => $session->currency ?? 'usd',
            'recipes_scans_count' => $scanCredits,
            'subscription_type'   => 'bundle', // or 'one_time_purchase'
            'payment_method'      => 'card',
        ]);

        // 8. Return success
        return response()->json(['message' => 'Scan credits added successfully'], 200);
    }

    // 9. If you have other event types, handle them, else:
    return response()->json(['message' => 'Event not handled'], 200);
}

// public function handleAppleWebhook(Request $request)
//     {
//         // Retrieve and log the entire request payload for debugging
//         $payload = $request->all();

//         // Ensure the signedPayload field is present
//         if (!isset($payload['signedPayload'])) {
//             return response()->json(['error' => 'Missing signedPayload'], 400);
//         }
//         $signedPayload = $payload['signedPayload'];

//         // Split the JWT into its three parts
//         $parts = explode('.', $signedPayload);
//         if (count($parts) !== 3) {
//             return response()->json(['error' => 'Invalid JWT format'], 400);
//         }

//         // Decode the header (first part) to extract the key ID (kid)
//         $headerJson = $this->base64UrlDecode($parts[0]);
//         $header = json_decode($headerJson, true);
//         $kid = $header['kid'] ?? null;
//         if (!$kid) {
//             return response()->json(['error' => 'Missing kid in JWT header'], 400);
//         }

//         // Fetch Apple's JWKS (JSON Web Key Set)
//         $jwksUrl = 'https://api.storekit.itunes.apple.com/inApps/v1/keys';
//         $jwksJson = file_get_contents($jwksUrl);
//         $jwks = json_decode($jwksJson, true);
//         if (!isset($jwks['keys'])) {
//             return response()->json(['error' => 'Unable to fetch Apple JWKS'], 500);
//         }

//         // Find the JWK that matches the kid from the JWT header
//         $matchingJwk = null;
//         foreach ($jwks['keys'] as $jwk) {
//             if ($jwk['kid'] === $kid) {
//                 $matchingJwk = $jwk;
//                 break;
//             }
//         }
//         if (!$matchingJwk) {
//             return response()->json(['error' => 'Matching JWK not found'], 400);
//         }

//         // Convert the JWK to a PEM-formatted public key
//         try {
//             $applePublicKey = $this->jwkToPem($matchingJwk);
//         } catch (\Exception $ex) {
//             return response()->json(['error' => 'Error processing public key'], 500);
//         }

//         try {
//             // Use Firebase JWT to decode and verify the JWT using the PEM key
//             $decoded = JWT::decode($signedPayload, new Key($applePublicKey, 'ES256'));

//             // Convert decoded object to an associative array
//             $notificationData = json_decode(json_encode($decoded), true);

//             // Extract necessary fields (adjust key names as per Apple's documentation)
//             $notificationType = $notificationData['notificationType'] ?? null;
//             $unifiedReceipt   = $notificationData['unifiedReceipt'] ?? [];
//             $latestReceiptInfo = $unifiedReceipt['latestReceiptInfo'] ?? [];
//             $originalTransactionId = $latestReceiptInfo[0]['originalTransactionId'] ?? null;

//             if (!$originalTransactionId) {
//                 return response()->json(['error' => 'Missing original transaction ID'], 400);
//             }

//             // Retrieve the subscription from your database
//             $subscription = Subscription::where('original_transaction_id', $originalTransactionId)->first();
//             if (!$subscription) {
//                 return response()->json(['error' => 'Subscription not found'], 404);
//             }

//             $user = User::find($subscription->user_id);

//             // Process the notification based on its type
//             switch ($notificationType) {
//                 case 'DID_RENEW':
//                 case 'RENEWAL':
//                     $newEndDate = Carbon::createFromTimestamp($latestReceiptInfo[0]['expiresDateMs'] / 1000);
//                     $subscription->end_date = $newEndDate;
//                     $subscription->status = 'active';
//                     $subscription->save();

//                     $user->subscription_status = 'active';
//                     $user->subscription_ends_at = $newEndDate;
//                     $user->premium = 1;
//                     $user->save();

//                     return response()->json(['message' => 'Subscription renewed successfully'], 200);

//                 case 'CANCEL':
//                     $subscription->status = 'canceled';
//                     $subscription->end_date = Carbon::now();
//                     $subscription->save();

//                     $user->subscription_status = 'canceled';
//                     $user->premium = 1;
//                     $user->save();

//                     return response()->json(['message' => 'Subscription canceled or failed to renew'], 200);
//                 case 'DID_FAIL_TO_RENEW':
//                     $subscription->status = 'canceled';
//                     $subscription->end_date = Carbon::now();
//                     $subscription->save();

//                     $user->subscription_status = 'canceled';
//                     $user->subscription_ends_at = null;
//                     $user->premium = 0;
//                     $user->save();

//                     return response()->json(['message' => 'Subscription canceled or failed to renew'], 200);

//                 case 'EXPIRED':
//                     $subscription->status = 'expired';
//                     $subscription->end_date = Carbon::now();
//                     $subscription->save();

//                     $user->subscription_status = 'expired';
//                     $user->subscription_ends_at = null;
//                     $user->premium = 0;
//                     $user->save();

//                     return response()->json(['message' => 'Subscription expired'], 200);

//                 default:
                    
//                     return response()->json(['message' => 'Unhandled notification type'], 200);
//             }
//         } catch (\Exception $e) {
//             return response()->json(['error' => 'Invalid signed payload'], 400);
//         }
//     }
    public function handleAppleWebhook(Request $request)
{
    $signedPayload = $request->getContent();

    // Split JWT into header, payload, signature
    $parts = explode('.', $signedPayload);
    if (count($parts) !== 3) {
        return response()->json(['error' => 'Invalid JWT format'], 400);
    }

    [$headerB64, $payloadB64, $signatureB64] = $parts;

    // Decode header
    $headerJson = base64_decode(strtr($headerB64, '-_', '+/'));
    $header = json_decode($headerJson, true);
    $kid = $header['kid'] ?? null;

    if (!$kid) {
        return response()->json(['error' => 'Missing "kid" in header'], 400);
    }

    // Fetch Apple public key and convert to PEM
    try {
        $jwkUrl = "https://api.storekit.itunes.apple.com/in-app-purchase/v1/keys/{$kid}";
        $jwkJson = file_get_contents($jwkUrl);
        $jwk = json_decode($jwkJson, true);
        $applePublicKey = $this->jwkToPem($jwk);
    } catch (\Exception $e) {
        return response()->json(['error' => 'Failed to fetch/convert JWK'], 500);
    }

    // Verify signature
    $dataToVerify = "$headerB64.$payloadB64";
    $signature = base64_decode(strtr($signatureB64, '-_', '+/'));

    $isValid = openssl_verify($dataToVerify, $signature, $applePublicKey, OPENSSL_ALGO_SHA256);

    if (!$isValid) {
        return response()->json(['error' => 'Invalid signature'], 400);
    }

    // Decode payload
    $payloadJson = base64_decode(strtr($payloadB64, '-_', '+/'));
    $payload = json_decode($payloadJson, true);

    $type     = $payload['notificationType'] ?? '';
    $subtype  = $payload['subtype'] ?? null;
    $data     = $payload['data'] ?? [];

    $txnInfo  = json_decode(base64_decode($data['signedTransactionInfo'] ?? ''), true);
    $renewal  = json_decode(base64_decode($data['signedRenewalInfo'] ?? ''), true);

    $originalTid = $txnInfo['originalTransactionId'] ?? $renewal['originalTransactionId'] ?? null;

    $subscription = \App\Subscription::where('original_transaction_id', $originalTid)->first();

    if (!$subscription) {
        \Log::warning('Apple webhook: unknown transaction', ['tid' => $originalTid]);
        return response()->json(['message' => 'ok'], 200); // Always ACK to Apple
    }

    $user = $subscription->user;

    switch ($type) {
        case 'DID_CHANGE_RENEWAL_STATUS':
            if ($subtype === 'AUTO_RENEW_DISABLED') {
                $subscription->status = 'canceled_at_period_end';
                $subscription->end_date = \Carbon\Carbon::createFromTimestampMs($renewal['expirationDate']);
                $user->subscription_status = 'canceled_at_period_end';
                $user->premium = 1; // still premium
            } elseif ($subtype === 'AUTO_RENEW_ENABLED') {
                $subscription->status = 'active';
                $subscription->end_date = \Carbon\Carbon::createFromTimestampMs($renewal['expirationDate']);
                $user->subscription_status = 'active';
                $user->premium = 1;
            }
            break;

        case 'DID_RENEW':
            $subscription->status = 'active';
            $subscription->end_date = \Carbon\Carbon::createFromTimestampMs($txnInfo['expiresDate']);
            $user->subscription_status = 'active';
            $user->premium = 1;
            break;

        case 'DID_FAIL_TO_RENEW':
            $subscription->status = 'in_retry';
            $user->subscription_status = 'in_retry';
            $user->premium = 0;
            break;

        case 'EXPIRED':
            $subscription->status = 'expired';
            $subscription->end_date = \Carbon\Carbon::createFromTimestampMs($txnInfo['expiresDate']);
            $user->subscription_status = 'expired';
            $user->premium = 0;
            break;

        case 'REFUND':
            $subscription->status = 'refunded';
            $subscription->end_date = now();
            $user->subscription_status = 'refunded';
            $user->premium = 0;
            break;

        default:
            \Log::info('Unhandled Apple notification', compact('type', 'subtype'));
    }

    $subscription->save();
    $user->save();

    return response()->json(['message' => 'ok'], 200);
}

public function cancelAppleSubscription(Request $request)
{
    // Validate incoming request
    $validatedData = $request->validate([
        'user_id'                => 'required|integer',
        'original_transaction_id'=> 'required|string',
    ]);

    // Find the subscription by user_id AND original_transaction_id
    $subscription = Subscription::where('user_id', $validatedData['user_id'])
                                ->where('original_transaction_id', $validatedData['original_transaction_id'])
                                ->first();

    if (!$subscription) {
        return response()->json(['error' => 'Subscription not found'], 404);
    }

    // Option A: Cancel immediately
    $subscription->status   = 'canceled';
    $subscription->save();

    // Retrieve the user
    $user = User::find($validatedData['user_id']);
    if (!$user) {
        return response()->json(['error' => 'User not found'], 404);
    }

    // Update user to reflect the canceled subscription
    $user->subscription_status = 'canceled';
    $user->premium = 0;            // remove premium immediately
    $user->subscription_ends_at = Carbon::now();

    $user->save();

    return response()->json(['message' => 'Apple Subscription canceled'], 200);
}

// public function cancelAppleSubscription(Request $request)
// {
//     // 1) validate & load…
//     $validated = $request->validate([
//         'user_id'                => 'required|integer',
//         'original_transaction_id'=> 'required|string',
//     ]);
//     $subscription = Subscription::where('user_id', $validated['user_id'])
//                                 ->where('original_transaction_id', $validated['original_transaction_id'])
//                                 ->firstOrFail();
//     $user = User::findOrFail($validated['user_id']);

//     // 2) decide trial vs. paid
//     $now = Carbon::now();
//     // adjust this to the exact trial length you offer on Apple
//     $trialDays     = 3;
//     $trialEndsAt   = $subscription->start_date->copy()->addDays($trialDays);

//     if ($now->lessThanOrEqualTo($trialEndsAt)) {
//         //
//         // → they’re still in the free‐trial window: revoke immediately
//         //
//         $subscription->status           = 'canceled';
//         $subscription->end_date         = $now;
//         $user->premium                  = 0;
//         $user->subscription_status      = 'canceled';
//         $user->subscription_ends_at      = null;
//         $message = 'Your free trial has been canceled immediately.';
//     } else {
//         //
//         // → they’re past trial and truly paid: schedule end‐of‐period cancel
//         //
//         $subscription->status           = 'canceled_at_period_end';
//         // leave end_date alone – that’s when their paid year actually expires
//         $user->subscription_status      = 'canceled_at_period_end';
//         $user->subscription_ends_at      = $subscription->end_date;
//         // don’t flip premium off yet
//         $message = 'Your subscription will remain active until '.$subscription->end_date->toDateString();
//     }

//     $subscription->save();
//     $user->save();

//     return response()->json([
//         'message'               => $message,
//         'subscription_ends_at'  => $user->subscription_ends_at,
//     ], 200);
// }
        private function base64UrlDecode($input)
    {
        $remainder = strlen($input) % 4;
        if ($remainder) {
            $padlen = 4 - $remainder;
            $input .= str_repeat('=', $padlen);
        }
        return base64_decode(strtr($input, '-_', '+/'));
    }

    /**
     * Converts a JWK (for an EC key) to PEM format.
     *
     * @param array $jwk The JSON Web Key array.
     * @return string PEM formatted public key.
     * @throws \Exception if the key type is unsupported.
     */
    private function jwkToPem(array $jwk)
    {
        if (!isset($jwk['kty']) || $jwk['kty'] !== 'EC') {
            throw new \Exception("Unsupported key type");
        }
        if (!isset($jwk['x']) || !isset($jwk['y'])) {
            throw new \Exception("Missing key parameters");
        }
        // Decode the x and y coordinates from base64url
        $x = $this->base64UrlDecode($jwk['x']);
        $y = $this->base64UrlDecode($jwk['y']);
        // Create the uncompressed EC public key (0x04 + X + Y)
        $publicKeyBytes = "\x04" . $x . $y;

        // These OIDs are for id-ecPublicKey (1.2.840.10045.2.1) and the prime256v1 curve (1.2.840.10045.3.1.7)
        $oidEcPublicKey = $this->asn1EncodeObjectIdentifier(pack('H*', '2A8648CE3D0201'));
        $oidPrime256v1 = $this->asn1EncodeObjectIdentifier(pack('H*', '2A8648CE3D030107'));

        // Build the AlgorithmIdentifier sequence
        $algorithmIdentifier = $this->asn1EncodeSequence([$oidEcPublicKey, $oidPrime256v1]);

        // Build the BIT STRING for the public key
        $publicKeyBitString = $this->asn1EncodeBitString($publicKeyBytes);

        // Build the SubjectPublicKeyInfo sequence
        $subjectPublicKeyInfo = $this->asn1EncodeSequence([$algorithmIdentifier, $publicKeyBitString]);

        $pem = "-----BEGIN PUBLIC KEY-----\n" .
               chunk_split(base64_encode($subjectPublicKeyInfo), 64, "\n") .
               "-----END PUBLIC KEY-----\n";
        return $pem;
    }

    /**
     * Encodes data with ASN.1 DER length.
     */
    private function asn1EncodeLength($length)
    {
        if ($length < 0x80) {
            return chr($length);
        }
        $lenBytes = ltrim(pack('N', $length), "\x00");
        return chr(0x80 | strlen($lenBytes)) . $lenBytes;
    }

    /**
     * Encodes a sequence in ASN.1 DER format.
     */
    private function asn1EncodeSequence(array $elements)
    {
        $payload = implode('', $elements);
        return "\x30" . $this->asn1EncodeLength(strlen($payload)) . $payload;
    }

    /**
     * Encodes an object identifier (OID) in ASN.1 DER format.
     *
     * Note: This function assumes the OID is already in binary format.
     */
    private function asn1EncodeObjectIdentifier($oidBinary)
    {
        return "\x06" . $this->asn1EncodeLength(strlen($oidBinary)) . $oidBinary;
    }

    /**
     * Encodes a BIT STRING in ASN.1 DER format.
     */
    private function asn1EncodeBitString($data)
    {
        // Prepend with a 0 byte to indicate no unused bits
        $data = "\x00" . $data;
        return "\x03" . $this->asn1EncodeLength(strlen($data)) . $data;
    }
    public function saveAppleScanPurchase(Request $request)
{

    // Validate the incoming request
    $validated = $request->validate([
        'user_id' => 'required|integer',
        'original_transaction_id' => 'required|string',
        'product_id' => 'required|string',
        'price'=>'required',
    ]);

    // Find the user
    $user = \App\Models\User::find($validated['user_id']);
    if (!$user) {
        return response()->json(['error' => 'User not found'], 404);
    }

    // Check scan credits from product_id
    $scanMap = [
        'scans_20' => 20,
        'scans_50' => 50,
        'scans_100_purchase' => 100,
    ];

    $scanCredits = $scanMap[$validated['product_id']] ?? 0;

    if ($scanCredits === 0) {
        return response()->json(['error' => 'Invalid product_id'], 400);
    }

    // Add scans
    $user->recipes_scans_count += $scanCredits;
    $user->save();

    // Save to payment table (optional)
    \App\Payment::create([
        'user_id'             => $user->id,
        'amount'              => $validated['price'],
        'currency'            => 'usd',
        'recipes_scans_count' => $scanCredits,
        'subscription_type'   => 'bundle',
        'payment_method'      => 'apple',
    ]);

    return response()->json(['message' => 'Scan credits added successfully'], 200);
}
    
//     public function revenuecatWebhook(Request $request)
// {
//     // 1. Parse payload
//     $payload = $request->getContent();
//     $json    = json_decode($payload, true);
//     if (json_last_error() !== JSON_ERROR_NONE || empty($json['event'])) {
//         Log::warning('RevenueCat webhook: invalid payload', ['raw' => $payload]);
//         return response()->json(['error' => 'Invalid payload'], 400);
//     }

//     $event      = $json['event'];
//     $type       = $event['type'];            // e.g. "INITIAL_PURCHASE", "DID_RENEW", "CANCELLATION", "EXPIRATION"
//     $appUserId  = $event['app_user_id'];     // your stored RevenueCat user identifier
//     $productId  = $event['product_id'] ?? null;
//     $purchase   = $event['purchase_date'] ?? null;   // ISO 8601 timestamp
//     $expires    = $event['expires_date'] ?? null;    // ISO 8601 timestamp (if present)
//     $cancelDate = $event['cancel_date'] ?? null;     // ISO 8601 timestamp (if present)

//     // 2. Find your user
//     $user = User::where('revenuecat_user_id', $appUserId)->first();
//     if (! $user) {
//         Log::warning('RevenueCat webhook: user not found', ['app_user_id' => $appUserId]);
//         // Always ack to RevenueCat
//         return response()->json(['message' => 'ok'], 200);
//     }


//     // 4. Flip user flags based on event type
//     switch ($type) {
//         case 'INITIAL_PURCHASE':
//         case 'DID_RENEW':
//             $user->premium             = 1;
//             $user->subscription_status = 'active';
//             $user->subscription_ends_at = $expires ? Carbon::parse($expires) : null;
//             break;

//         case 'CANCELLATION':
//             $user->premium             = 0;
//             $user->subscription_status = 'canceled';
//             // If RevenueCat gives you a cancel_date, store that; otherwise use now()
//             $user->subscription_ends_at = $cancelDate
//                 ? Carbon::parse($cancelDate)
//                 : Carbon::now();
//             break;

//         case 'EXPIRATION':
//             $user->premium             = 0;
//             $user->subscription_status = 'expired';
//             $user->subscription_ends_at = $expires
//                 ? Carbon::parse($expires)
//                 : Carbon::now();
//             break;

//         // you can add more cases: "BILLING_ISSUE", "BILLING_RESUMED", etc.
//         default:
//             Log::info("RevenueCat webhook: unhandled event type {$type}");
//     }

//     $user->save();

//     return response()->json(['message' => 'ok'], 200);
// }

// public function revenuecatWebhook(Request $request)
// {
//     try {
//         // --- 0) Content-Type guard ---
//         $ct = $request->header('Content-Type') ?? $request->header('content-type');
//         if (! $ct || stripos($ct, 'application/json') === false) {
//             \Log::warning('RC: bad content-type', ['ct' => $ct]);
//             return response()->json(['error' => 'Unsupported Media Type'], 415);
//         }

//         // --- 0a) Auth (raw or "Bearer <secret>") ---
//         $expected = (string) (config('services.revenuecat.webhook_secret') ?: env('REVENUECAT_WEBHOOK_SECRET') ?: '');
//         $authHdr  = $request->header('Authorization')
//                     ?? $request->server('HTTP_AUTHORIZATION')
//                     ?? $request->server('REDIRECT_HTTP_AUTHORIZATION')
//                     ?? '';
//         $authHdr = trim($authHdr);
//         $token   = stripos($authHdr, 'Bearer ') === 0 ? trim(substr($authHdr, 7)) : $authHdr;
//         if ($expected === '' || ! hash_equals(trim($expected), trim($token))) {
//             \Log::warning('RC: unauthorized webhook');
//             return response()->json(['error' => 'Unauthorized'], 401);
//         }

//         // --- 1) Parse JSON ---
//         $payload = $request->getContent();
//         $json    = json_decode($payload, true);
//         if (json_last_error() !== JSON_ERROR_NONE || empty($json['event'])) {
//             \Log::warning('RC: invalid payload', ['raw' => \Illuminate\Support\Str::limit($payload, 512)]);
//             return response()->json(['error' => 'Invalid payload'], 400);
//         }

//         $event = $json['event'];

//         // --- Helpers ---
//         $msToCarbon = function ($val) {
//             if ($val === null || $val === '') return null;
//             try { return \Carbon\Carbon::createFromTimestampMs((int) $val); }
//             catch (\Throwable $e) { return null; }
//         };
//         // Column-safe setter: only sets attribute if the column exists
//         $setCol = function (\Illuminate\Database\Eloquent\Model $model, string $col, $value): void {
//             static $cache = [];
//             $table = $model->getTable();
//             $key   = $table.'.'.$col;
//             if (!array_key_exists($key, $cache)) {
//                 $cache[$key] = \Illuminate\Support\Facades\Schema::hasColumn($table, $col);
//             }
//             if ($cache[$key]) {
//                 $model->setAttribute($col, $value);
//             }
//         };

//         // --- 1a) Extract fields ---
//         $type        = $event['type'] ?? null;
//         $eventId     = $event['id'] ?? null;
//         $appUserId   = $event['app_user_id'] ?? null;
//         $productId   = $event['product_id'] ?? null;
//         $environment = $event['environment'] ?? null;

//         $occurredAt  = $msToCarbon($event['event_timestamp_ms'] ?? null)
//                     ?: $msToCarbon($event['purchased_at_ms'] ?? null)
//                     ?: $msToCarbon($event['expiration_at_ms'] ?? null)
//                     ?: now();

//         $purchaseAt  = $msToCarbon($event['purchased_at_ms']  ?? null);
//         $expiresAt   = $msToCarbon($event['expiration_at_ms'] ?? null);
//         $cancelAt    = $msToCarbon($event['cancellation_at_ms'] ?? null) ?: $occurredAt;

//         // Resolution aids
//         $aliases         = (array)($event['aliases'] ?? []);
//         $transferredFrom = (array)($event['transferred_from'] ?? []);
//         $transferredTo   = (array)($event['transferred_to'] ?? []);
//         $originalId      = $event['original_app_user_id'] ?? null;

//         if (! $type) {
//             \Log::warning('RC: missing type');
//             return response()->json(['message' => 'ok'], 200);
//         }

//         // --- 2) Resolve user robustly ---
//         $idsToTry = array_values(array_unique(array_filter(array_merge(
//             [$appUserId],
//             $aliases,
//             $transferredFrom,
//             [$originalId]
//         ))));

//         $user = null;
//         if (!empty($idsToTry)) {
//             $user = \App\Models\User::whereIn('revenuecat_user_id', $idsToTry)->first();
//         }
//         if (! $user && $type === 'TRANSFER' && !empty($transferredFrom)) {
//             $user = \App\Models\User::whereIn('revenuecat_user_id', $transferredFrom)->first();
//         }

//         if (! $user) {
//             \Log::warning('RC: user not found', compact('appUserId','type','productId','eventId'));
//             return response()->json(['message' => 'ok'], 200);
//         }

//         // --- 2a) Adopt the new app_user_id so future events match directly ---
//         $newOfficialId = $appUserId ?: ($transferredTo[0] ?? null);
//         if ($newOfficialId && $user->revenuecat_user_id !== $newOfficialId) {
//             $user->revenuecat_user_id = $newOfficialId;
//             $user->save();
//             \Log::info('RC: adopted new app_user_id for user', [
//                 'user_id' => $user->id,
//                 'new_id'  => $newOfficialId
//             ]);
//         }

//         // --- 3) Ignore stale events if you keep a timestamp column ---
//         $hasLastEvtCol = \Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'last_billing_event_at');
//         if ($hasLastEvtCol && $user->last_billing_event_at && $occurredAt->lt($user->last_billing_event_at)) {
//             \Log::info('RC: stale event ignored', [
//                 'event_id' => $eventId,
//                 'type'     => $type,
//                 'user'     => $user->id,
//                 'evtAt'    => $occurredAt->toIso8601String(),
//             ]);
//             return response()->json(['message' => 'ok'], 200);
//         }

//         // --- 4) State machine (users table only, column-safe) ---
//         $status = $hasStatus = \Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'subscription_status')
//                 ? $user->subscription_status : null;
//         $endsAt = \Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'subscription_ends_at')
//                 ? $user->subscription_ends_at : null;

//         switch ($type) {
//             case 'INITIAL_PURCHASE':
//             case 'RENEWAL':
//             case 'SUBSCRIPTION_RESTARTED':
//                 if (\Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'premium')) {
//                     $user->premium = 1;
//                 }
//                 if ($endsAt !== null) $endsAt = $expiresAt ?: $endsAt;
//                 if ($hasStatus !== null) $status = 'active';
//                 if (\Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'subscription_cancel_at')) {
//                     $setCol($user, 'subscription_cancel_at', null);
//                 }
//                 break;

//             case 'CANCELLATION':
//                 if (\Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'premium')) {
//                     $user->premium = 1; // keep until expiry
//                 }
//                 if ($endsAt !== null) $endsAt = $expiresAt ?: ($endsAt ?: now());
//                 if ($hasStatus !== null) $status = 'canceled';
//                 if (\Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'subscription_cancel_at')) {
//                     $setCol($user, 'subscription_cancel_at', $cancelAt);
//                 }
//                 break;

//             case 'EXPIRATION':
//             case 'SUBSCRIPTION_EXPIRED':
//                 if (\Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'premium')) {
//                     $user->premium = 0;
//                 }
//                 if ($endsAt !== null) $endsAt = $expiresAt ?: now();
//                 if ($hasStatus !== null) $status = 'expired';
//                 break;

//             case 'BILLING_ISSUE':
//             case 'DID_FAIL_TO_RENEW':
//                 if (\Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'premium')) {
//                     $user->premium = 1; // keep during grace
//                 }
//                 if ($hasStatus !== null) $status = 'past_due';
//                 break;

//             case 'PRODUCT_CHANGE':
//                 if (\Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'premium')) {
//                     $user->premium = 1;
//                 }
//                 if ($endsAt !== null) $endsAt = $expiresAt ?: $endsAt;
//                 if ($hasStatus !== null) $status = 'active';
//                 if (\Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'current_product_id')) {
//                     $setCol($user, 'current_product_id', $productId);
//                 }
//                 break;

//             case 'SUBSCRIPTION_PAUSED':
//                 if (\Illuminate\Support\Facades.Schema::hasColumn($user->getTable(), 'premium')) {
//                     $user->premium = 0;
//                 }
//                 if ($hasStatus !== null) $status = 'paused';
//                 break;

//             case 'SUBSCRIPTION_REVOKED':
//                 if (\Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'premium')) {
//                     $user->premium = 0;
//                 }
//                 if ($endsAt !== null) $endsAt = now();
//                 if ($hasStatus !== null) $status = 'revoked';
//                 break;

//             case 'TRANSFER':
//                 // On restore/transfer, keep access and adopt IDs above
//                 if (\Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'premium')) {
//                     $user->premium = 1;
//                 }
//                 if ($hasStatus !== null) $status = $status ?: 'active';
//                 if ($endsAt !== null) $endsAt = $expiresAt ?: $endsAt;
//                 break;

//             case 'NON_SUBSCRIPTION_PURCHASE':
//             case 'PURCHASE':
//                 \Log::info('RC: non-subscription purchase (ignored for users-only)', [
//                     'user_id' => $user->id,
//                     'productId' => $productId,
//                 ]);
//                 break;

//             default:
//                 \Log::info("RC: unhandled type {$type}", compact('eventId','appUserId'));
//                 break;
//         }

//         // --- 5) Save (column-safe) ---
//         \Illuminate\Support\Facades\DB::transaction(function () use (
//             $user, $status, $endsAt, $occurredAt, $environment, $productId, $setCol
//         ) {
//             if ($status !== null) {
//                 $setCol($user, 'subscription_status', $status);
//             }
//             if ($endsAt !== null) {
//                 $setCol($user, 'subscription_ends_at', $endsAt);
//             }
//             // Only set these if the columns exist
//             $setCol($user, 'last_billing_event_at', $occurredAt);
//             if ($environment) $setCol($user, 'billing_environment', $environment);
//             if ($productId)   $setCol($user, 'current_product_id', $productId);

//             $user->save();
//         });

//         \Log::info('RC: applied event', [
//             'user_id'  => $user->id,
//             'type'     => $type,
//             'status'   => $status ?? null,
//             'endsAt'   => isset($endsAt) ? optional($endsAt)->toIso8601String() : null,
//             'evtAt'    => $occurredAt->toIso8601String(),
//             'event_id' => $eventId,
//             'appUserId'=> $appUserId,
//         ]);

//         return response()->json(['message' => 'ok'], 200);

//     } catch (\Throwable $e) {
//         \Log::error('RC: webhook exception', [
//             'ex'   => get_class($e),
//             'msg'  => $e->getMessage(),
//             'file' => $e->getFile().':'.$e->getLine(),
//         ]);
//         return response()->json(['error' => 'server_error'], 500);
//     }
// }

public function revenuecatWebhook(Request $request)
{
    try {
        // --- 0) Content-Type guard ---
        $ct = $request->header('Content-Type') ?? $request->header('content-type');
        if (! $ct || stripos($ct, 'application/json') === false) {
            return response()->json(['error' => 'Unsupported Media Type'], 415);
        }

        // --- 0a) Auth (accepts raw or "Bearer <secret>") ---
        $expected = (string) (config('services.revenuecat.webhook_secret') ?: env('REVENUECAT_WEBHOOK_SECRET') ?: '');
        $authHdr  = $request->header('Authorization')
                    ?? $request->server('HTTP_AUTHORIZATION')
                    ?? $request->server('REDIRECT_HTTP_AUTHORIZATION')
                    ?? '';
        $authHdr = trim($authHdr);
        $token   = stripos($authHdr, 'Bearer ') === 0 ? trim(substr($authHdr, 7)) : $authHdr;

        if ($expected === '' || ! hash_equals(trim($expected), trim($token))) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        // --- 1) Parse JSON ---
        $payload = $request->getContent();
        $json    = json_decode($payload, true);
        if (json_last_error() !== JSON_ERROR_NONE || empty($json['event'])) {
            return response()->json(['error' => 'Invalid payload'], 400);
        }

        $event = $json['event'];

        // --- Helpers ---
        $msToCarbon = function ($val) {
            if ($val === null || $val === '') return null;
            try { return \Carbon\Carbon::createFromTimestampMs((int) $val); }
            catch (\Throwable $e) { return null; }
        };
        // Column-safe assign (write only if column exists)
        $setCol = function (\Illuminate\Database\Eloquent\Model $model, string $col, $value): void {
            static $exists = [];
            $table = $model->getTable();
            $key   = $table.'.'.$col;
            if (!array_key_exists($key, $exists)) {
                $exists[$key] = \Illuminate\Support\Facades\Schema::hasColumn($table, $col);
            }
            if ($exists[$key]) {
                $model->setAttribute($col, $value);
            }
        };

        // --- 1a) Extract fields we use ---
        $type        = $event['type'] ?? null;
        $eventId     = $event['id'] ?? null;
        $appUserId   = $event['app_user_id'] ?? null;
        $productId   = $event['product_id'] ?? null;
        $environment = $event['environment'] ?? null;

        $occurredAt  = $msToCarbon($event['event_timestamp_ms'] ?? null)
                    ?: $msToCarbon($event['purchased_at_ms'] ?? null)
                    ?: $msToCarbon($event['expiration_at_ms'] ?? null)
                    ?: now();

        $purchaseAt  = $msToCarbon($event['purchased_at_ms']  ?? null);
        $expiresAt   = $msToCarbon($event['expiration_at_ms'] ?? null);
        $cancelAt    = $msToCarbon($event['cancellation_at_ms'] ?? null) ?: $occurredAt;

        // Resolution aids
        $aliases         = (array)($event['aliases'] ?? []);
        $transferredFrom = (array)($event['transferred_from'] ?? []);
        $transferredTo   = (array)($event['transferred_to'] ?? []);
        $originalId      = $event['original_app_user_id'] ?? null;

        if (! $type) {
            return response()->json(['message' => 'ok'], 200);
        }

        // --- 2) Resolve user robustly ---
        $idsToTry = array_values(array_unique(array_filter(array_merge(
            [$appUserId],
            $aliases,
            $transferredFrom,
            [$originalId]
        ))));

        $user = null;
        if (!empty($idsToTry)) {
            $user = \App\Models\User::whereIn('revenuecat_user_id', $idsToTry)->first();
        }
        if (! $user && $type === 'TRANSFER' && !empty($transferredFrom)) {
            $user = \App\Models\User::whereIn('revenuecat_user_id', $transferredFrom)->first();
        }

        if (! $user) {
            return response()->json(['message' => 'ok'], 200);
        }

        // --- 2a) Adopt the new app_user_id so future events match directly ---
        $newOfficialId = $appUserId ?: ($transferredTo[0] ?? null);
        if ($newOfficialId && $user->revenuecat_user_id !== $newOfficialId) {
            $user->revenuecat_user_id = $newOfficialId;
            $user->save();
        }

        // --- 3) Ignore stale events if you keep a timestamp column ---
        $hasLastEvtCol = \Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'last_billing_event_at');
        if ($hasLastEvtCol && $user->last_billing_event_at && $occurredAt->lt($user->last_billing_event_at)) {
            return response()->json(['message' => 'ok'], 200);
        }

        // --- 4) State machine (users table only, column-safe) ---
        $hasStatusCol = \Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'subscription_status');
        $hasEndsCol   = \Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'subscription_ends_at');
        $hasPremium   = \Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'premium');
        $hasCancelAt  = \Illuminate\Support\Facades\Schema::hasColumn($user->getTable(), 'subscription_cancel_at');

        $status = $hasStatusCol ? $user->subscription_status : null;
        $endsAt = $hasEndsCol   ? $user->subscription_ends_at : null;

        switch ($type) {
            // New/renewal/restore → Active
            case 'INITIAL_PURCHASE':
            case 'RENEWAL':
            case 'SUBSCRIPTION_RESTARTED':
                if ($hasPremium) $user->premium = 1;
                if ($hasEndsCol) $endsAt = $expiresAt ?: $endsAt;
                if ($hasStatusCol) $status = 'active';
                if ($hasCancelAt) $setCol($user, 'subscription_cancel_at', null);
                break;

            // User reverted a cancellation during current period
            case 'UNCANCELLATION':
                if ($hasPremium) $user->premium = 1;
                if ($hasStatusCol) $status = 'active';
                // keep the same $endsAt (no change to expiration)
                if ($hasCancelAt) $setCol($user, 'subscription_cancel_at', null);
                break;

            // User scheduled cancel → keep benefits until expiry
            case 'CANCELLATION':
                if ($hasPremium) $user->premium = 1;
                if ($hasEndsCol) $endsAt = $expiresAt ?: ($endsAt ?: now());
                if ($hasStatusCol) $status = 'canceled';
                if ($hasCancelAt) $setCol($user, 'subscription_cancel_at', $cancelAt);
                break;

            // Actually expired → remove access
            case 'EXPIRATION':
            case 'SUBSCRIPTION_EXPIRED':
                if ($hasPremium) $user->premium = 0;
                if ($hasEndsCol) $endsAt = $expiresAt ?: now();
                if ($hasStatusCol) $status = 'expired';
                break;

            // Billing issue / grace period
            case 'BILLING_ISSUE':
            case 'DID_FAIL_TO_RENEW':
                if ($hasPremium) $user->premium = 1; // you can change to 0 if you don't grant grace
                if ($hasStatusCol) $status = 'past_due';
                break;

            // Plan change → stay active
            case 'PRODUCT_CHANGE':
                if ($hasPremium) $user->premium = 1;
                if ($hasEndsCol) $endsAt = $expiresAt ?: $endsAt;
                if ($hasStatusCol) $status = 'active';
                $setCol($user, 'current_product_id', $productId);
                break;

            // Android paused / revoked
            case 'SUBSCRIPTION_PAUSED':
                if ($hasPremium) $user->premium = 0;
                if ($hasStatusCol) $status = 'paused';
                break;

            case 'SUBSCRIPTION_REVOKED':
                if ($hasPremium) $user->premium = 0;
                if ($hasEndsCol) $endsAt = now();
                if ($hasStatusCol) $status = 'revoked';
                break;

            // Expiration extended (grace/CS adjustment) → keep active, update end date if provided
            case 'SUBSCRIPTION_EXTENDED':
                if ($hasPremium) $user->premium = 1;
                if ($hasStatusCol) $status = $status ?: 'active';
                if ($hasEndsCol) $endsAt = $expiresAt ?: $endsAt;
                break;

            // Temporary unlock while store validation finishes
            case 'TEMPORARY_ENTITLEMENT_GRANT':
                if ($hasPremium) $user->premium = 1;
                if ($hasStatusCol) $status = 'temporary';
                if ($hasEndsCol) $endsAt = $expiresAt ?: $endsAt;
                break;

            // Transfers (restore) → adopt IDs above, keep active
            case 'TRANSFER':
                if ($hasPremium) $user->premium = 1;
                if ($hasStatusCol) $status = $status ?: 'active';
                if ($hasEndsCol) $endsAt = $expiresAt ?: $endsAt;
                break;

            // Non-subscription purchases (ignored in users-only flow)
            case 'NON_SUBSCRIPTION_PURCHASE':
            case 'PURCHASE':
                break;

            default:
                break;
        }

        // --- 5) Persist (column-safe) ---
        \Illuminate\Support\Facades\DB::transaction(function () use (
            $user, $status, $endsAt, $occurredAt, $environment, $productId, $setCol
        ) {
            if ($status !== null)   $setCol($user, 'subscription_status', $status);
            if ($endsAt !== null)   $setCol($user, 'subscription_ends_at', $endsAt);
            $setCol($user, 'last_billing_event_at', $occurredAt);
            if ($environment) $setCol($user, 'billing_environment', $environment);
            if ($productId)   $setCol($user, 'current_product_id', $productId);

            $user->save();
        });


        return response()->json(['message' => 'ok'], 200);

    } catch (\Throwable $e) {
        return response()->json(['error' => 'server_error'], 500);
    }
}

}