<?php

namespace Modules\PaynowGateway\Services;

use App\Models\SubscriptionPlan;
use App\Models\User;
use Illuminate\Support\Facades\Log;
use Paynow\Payments\Paynow; // Import the SDK class
use Exception; // Import base Exception

class PaynowService
{
    protected string $integrationId;
    protected string $integrationKey;
    protected string $mode;
    protected Paynow $paynow;

    public function __construct()
    {
        if (!function_exists('setting')) {
            throw new Exception("Settings helper function not found or Paynow settings are not configured.");
        }
        $this->integrationId = setting('paynow_integration_id');
        $this->integrationKey = setting('paynow_integration_key');
        $this->mode = setting('paynow_mode', 'test');

        if (empty($this->integrationId) || empty($this->integrationKey)) {
            Log::error('PaynowService: Integration ID or Key is not configured.');
            throw new Exception('Paynow Service is not configured. Missing Integration ID or Key.');
        }

        // The resultURL is where Paynow will POST to your server (webhook)
        $resultUrl = route('paynowgateway.webhook');
        // The returnURL is where the user is redirected in their browser after payment attempt
        // This will often be overridden per transaction type (subscription vs wallet)
        $defaultReturnUrl = route('dashboard');

        $this->paynow = new Paynow(
            $this->integrationId,
            $this->integrationKey,
            $resultUrl,
            $defaultReturnUrl
        );

        // Note: Paynow SDK doesn't have an explicit "test mode" switch.
        // Test vs Live is typically determined by the Integration ID/Key used.
        // Ensure your 'test' credentials from Paynow are used when 'paynow_mode' is 'test'.
    }

    public function initiateSubscriptionPayment(User $user, SubscriptionPlan $plan, int $localSubscriptionId, string $subscriptionReference)
    {
        $amount = (float)number_format($plan->price, 2, '.', '');
        $payment = $this->paynow->createPayment($subscriptionReference, $user->email);
        $payment->add("Subscription to {$plan->name} for {$plan->interval_count} {$plan->interval}(s)", $amount);

        // Set the specific return URL for subscription callbacks
        $this->paynow->setReturnUrl(route('subscription.paynow.callback', [
            'subscription_id' => $localSubscriptionId, // Pass your local subscription ID
            'ref' => $subscriptionReference          // Pass your internal reference
        ]));

        try {
            $response = $this->paynow->send($payment);

            if ($response->success()) {
                return [
                    'status' => 'success',
                    'redirect_url' => $response->redirectUrl(),
                    'poll_url' => $response->pollUrl(), // IMPORTANT: Store this URL to check transaction status later
                    'transaction_reference' => $subscriptionReference, // Your internal reference
                    'paynow_reference' => null, // Paynow's reference might not be available from InitResponse; get from StatusResponse later
                ];
            } else {
                $errorData = $response->errors(); // This might be an array or a string
                $errorMessage = 'Paynow SDK call failed for subscription payment.'; // Default message

                if (is_array($errorData) && !empty($errorData)) {
                    $errorMessage = implode(', ', $errorData);
                } elseif (is_string($errorData) && !empty($errorData)) {
                    $errorMessage = $errorData; // Use the string directly
                }

                Log::error("PaynowService: Failed to initiate subscription payment via SDK.", [
                    'reference' => $subscriptionReference, 'user_id' => $user->id, 'plan_id' => $plan->id,
                    'sdk_error_data' => $errorData // Log the raw data from errors()
                ]);
                throw new Exception("Could not initiate Paynow subscription payment: " . $errorMessage);
            }
        } catch (Exception $e) {
            Log::error("PaynowService SDK Exception during subscription initiation: " . $e->getMessage(), [
                'reference' => $subscriptionReference, 'user_id' => $user->id, 'exception_type' => get_class($e)
            ]);
            throw $e; // Re-throw the exception to be caught by the controller
        }
    }

    public function initiateOneTimePayment(User $user, float $amount, string $currency, string $description, string $transactionReference)
    {
        // Paynow typically works with the merchant's default currency (ZWL or USD).
        // Ensure the $amount is in the currency expected by your Paynow account configuration.
        // The $currency parameter here is for your internal tracking if needed, but Paynow might ignore it.
        $formattedAmount = (float)number_format($amount, 2, '.', '');
        $payment = $this->paynow->createPayment($transactionReference, $user->email);
        $payment->add($description, $formattedAmount);

        // Determine the correct return URL based on the description or reference prefix
        if (Str::startsWith($transactionReference, 'CRD-')) {
            $this->paynow->setReturnUrl(route('credits.paynow.purchaseCallback', ['ref' => $transactionReference]));
        } elseif (Str::startsWith($transactionReference, 'WLT-')) {
            $this->paynow->setReturnUrl(route('wallet.paynow.depositCallback', ['ref' => $transactionReference]));
        } // Add more conditions if this method is used for other payment types

        try {
            $response = $this->paynow->send($payment);

            if ($response->success()) {
                return [
                    'status' => 'success',
                    'redirect_url' => $response->redirectUrl(),
                    'poll_url' => $response->pollUrl(), // IMPORTANT: Store this
                    'transaction_reference' => $transactionReference,
                    'paynow_reference' => null, // Paynow's reference might not be available from InitResponse; get from StatusResponse later
                ];
            } else {
                $errorData = $response->errors(); // This might be an array or a string
                $errorMessage = 'Paynow SDK call failed for one-time payment.'; // Default message

                if (is_array($errorData) && !empty($errorData)) {
                    $errorMessage = implode(', ', $errorData);
                } elseif (is_string($errorData) && !empty($errorData)) {
                    $errorMessage = $errorData; // Use the string directly
                }

                Log::error("PaynowService: Failed to initiate one-time payment via SDK.", [
                    'reference' => $transactionReference, 'user_id' => $user->id, 'amount' => $formattedAmount,
                    'sdk_error_data' => $errorData, // Log the raw data from errors()
                ]);
                throw new Exception("Could not initiate Paynow deposit: " . $errorMessage);
            }
        } catch (Exception $e) {
            Log::error("PaynowService SDK Exception during one-time payment initiation: " . $e->getMessage(), [
                'reference' => $transactionReference, 'user_id' => $user->id, 'exception_type' => get_class($e)
            ]);
            throw $e;
        }
    }

    public function verifyPaymentStatus(string $pollUrl)
    {
        if (empty($pollUrl)) {
            Log::warning('PaynowService: verifyPaymentStatus called with empty pollUrl.');
            return ['status' => 'error', 'message' => 'Poll URL is missing for verification.', 'paid' => false];
        }

        try {
            // The Paynow SDK instance ($this->paynow) is already configured with credentials.
            $statusResponse = $this->paynow->pollTransaction($pollUrl);
            Log::info('Paynow Poll Transaction Response:', (array)$statusResponse); // Cast to array for better logging

            // Structure the response for consistency in your application
            return [
                'status' => $statusResponse->status(), // e.g., "Paid", "Cancelled", "Awaiting Delivery", etc.
                'paid' => $statusResponse->paid(),     // boolean: true if the transaction was paid
                'amount' => $statusResponse->amount(),
                'paynow_reference' => $statusResponse->paynowReference(),
                'external_reference' => $statusResponse->reference(), // This is your $transactionReference
            ];
        } catch (Exception $e) {
            Log::error("PaynowService SDK Exception during pollTransaction: " . $e->getMessage(), ['poll_url' => $pollUrl]);
            return ['status' => 'error', 'message' => 'Failed to verify payment status with Paynow: ' . $e->getMessage(), 'paid' => false];
        }
    }

    public function processWebhook(array $webhookData): array
    {
        Log::info('Paynow Webhook Data Received for Processing:', $webhookData);

        // CRITICAL: Implement Webhook Hash Verification
        // Paynow's documentation should specify how the 'hash' field in the POST data is generated.
        // It typically involves:
        // 1. Concatenating specific fields from the POST data in a PRECISE order.
        // 2. Appending your Integration Key to this string.
        // 3. Calculating the SHA512 hash of the resulting string.
        // 4. Comparing this generated hash with the 'hash' value received in the POST data.

        // Example (PSEUDOCODE - YOU MUST GET THE EXACT FIELDS AND ORDER FROM PAYNOW DOCS):
        // $stringToHash = "";
        // $fieldsInOrder = ['reference', 'paynowreference', 'amount', 'status', /* ... other fields ... */];
        // foreach ($fieldsInOrder as $field) {
        //     if (isset($webhookData[$field])) {
        //         $stringToHash .= $webhookData[$field];
        //     }
        // }
        // $stringToHash .= $this->integrationKey;
        // $generatedHash = strtoupper(hash('sha512', $stringToHash));

        // if (!isset($webhookData['hash']) || !hash_equals($generatedHash, strtoupper($webhookData['hash']))) {
        //     Log::warning('Paynow Webhook: Invalid hash.', [
        //         'received_hash' => $webhookData['hash'] ?? 'NOT_SET',
        //         'generated_hash' => $generatedHash,
        //         'data_used_for_hash' => $stringToHash // Log this carefully for debugging
        //     ]);
        //     return ['verified' => false, 'status' => 'error', 'message' => 'Invalid webhook hash.'];
        // }

        Log::warning('Paynow Webhook: Hash verification is CURRENTLY SKIPPED. This MUST BE IMPLEMENTED for security before going live.');

        $status = strtolower($webhookData['status'] ?? 'unknown'); // Normalize status to lowercase

        return [
            'verified' => true, // CHANGE TO true ONLY AFTER IMPLEMENTING AND TESTING HASH VERIFICATION
            'status' => $status,
            // Paynow status 'paid' means successful. Other statuses like 'delivered', 'awaiting_delivery' might also be relevant.
            'paid' => in_array($status, ['paid', 'delivered']),
            'paynow_reference' => $webhookData['paynowreference'] ?? null,
            'external_reference' => $webhookData['reference'] ?? null, // Your $transactionReference
            'amount' => $webhookData['amount'] ?? null,
            'poll_url' => $webhookData['pollurl'] ?? null,
            'raw_data' => $webhookData, // For further processing or logging
        ];
    }
}
