<?php

namespace App\Http\Controllers;

use App\Models\Item;
use App\Models\Package;
use App\Models\PaymentConfiguration;
use App\Models\PaymentTransaction;
use App\Models\UserFcmToken;
use App\Models\UserPurchasedPackage;
use App\Services\NotificationService;
use App\Services\Payment\PaymentService;
use App\Services\Payment\PayPalPayment;
use App\Services\ResponseService;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Razorpay\Api\Api;
use Stripe\Exception\SignatureVerificationException;
use Stripe\Exception\UnexpectedValueException;
use Stripe\Webhook;
use PhonePe\PhonePe;
use Throwable;


class WebhookController extends Controller {
    public function stripe() {
        $payload = @file_get_contents('php://input');
        try {
            // Verify webhook signature and extract the event.
            // See https://stripe.com/docs/webhooks/signatures for more information.
            // $data = json_decode($payload, false, 512, JSON_THROW_ON_ERROR);

            $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];

            // You can find your endpoint's secret in your webhook settings
            $paymentConfiguration = PaymentConfiguration::select('webhook_secret_key')->where('payment_method', 'Stripe')->first();
            $endpoint_secret = $paymentConfiguration['webhook_secret_key'] ;
            $event = Webhook::constructEvent(
                $payload, $sig_header, $endpoint_secret
            );

            $metadata = $event->data->object->metadata;

            // Use this lines to Remove Signature verification for debugging purpose
            // $event = json_decode($payload, false, 512, JSON_THROW_ON_ERROR);
            // $metadata = (array)$event->data->object->metadata;

            Log::info("Stripe Webhook : ", [$event]);
            // handle the events
            switch ($event->type) {
                case 'payment_intent.created':
                    //Do nothing
                    http_response_code(200);
                    break;
                case 'payment_intent.succeeded':
                    $response = $this->assignPackage($metadata['payment_transaction_id'], $metadata['user_id'], $metadata['package_id']);

                    if ($response['error']) {
                        Log::error("Stripe Webhook : ", [$response['message']]);
                    }
                    http_response_code(200);
                    break;
                case 'payment_intent.payment_failed':
                    $response = $this->failedTransaction($metadata['payment_transaction_id'], $metadata['user_id']);
                    if ($response['error']) {
                        Log::error("Stripe Webhook : ", [$response['message']]);
                    }
                    http_response_code(400);
                    break;
                default:
                    Log::error('Stripe Webhook : Received unknown event type', [$event->type]);
            }
        } catch (UnexpectedValueException) {
            // Invalid payload
            echo "Stripe Webhook : Payload Mismatch";
            Log::error("Stripe Webhook : Payload Mismatch");
            http_response_code(400);
            exit();
        } catch (SignatureVerificationException) {
            // Invalid signature
            echo "Stripe Webhook : Signature Verification Failed";
            Log::error("Stripe Webhook : Signature Verification Failed");
            http_response_code(400);
            exit();
        } catch
        (Throwable $e) {
            Log::error("Stripe Webhook : Error occurred", [$e->getMessage() . ' --> ' . $e->getFile() . ' At Line : ' . $e->getLine()]);
            http_response_code(400);
            exit();
        }
    }

    public function razorpay() {
        try {
            Log::info("Razorpay Webhook called");
            $paymentConfiguration = PaymentConfiguration::select('webhook_secret_key','api_key')->where('payment_method', 'razorpay')->first();
            $webhookSecret = $paymentConfiguration['webhook_secret_key'];
            $webhookPublic = $paymentConfiguration["api_key"];

            // get the json data of payment
            $webhookBody = file_get_contents('php://input');
            $data = json_decode($webhookBody, false, 512, JSON_THROW_ON_ERROR);
            Log::info("Razorpay Webhook : ", [$data]);

            $api = new Api($webhookPublic, $webhookSecret);

            $metadata = $data->payload->payment->entity->notes;

            if (isset($data->event) && $data->event == 'payment.captured') {

                //checks the signature
                $expectedSignature = hash_hmac("SHA256", $webhookBody, $webhookSecret);

                $api->utility->verifyWebhookSignature($webhookBody, $expectedSignature, $webhookSecret);

                $paymentTransactionData = PaymentTransaction::where('id', $metadata->payment_transaction_id)->first();
                if ($paymentTransactionData == null) {
                    Log::error("Stripe Webhook : Payment Transaction id not found");
                }

                if ($paymentTransactionData->status == "succeed") {
                    Log::info("Stripe Webhook : Transaction Already Succeed");
                }
                $response = $this->assignPackage($metadata->payment_transaction_id, $metadata->user_id, $metadata->package_id);

                if ($response['error']) {
                    Log::error("Razorpay Webhook : ", [$response['message']]);
                }
                http_response_code(200);
            } elseif (isset($data->event) && $data->event == 'payment.failed') {
                $response = $this->failedTransaction($metadata->payment_transaction_id, $metadata->user_id);
                if ($response['error']) {
                    Log::error("Razorpay Webhook : ", [$response['message']]);
                }
                http_response_code(400);
            } elseif (isset($data->event) && $data->event == 'payment.authorized') {

                http_response_code(200);
            } else {
                Log::error('Unknown Event Type', [$data->event]);
            }
        } catch (Throwable $th) {
            Log::error($th);
            Log::error('Razorpay --> Webhook Error Occurred');
            http_response_code(400);
        }
    }

    public function paystack() {
        try {
            // only a post with paystack signature header gets our attention
            if (!array_key_exists('HTTP_X_PAYSTACK_SIGNATURE', $_SERVER) || (strtoupper($_SERVER['REQUEST_METHOD']) != 'POST')) {
                echo "Signature not found";
                http_response_code(400);
                exit(0);
            }

            // Retrieve the request's body
            $input = @file_get_contents("php://input");
            $paymentConfiguration = PaymentConfiguration::select('webhook_secret_key')->where('payment_method', 'paystack')->first();
            $endpoint_secret = $paymentConfiguration['webhook_secret_key'];

            if (hash_equals($_SERVER['HTTP_X_PAYSTACK_SIGNATURE'], hash_hmac('sha512', $input, $endpoint_secret))) {
                echo "Signature does not match";
                http_response_code(400);
                exit(0);
            }
            // parse event (which is json string) as object
            // Do something - that will not take long - with $event
            $event = json_decode($input, false, 512, JSON_THROW_ON_ERROR);
            $metadata = $event->data->metadata;
            Log::info("Paystack Webhook event Called", [$event]);
            switch ($event->event) {
                case 'charge.success':
                    $response = $this->assignPackage($metadata->payment_transaction_id, $metadata->user_id, $metadata->package_id);
                    if ($response['error']) {
                        Log::error("Paystack Webhook : ", [$response['message']]);
                    }
                    http_response_code(200);
                    break;
                default:
                    Log::error('Paystack Webhook : Received unknown event type', [$event->event]);
            }
            http_response_code(200);
            exit();
        } catch (Throwable $e) {
            Log::error("Paystack Webhook : Error occurred", [$e->getMessage() . ' --> ' . $e->getFile() . ' At Line : ' . $e->getLine()]);
            http_response_code(400);
            exit();
        }
    }

    public function paystackSuccessCallback(){
        ResponseService::successResponse("Payment done successfully.");
    }

         public function phonePe()
    {
        try {
            Log::info("PhonePe Webhook event called");

            // Must be POST
            if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') {
                Log::error("Invalid request method");
                return response('Invalid request method', 400);
            }

            // Raw payload
            $content = trim(file_get_contents("php://input"));
            $jsonInput = json_decode($content, true);
            Log::info("PhonePe Webhook Raw Payload", [$jsonInput]);

            if (!$jsonInput) {
                Log::error("Invalid JSON payload");
                return response()->json(['error' => 'Invalid JSON'], 400);
            }

            // --- Step 1: Verify Authorization Header ---
            $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? null;
            if (!$authHeader) {
                Log::error("Missing Authorization header");
                return response()->json(['error' => 'Unauthorized'], 401);
            }

            // Fetch credentials (from DB or static values)
            $paymentConfiguration = PaymentConfiguration::where('payment_method', 'PhonePe')->first();
            $username = $paymentConfiguration->username ?? '';
            $password = $paymentConfiguration->password ?? '';


             $expectedHash = hash('sha256', $username . ':' . $password);

            if (hash_equals($expectedHash, $authHeader)) {
                Log::error("Authorization header mismatch");
                return response()->json(['error' => 'Unauthorized'], 401);
            }

            // --- Step 2: Extract Order Info ---
            $payload = $jsonInput['payload'] ?? [];
            $merchantOrderId = $payload['merchantOrderId'] ?? null;
            $orderId = $payload['orderId'] ?? null;
            $state = $payload['state'] ?? null;
            $amount = $payload['amount'] ?? null;
                        $metadata = $payload['metadata'] ?? [];

            // Try from merchantOrderId (for web)
            $transaction_id = null;
            $package_id = null;
            $user_id = null;
            
            if (!empty($merchantOrderId) && strpos($merchantOrderId, 't-') === 0) {
                $parts = explode('-', $merchantOrderId);
                $transaction_id = $parts[1] ?? null;
                $package_id = $parts[3] ?? null;
                $user_id = $metadata['user_id'] ?? null;
            }
            
            // Fallback for App SDK
            if (empty($transaction_id) || empty($package_id)) {
                $transaction_id = $metadata['payment_transaction_id'] ?? null;
                $package_id = $metadata['package_id'] ?? null;
                $user_id = $metadata['user_id'] ?? null;
            }
            
            Log::info("PhonePe Identified Meta", [
                'transaction_id' => $transaction_id,
                'package_id' => $package_id,
                'user_id' => $user_id,
            ]);

            Log::info("PhonePe Payment Event", [
                'event' => $jsonInput['event'] ?? null,
                'merchantOrderId' => $merchantOrderId,
                'orderId' => $orderId,
                'state' => $state,
                'amount' => $amount,
            ]);

            // --- Step 3: Business Logic ---
            if ($state === "COMPLETED" || $state === "SUCCESS") {
                // Parse merchantOrderId (example format: t-151-p-1)
                $parts = explode('-', $merchantOrderId);
                $transaction_id = $parts[1] ?? null;
                $package_id = $parts[3] ?? null;

                $paymentTransaction = PaymentTransaction::find($transaction_id);

                if ($paymentTransaction) {
                    $metadata = [
                        'payment_transaction_id' => $transaction_id,
                        'package_id' => $package_id,
                        'user_id' => $paymentTransaction->user_id,
                    ];

                    $response = $this->assignPackage(
                        $metadata['payment_transaction_id'],
                        $metadata['user_id'],
                        $metadata['package_id']
                    );

                    if ($response['error']) {
                        Log::error("PhonePe Webhook assignPackage error", [$response['message']]);
                    }
                }

                return response()->json(['status' => 'success'], 200);
            } else {
                Log::warning("PhonePe Payment Failed", $jsonInput);
                return response()->json(['status' => 'failed'], 400);
            }

        } catch (\Throwable $e) {
            Log::error("PhonePe Webhook Error", [
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
            ]);
            return response()->json(['error' => 'Webhook processing failed'], 500);
        }
    }
        public function flutterwave()
    {
        try {
            if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') {
                Log::error("Invalid request method");
                return response('Invalid request method', 400);
            }

            $content = trim(file_get_contents("php://input"));
            $payload = json_decode($content, true);
            if (!$payload || empty($payload)) {
                Log::error('Invalid webhook payload');
                return response()->json(['error' => 'Invalid payload'], 400);
            }

            if (!isset($payload['txRef']) || !isset($payload['status'])) {
                Log::error('Missing required fields in webhook payload');
                return response()->json(['error' => 'Invalid payload structure'], 400);
            }

            $transactionRef = $payload['txRef'];
            $status = $payload['status'];
            $amount = $payload['amount'];
            $currency = $payload['currency'];
            $customer = $payload['customer'];
            $transactionId = $payload['id'];

            $parts = explode('-', $transactionRef);
            $transaction_id = $parts[1];
            $package_id = $parts[3];
            $paymentTransaction = PaymentTransaction::findOrFail($transaction_id);

            if ($status === 'successful') {
                Log::info('Flutterwave Payment Successful', [
                    'transactionId' => $transactionId,
                    'transactionRef' => $transactionRef,
                    'amount' => $amount,
                    'currency' => $currency,
                    'customer' => $customer
                ]);

                $metadata = [
                    'payment_transaction_id' => $transaction_id,
                    'package_id' => $package_id,
                    'user_id' => $paymentTransaction->user_id,
                ];

                $response = $this->assignPackage($metadata['payment_transaction_id'], $metadata['user_id'], $metadata['package_id']);
                if ($response['error']) {
                    Log::error("Flutterwave Webhook : ", [$response['message']]);
                }

                return response()->json(['status' => 'success'], 200);
            } else {
                Log::warning('Flutterwave Payment Failed or Incomplete', [$payload]);
                return response()->json(['status' => 'failure'], 400);
            }
        } catch (Throwable $e) {
            Log::error("Flutterwave Webhook Error", [
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ]);
            return response()->json(['error' => 'Webhook processing failed'], 500);
        }
    }

    public function phonePeSuccessCallback(){
        ResponseService::successResponse("Payment done successfully.");
    }

    public function paypal()
        {
            try {
                \Log::info("PayPal Webhook event called");

                // ✅ Ensure POST
                if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') {
                    \Log::error("Invalid request method");
                    return response('Invalid request method', 400);
                }

                // ✅ Raw payload
                $content = trim(file_get_contents("php://input"));
                $jsonInput = json_decode($content, true);
                \Log::info("PayPal Webhook Raw Payload", [$jsonInput]);

                if (!$jsonInput || empty($jsonInput)) {
                    return response()->json(['error' => 'Invalid JSON'], 400);
                }

                $eventType = $jsonInput['event_type'] ?? null;
                $resource  = $jsonInput['resource'] ?? [];

                \Log::info("PayPal Event", [
                    'event' => $eventType,
                    'orderId' => $resource['id'] ?? null,
                    'status' => $resource['status'] ?? null,
                ]);
            $paymentConfiguration = PaymentConfiguration::where('payment_method', 'paypal')->first();
            $webhookId = $paymentConfiguration['webhook_url'] ?? url('/webhook/paypal');

            $paypal = new PayPalPayment($paymentConfiguration->api_key, $paymentConfiguration->secret_key, $paymentConfiguration->currency_code,$paymentConfiguration->payment_mode);


            // Get raw body for verification
            $rawBody = file_get_contents('php://input');

            // Extract headers properly (case-insensitive)
            $paypalHeaders = [
            'paypal-auth-algo'         => request()->header('paypal-auth-algo') ?: request()->header('PAYPAL-AUTH-ALGO'),
            'paypal-cert-url'          => request()->header('paypal-cert-url') ?: request()->header('PAYPAL-CERT-URL'),
            'paypal-transmission-id'   => request()->header('paypal-transmission-id') ?: request()->header('PAYPAL-TRANSMISSION-ID'),
            'paypal-transmission-sig'  => request()->header('paypal-transmission-sig') ?: request()->header('PAYPAL-TRANSMISSION-SIG'),
            'paypal-transmission-time' => request()->header('paypal-transmission-time') ?: request()->header('PAYPAL-TRANSMISSION-TIME'),
        ];

            // Verify webhook authenticity
            $isValid = $paypal->verifyWebhookSignature($paypalHeaders, $rawBody, $webhookId);

            if (!$isValid) {
                Log::warning('Invalid PayPal Webhook Signature');
                return response()->json(['status' => 'invalid'], 200); // respond 200 to prevent PayPal retries
            }


                // --- Step 1: Handle APPROVED orders (capture payment) ---
                if ($eventType === "CHECKOUT.ORDER.APPROVED") {
                    $orderId = $resource['id'];

                    // $paypal = app(PaypalPayment::class);
                    // $capture = $paypal->capturePayment($orderId);

                    Log::info("PayPal Order checkout approved");

                    // update your DB here
                    return response()->json(['status' => 'captured'], 200);
                }

                // --- Step 2: Handle final success event ---
               if ($eventType === "PAYMENT.CAPTURE.COMPLETED") {
                    $captureId = $resource['id'];
                    $amount    = $resource['amount']['value'] ?? null;
                    $currency  = $resource['amount']['currency_code'] ?? null;
                    $customId  = $resource['custom_id'] ?? null;

                    \Log::info("PayPal Payment Completed", [
                        'captureId' => $captureId,
                        'amount'    => $amount,
                        'currency'  => $currency,
                        'custom_id' => $customId,
                    ]);

                    if ($customId) {
                        // Extract transaction_id & package_id
                        $parts = explode('-', $customId);
                        $transaction_id = $parts[1] ?? null;
                        $package_id     = $parts[3] ?? null;

                        if ($transaction_id && $package_id) {
                            $paymentTransaction = PaymentTransaction::find($transaction_id);

                            if ($paymentTransaction) {
                                $metadata = [
                                    'payment_transaction_id' => $transaction_id,
                                    'package_id'             => $package_id,
                                    'user_id'                => $paymentTransaction->user_id,
                                ];

                                $response = $this->assignPackage(
                                    $metadata['payment_transaction_id'],
                                    $metadata['user_id'],
                                    $metadata['package_id']
                                );

                                if ($response['error']) {
                                    \Log::error("PayPal Webhook assignPackage error", [$response['message']]);
                                }
                            } else {
                                \Log::error("PayPal Webhook: PaymentTransaction not found", [
                                    'transaction_id' => $transaction_id
                                ]);
                            }
                        }
                    }

                    return response()->json(['status' => 'success'], 200);
                }

                // --- Step 3: Handle failed/refunded ---
                if (in_array($eventType, ["PAYMENT.CAPTURE.DENIED", "PAYMENT.CAPTURE.REFUNDED"])) {
                    \Log::warning("PayPal Payment Issue", $jsonInput);
                    return response()->json(['status' => 'failed'], 200);
                }

                return response()->json(['status' => 'ignored'], 200);

            } catch (\Throwable $e) {
                \Log::error("PayPal Webhook Error", [
                    'message' => $e->getMessage(),
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                ]);
                return response()->json(['error' => 'Webhook processing failed'], 500);
            }
        }

      private function handlePaypalCapture($orderId)
        {
            $payment = PaymentConfiguration::where([
                'payment_method' =>'paypal',
                'status' => 1
            ])->first();

            if (!$payment) {
                throw new \Exception("PayPal payment configuration not found.");
            }

            $paypal = new PayPalPayment(
                $payment->api_key,
                $payment->secret_key,
                $payment->currency_code,
                $payment->payment_mode
            );

            return $paypal->capturePayment($orderId);
        }

        public function paypalPaymentSuccess(Request $request)
        {
            try {
                \Log::info("PayPal Success Raw Payload", [$request->all()]);

                $orderId = $request->query('token');

                if (!$orderId) {
                    return view('payment.paypal', [
                        'trxref'    => null,
                        'reference' => null
                    ]);
                }

                $paymentResult = $this->handlePaypalCapture($orderId);

                \Log::info("PayPal Success Redirect Capture", (array) $paymentResult);

                return view('payment.paypal', [
                    'trxref'    => $orderId,
                    'reference' => $paymentResult['id'] ?? null
                ]);

            } catch (\Throwable $e) {
                \Log::error("PayPal Success Handler Error", [
                    'message' => $e->getMessage(),
                    'file'    => $e->getFile(),
                    'line'    => $e->getLine(),
                ]);

                return view('payment.paypal', [
                    'trxref'    => null,
                    'reference' => null
                ]);
            }
        }

        public function paypalSuccessCallback(Request $request)
        {
            try {
                \Log::info("PayPal Success callback for app");
                  \Log::info("PayPal Success Raw Payload for app", [$request->all()]);
                 $orderId = $request->query('token');
                if (!$orderId) {
                    return ResponseService::errorResponse("Missing PayPal order ID.");
                }

                $paymentResult = $this->handlePaypalCapture($orderId);
                \Log::info("PayPal Success Redirect Capture For App", (array) $paymentResult);
                return ResponseService::successResponse("Payment done successfully.");

            } catch (\Throwable $e) {
                \Log::error("PayPal Success Callback Error", [
                    'message' => $e->getMessage(),
                    'file'    => $e->getFile(),
                    'line'    => $e->getLine(),
                ]);

                return ResponseService::errorResponse("Something went wrong during PayPal payment.");
            }
        }

        public function paypalCancelCallback()
        {
              return ResponseService::successResponse("Payment Cancelled successfully.");
        }
        public function paypalCancelCallbackWeb(Request $request)
        {
            try {
                $orderId = $request->query('token');
                return view('payment.paypal', [
                    'trxref'    => $orderId ?? null,
                    'reference' => null
                ]);

            } catch (\Throwable $e) {
                \Log::error("PayPal Success Handler Error", [
                    'message' => $e->getMessage(),
                    'file'    => $e->getFile(),
                    'line'    => $e->getLine(),
                ]);

                return view('payment.paypalcancle', [
                    'trxref'    => null,
                    'reference' => null
                ]);
            }
        }
        /**
         * Success Business Login
         * @param $payment_transaction_id
         * @param $user_id
         * @param $package_id
         * @return array
         */
       private function assignPackage($payment_transaction_id, $user_id, $package_id) {
            try {
        $paymentTransactionData = PaymentTransaction::where('id', $payment_transaction_id)->first();
        if ($paymentTransactionData == null) {
            Log::error("Payment Transaction id not found");
            return [
                'error'   => true,
                'message' => 'Payment Transaction id not found'
            ];
        }

        if ($paymentTransactionData->status == "succeed") {
            Log::info("Transaction Already Succeed");
            return [
                'error'   => true,
                'message' => 'Transaction Already Succeed'
            ];
        }

        DB::beginTransaction();
        $paymentTransactionData->update(['payment_status' => "succeed"]);

        $package = Package::find($package_id);

        if (!empty($package)) {
            // create purchased package record
            $userPackage = UserPurchasedPackage::create([
                'package_id'  => $package_id,
                'user_id'     => $user_id,
                'start_date'  => Carbon::now(),
                'end_date'    => $package->duration == "unlimited" ? null : Carbon::now()->addDays($package->duration),
                'total_limit' => $package->item_limit == "unlimited" ? null : $package->item_limit,
                'listing_duration_type' => $package->listing_duration_type,
                'listing_duration_days' => $package->listing_duration_days
            ]);
        }

        $title = "Package Purchased";
        $body = 'Amount :- ' . $paymentTransactionData->amount;
        $userTokens = UserFcmToken::where('user_id', $user_id)->pluck('fcm_token')->toArray();
        if (!empty($userTokens)) {
            NotificationService::sendFcmNotification($userTokens, $title, $body, 'payment');
        }
        DB::commit();

        return [
            'error'   => false,
            'message' => 'Transaction Verified Successfully'
        ];

        } catch (Throwable $th) {
        DB::rollBack();
        Log::error($th->getMessage() . "WebhookController -> assignPackage");
        return [
            'error'   => true,
            'message' => 'Error Occurred'
        ];
        }
    }

    public function flutterWaveSuccessCallback(){
        ResponseService::successResponse("Payment done successfully.");
    }
    /**
     * Failed Business Logic
     * @param $payment_transaction_id
     * @param $user_id
     * @return array
     */
    private function failedTransaction($payment_transaction_id, $user_id) {
        try {
            $paymentTransactionData = PaymentTransaction::find($payment_transaction_id);
            if (!$paymentTransactionData) {
                return [
                    'error'   => true,
                    'message' => 'Payment Transaction id not found'
                ];
            }

            $paymentTransactionData->update(['payment_status' => "failed"]);

            $body = 'Amount :- ' . $paymentTransactionData->amount;
            $userTokens = UserFcmToken::where('user_id', $user_id)->pluck('fcm_token')->toArray();
            NotificationService::sendFcmNotification($userTokens, 'Package Payment Failed', $body, 'payment');
            return [
                'error'   => false,
                'message' => 'Transaction Verified Successfully'
            ];
        } catch (Throwable $th) {
            DB::rollBack();
            Log::error($th->getMessage() . "WebhookController -> failedTransaction");
            return [
                'error'   => true,
                'message' => 'Error Occurred'
            ];
        }
    }
}

