<?php
namespace Modules\PaypalGateway\Services;

use App\Models\Subscription;
use App\Models\SubscriptionPlan;
use App\Models\User;
use Illuminate\Support\Facades\Log;
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Core\ProductionEnvironment;
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
// Subscription-specific request classes might not be in the installed paypal-checkout-sdk version
// We will construct these requests manually if needed.
// use PayPalCheckoutSdk\Subscriptions\SubscriptionsCreateRequest;
// use PayPalCheckoutSdk\Subscriptions\SubscriptionsGetRequest;
// use PayPalCheckoutSdk\Subscriptions\SubscriptionsCancelRequest;

use PayPalHttp\HttpException;
use PayPalHttp\HttpRequest; // For manually creating requests

class PaypalService
{
    protected $client;
    protected $clientId;
    protected $clientSecret;
    protected $mode;
    protected $webhookId;

    public function __construct()
    {
        if (!function_exists('setting')) {
            throw new \Exception("Settings helper function not found. Ensure it's globally available.");
        }

        $this->clientId = setting('paypal_client_id');
        $this->clientSecret = setting('paypal_client_secret');
        $this->mode = setting('paypal_mode', 'sandbox');
        $this->webhookId = setting('paypal_webhook_id');

        if ($this->clientId && $this->clientSecret) {
            Log::info("PayPalService initialized with Mode: {$this->mode}");
            $environment = ($this->mode === 'live')
                ? new ProductionEnvironment($this->clientId, $this->clientSecret)
                : new SandboxEnvironment($this->clientId, $this->clientSecret);
            $this->client = new PayPalHttpClient($environment);
        } else {
            Log::error('PayPalService not fully configured. Client ID or Secret might be missing.');
        }
    }

    protected function ensureClientIsAvailable()
    {
        if (!$this->client) {
            Log::error('PayPal client is not configured. Client ID or Secret might be missing.');
            throw new \Exception('PayPal client is not configured. Please set credentials in admin settings.');
        }
    }

    public function isConfigured(): bool
    {
        return !empty($this->clientId) && !empty($this->clientSecret) && $this->client !== null;
    }

    /**
     * Create a PayPal subscription.
     *
     * @param User $user
     * @param SubscriptionPlan $localPlan
     * @return string|null The approval link or null on failure
     * @throws \Exception
     */
    public function createPaypalSubscription(User $user, SubscriptionPlan $localPlan): ?string
    {
        $this->ensureClientIsAvailable();

        if (empty($localPlan->paypal_plan_id)) {
            Log::error("PayPal Plan ID is missing for local plan ID: {$localPlan->id} ({$localPlan->name})");
            throw new \Exception("PayPal Plan ID is not configured for the '{$localPlan->name}' subscription plan.");
        }

        // Manually construct the request for creating a subscription
        // Endpoint: /v1/billing/subscriptions
        $request = new HttpRequest("/v1/billing/subscriptions", "POST");
        $request->headers["Content-Type"] = "application/json";
        $request->headers["Prefer"] = "return=representation"; // To get the full resource representation back

        // Set the request body
        $request->body = [
            'plan_id' => $localPlan->paypal_plan_id,
            'subscriber' => [
                'name' => [
                    // More robust name splitting if first_name/last_name are not separate attributes
                    'given_name' => $user->first_name ?? (explode(' ', $user->name, 2)[0] ?? $user->name),
                    'surname' => $user->last_name ?? (explode(' ', $user->name, 2)[1] ?? ''), // Use empty string if no surname part
                ],
                'email_address' => $user->email,
            ],
            'application_context' => [
                'brand_name' => config('app.name'),
                'locale' => 'en-US',
                'shipping_preference' => 'NO_SHIPPING',
                'user_action' => 'SUBSCRIBE_NOW',
                'return_url' => route('subscription.paypal.execute.subscription'),
                'cancel_url' => route('subscription.paypal.cancel.subscription.page'),
            ],
        ];

        try {
            $response = $this->client->execute($request);
            if ($response->statusCode == 201 && isset($response->result->id)) { // 201 Created
                foreach ($response->result->links as $link) {
                    if ($link->rel === 'approve') {
                        session(['paypal_subscription_id_pending' => $response->result->id]);
                        session(['local_plan_id_pending' => $localPlan->id]); // Optional: if needed on return
                        return $link->href;
                    }
                }
            }
            Log::error("PayPal Subscription Create: No approval link found or unexpected status.", (array)$response);
        } catch (HttpException $e) {
            // HttpException message is often a JSON string with details
            $errorMessage = $e->getMessage();
            $errorDetails = json_decode($errorMessage, true);
            $paypalDebugId = $e->paypalDebugId ?? ($errorDetails['debug_id'] ?? 'N/A'); // Some SDK versions might add paypalDebugId property

            Log::error("PayPal Subscription Create Error - Debug ID: {$paypalDebugId}", [
                'message' => $errorDetails['message'] ?? $errorMessage,
                'details' => $errorDetails['details'] ?? $errorDetails ?? $e->getMessage(),
                'statusCode' => $e->statusCode,
                'raw_exception_message' => $e->getMessage()
            ]);
            throw new \Exception("PayPal API Error during subscription creation: " . ($errorDetails['message'] ?? $e->getMessage()));
        }
        return null;
    }

    /**
     * Get details of a PayPal subscription.
     *
     * @param string $paypalSubscriptionId
     * @return \stdClass|null
     * @throws \Exception
     */
    public function getPaypalSubscriptionDetails(string $paypalSubscriptionId): ?\stdClass
    {
        $this->ensureClientIsAvailable();
        // Manually construct the request for getting subscription details
        // Endpoint: /v1/billing/subscriptions/{id}
        $request = new HttpRequest("/v1/billing/subscriptions/" . $paypalSubscriptionId, "GET");
        $request->headers["Content-Type"] = "application/json";

        // $request = new SubscriptionsGetRequest($paypalSubscriptionId); // If the class existed

        try {
            $response = $this->client->execute($request);
            return $response->result;
        } catch (HttpException $e) {
            $errorMessage = $e->getMessage();
            $errorDetails = json_decode($errorMessage, true);
            $paypalDebugId = $e->paypalDebugId ?? ($errorDetails['debug_id'] ?? 'N/A');

            Log::error("PayPal Get Subscription Details Error for ID {$paypalSubscriptionId} - Debug ID: {$paypalDebugId}", [
                'message' => $errorDetails['message'] ?? $errorMessage,
                'details' => $errorDetails['details'] ?? $errorDetails ?? $e->getMessage(),
                'statusCode' => $e->statusCode,
                'raw_exception_message' => $e->getMessage()
            ]);
            return null;
        }
    }

    /**
     * Cancel a PayPal subscription at the gateway.
     *
     * @param Subscription $localSubscription
     * @return bool
     * @throws \Exception
     */
    public function cancelSubscriptionAtGateway(Subscription $localSubscription): bool
    {
        $this->ensureClientIsAvailable();
        if (!$localSubscription->gateway_subscription_id) {
            Log::error("Cannot cancel PayPal subscription for local subscription ID {$localSubscription->id}: missing gateway_subscription_id.");
            return false;
        }

        // Manually construct the request for cancelling a subscription
        // Endpoint: /v1/billing/subscriptions/{id}/cancel
        $request = new HttpRequest("/v1/billing/subscriptions/" . $localSubscription->gateway_subscription_id . "/cancel", "POST");
        $request->headers["Content-Type"] = "application/json";
        $request->body = ['reason' => 'Subscription cancelled by user from application.'];

        // $request = new SubscriptionsCancelRequest($localSubscription->gateway_subscription_id); // If the class existed

        try {
            $response = $this->client->execute($request);
            // PayPal returns 204 No Content on success
            if ($response->statusCode == 204) {
                Log::info("PayPal subscription {$localSubscription->gateway_subscription_id} cancelled successfully at gateway.");
                return true;
            }
            Log::warning("PayPal subscription cancellation for {$localSubscription->gateway_subscription_id} returned status: {$response->statusCode}");
            return false;
        } catch (HttpException $e) {
            $errorMessage = $e->getMessage();
            $errorDetails = json_decode($errorMessage, true);
            $paypalDebugId = $e->paypalDebugId ?? ($errorDetails['debug_id'] ?? 'N/A');

            Log::error("Error cancelling PayPal subscription {$localSubscription->gateway_subscription_id} - Debug ID: {$paypalDebugId}", [
                'message' => $errorDetails['message'] ?? $errorMessage,
                'details' => $errorDetails['details'] ?? $errorDetails ?? $e->getMessage(),
                'statusCode' => $e->statusCode,
                'raw_exception_message' => $e->getMessage()
            ]);
            return false;
        }
    }

    /**
     * Create a PayPal order for a one-time wallet deposit.
     *
     * @param \App\Models\User $user
     * @param float $amount
     * @param string $currency
     * @return \PayPalHttp\HttpResponse|null
     * @throws \PayPalHttp\IOException
     * @throws \Exception
     */
    public function createWalletDepositOrder(\App\Models\User $user, float $amount, string $currency = 'USD')
    {
        $this->ensureClientIsAvailable();

        $request = new OrdersCreateRequest();
        $request->prefer('return=representation');
        $request->body = [
            'intent' => 'CAPTURE',
            'application_context' => [
                'return_url' => route('wallet.paypal.depositCallback'),
                'cancel_url' => route('wallet.paypal.depositCancel'),
                'brand_name' => config('app.name'),
                'user_action' => 'PAY_NOW',
            ],
            'purchase_units' => [[
                'description' => 'Wallet Deposit for ' . config('app.name'),
                'amount' => [
                    'currency_code' => strtoupper($currency),
                    'value' => number_format($amount, 2, '.', ''),
                ],
                'custom_id' => 'DEPOSIT-' . $user->id . '-' . time(), // Custom ID for tracking
            ]],
        ];

        try {
            return $this->client->execute($request);
        } catch (HttpException $e) {
            $errorMessage = $e->getMessage();
            $errorDetails = json_decode($errorMessage, true);
            $paypalDebugId = $e->paypalDebugId ?? ($errorDetails['debug_id'] ?? 'N/A');

            Log::error('PayPal Create Wallet Deposit Order Error - Debug ID: ' . $paypalDebugId, [
                'message' => $errorDetails['message'] ?? $errorMessage,
                'details' => $errorDetails['details'] ?? $errorDetails ?? $e->getMessage(),
                'statusCode' => $e->statusCode,
                'raw_exception_message' => $e->getMessage(),
                'attempted_amount' => $amount,
                'attempted_currency' => $currency,
                'user_id' => $user->id,
            ]);
            throw $e;
        }
    }

    /**
     * Capture a PayPal order for a wallet deposit.
     *
     * @param string $paypalOrderId
     * @return \PayPalHttp\HttpResponse|null
     * @throws \PayPalHttp\IOException
     * @throws \Exception
     */
    public function captureWalletDepositOrder(string $paypalOrderId)
    {
        $this->ensureClientIsAvailable();
        $request = new OrdersCaptureRequest($paypalOrderId);
        $request->prefer('return=representation');
        
        try {
            return $this->client->execute($request);
        } catch (HttpException $e) {
            $errorMessage = $e->getMessage();
            $errorDetails = json_decode($errorMessage, true);
            $paypalDebugId = $e->paypalDebugId ?? ($errorDetails['debug_id'] ?? 'N/A');

            Log::error('PayPal Capture Wallet Deposit Order Error for Order ID ' . $paypalOrderId . ' - Debug ID: ' . $paypalDebugId, [
                'message' => $errorDetails['message'] ?? $errorMessage,
                'details' => $errorDetails['details'] ?? $errorDetails ?? $e->getMessage(),
                'statusCode' => $e->statusCode,
                'raw_exception_message' => $e->getMessage()
            ]);
            throw $e;
        }
    }

    /**
     * Verify PayPal Webhook Signature.
     *
     * @param array $headers All request headers
     * @param string $rawBody The raw request body
     * @return bool
     * @throws \Exception
     */
    public function verifyWebhookSignature(array $headers, string $rawBody): bool
    {
        $this->ensureClientIsAvailable();
        if (!$this->webhookId) {
            Log::critical('PayPal Webhook ID is not configured. Cannot verify signature. This is a security risk.');
            return false;
        }

        // Extract necessary headers. Ensure they are lowercase as Laravel might convert them.
        $transmissionId = $headers['paypal-transmission-id'][0] ?? null;
        $transmissionTime = $headers['paypal-transmission-time'][0] ?? null;
        $certUrl = $headers['paypal-cert-url'][0] ?? null;
        $authAlgo = $headers['paypal-auth-algo'][0] ?? null;
        $transmissionSig = $headers['paypal-transmission-sig'][0] ?? null;

        if (!$transmissionId || !$transmissionTime || !$certUrl || !$authAlgo || !$transmissionSig) {
            Log::warning('PayPal Webhook: Missing required headers for signature verification.', $headers);
            return false;
        }

        // IMPORTANT: The PayPal PHP SDK v2 does not include a helper method for webhook signature verification.
        // You MUST implement this by making an API call to PayPal's /v1/notifications/verify-webhook-signature endpoint.
        // Refer to PayPal documentation: https://developer.paypal.com/docs/api/webhooks/v1/#verify-webhook-signature_post

        $payload = [
            'auth_algo' => $authAlgo,
            'cert_url' => $certUrl,
            'transmission_id' => $transmissionId,
            'transmission_sig' => $transmissionSig,
            'transmission_time' => $transmissionTime,
            'webhook_id' => $this->webhookId,
            'webhook_event' => json_decode($rawBody) // PayPal expects the event body as a JSON object
        ];

        $request = new HttpRequest("/v1/notifications/verify-webhook-signature", "POST");
        $request->headers["Content-Type"] = "application/json";
        // The PayPalHttpClient's execute method should automatically add the Authorization header
        // with a valid OAuth2 token.
        $request->body = $payload;

        try {
            Log::debug("PayPal Webhook: Attempting to verify signature with payload:", $payload);
            $response = $this->client->execute($request);
            Log::debug("PayPal Webhook: Verification API response status: " . $response->statusCode);
            // Log::debug("PayPal Webhook: Verification API response body: ", (array)$response->result); // Can be verbose

            if ($response->statusCode == 200 && isset($response->result->verification_status) && $response->result->verification_status === 'SUCCESS') {
                Log::info('PayPal Webhook: Signature VERIFIED successfully.');
                return true;
            }
            Log::warning('PayPal Webhook: Signature verification FAILED or status not SUCCESS.', [
                'status_code' => $response->statusCode,
                'response_body' => (array)$response->result
            ]);
            return false;
        } catch (HttpException $e) {
            $errorMessage = $e->getMessage();
            $errorDetails = json_decode($errorMessage, true);
            $paypalDebugId = $e->paypalDebugId ?? ($errorDetails['debug_id'] ?? 'N/A');

            Log::error("PayPal Webhook: HttpException during signature verification API call - Debug ID: {$paypalDebugId}", [
                'message' => $errorDetails['message'] ?? $errorMessage,
                'details' => $errorDetails['details'] ?? $errorDetails ?? $e->getMessage(),
                'statusCode' => $e->statusCode,
                'raw_exception_message' => $e->getMessage()
            ]);
            return false;
        } catch (\Exception $e) {
            Log::error('PayPal Webhook: General Exception during signature verification API call.', ['error' => $e->getMessage()]);
            return false;
        }
    }
}
