<?php
namespace App\Core;

use RuntimeException;

class PaymentGateway {
    public static function methods(): array {
        return [
            'sslcommerz' => ['name'=>'SSLCommerz','online'=>true,'implemented'=>true],
            'bkash' => ['name'=>'bKash','online'=>true,'implemented'=>false],
            'nagad' => ['name'=>'Nagad','online'=>true,'implemented'=>false],
            'wallet' => ['name'=>'Customer Wallet','online'=>false,'implemented'=>true],
            'cod' => ['name'=>'Cash on Delivery','online'=>false,'implemented'=>true],
            'partial' => ['name'=>'Partial Payment','online'=>true,'implemented'=>false],
        ];
    }

    public static function callbackUrl(string $gateway, string $result='ipn'): string {
        return \url('api/payment-callback.php?gateway=' . rawurlencode($gateway) . '&result=' . rawurlencode($result));
    }

    public static function orderForPayment(int $orderId): array {
        if ($orderId < 1) throw new RuntimeException('Invalid order.');
        $order = Database::fetchOne('SELECT * FROM orders WHERE id=? LIMIT 1', [$orderId]);
        if (!$order) throw new RuntimeException('Order not found.');
        $role = (string)(Auth::role() ?? '');
        $authorized = in_array($role, ['admin','staff'], true)
            || ((int)($order['customer_id'] ?? 0) > 0 && (int)$order['customer_id'] === (int)Auth::id())
            || ((int)($_SESSION['last_order_id'] ?? 0) === $orderId);
        if (!$authorized) throw new RuntimeException('You are not authorized to pay this order.');
        if ((string)$order['payment_status'] === 'paid') throw new RuntimeException('This order is already paid.');
        return $order;
    }

    public static function initiate(int $orderId, string $gateway): array {
        $order = self::orderForPayment($orderId);
        if ((string)$order['payment_method'] !== $gateway) throw new RuntimeException('The selected gateway does not match this order.');
        if ($gateway === 'sslcommerz') return self::initiateSslCommerz($order);
        throw new RuntimeException('This payment gateway requires merchant credentials and a provider-approved integration. It is disabled until configured.');
    }

    private static function initiateSslCommerz(array $order): array {
        if (SSLCOMMERZ_STORE_ID === '' || SSLCOMMERZ_STORE_PASSWORD === '') {
            throw new RuntimeException('SSLCommerz credentials are not configured. Add SSLCOMMERZ_STORE_ID and SSLCOMMERZ_STORE_PASSWORD.');
        }
        if (!function_exists('curl_init')) throw new RuntimeException('The PHP cURL extension is required for SSLCommerz.');
        $transactionId = 'PH' . (int)$order['id'] . strtoupper(substr(bin2hex(random_bytes(5)), 0, 10));
        $endpoint = PAYMENT_SANDBOX
            ? 'https://sandbox.sslcommerz.com/gwprocess/v4/api.php'
            : 'https://securepay.sslcommerz.com/gwprocess/v4/api.php';
        $post = [
            'store_id'=>SSLCOMMERZ_STORE_ID,
            'store_passwd'=>SSLCOMMERZ_STORE_PASSWORD,
            'total_amount'=>number_format((float)$order['due_amount'] ?: (float)$order['total_amount'], 2, '.', ''),
            'currency'=>'BDT',
            'tran_id'=>$transactionId,
            'success_url'=>self::callbackUrl('sslcommerz','success'),
            'fail_url'=>self::callbackUrl('sslcommerz','failed'),
            'cancel_url'=>self::callbackUrl('sslcommerz','cancelled'),
            'ipn_url'=>self::callbackUrl('sslcommerz','ipn'),
            'cus_name'=>(string)$order['customer_name'],
            'cus_email'=>(string)($order['customer_email'] ?: 'customer@example.com'),
            'cus_add1'=>(string)$order['delivery_address'],
            'cus_city'=>'Dhaka',
            'cus_country'=>'Bangladesh',
            'cus_phone'=>(string)$order['customer_phone'],
            'shipping_method'=>'YES',
            'ship_name'=>(string)$order['customer_name'],
            'ship_add1'=>(string)$order['delivery_address'],
            'ship_city'=>'Dhaka',
            'ship_country'=>'Bangladesh',
            'product_name'=>'Order ' . (string)$order['order_no'],
            'product_category'=>'Marketplace',
            'product_profile'=>'general',
            'value_a'=>(string)$order['id'],
        ];
        $ch = curl_init($endpoint);
        curl_setopt_array($ch, [CURLOPT_POST=>true,CURLOPT_POSTFIELDS=>$post,CURLOPT_RETURNTRANSFER=>true,CURLOPT_CONNECTTIMEOUT=>15,CURLOPT_TIMEOUT=>35,CURLOPT_SSL_VERIFYPEER=>true,CURLOPT_SSL_VERIFYHOST=>2]);
        $body = curl_exec($ch);
        $error = curl_error($ch);
        $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        $response = is_string($body) ? json_decode($body, true) : null;
        Database::safeExecute('INSERT INTO payment_logs (gateway,payload,created_at) VALUES (?,?,NOW())', ['sslcommerz', json_encode(['stage'=>'initiate','order_id'=>$order['id'],'http'=>$http,'response'=>$response,'error'=>$error], JSON_UNESCAPED_SLASHES)]);
        if ($http !== 200 || !is_array($response) || empty($response['GatewayPageURL']) || (($response['status'] ?? '') !== 'SUCCESS')) {
            throw new RuntimeException('SSLCommerz could not create a payment session. ' . ($response['failedreason'] ?? $error ?: 'Unknown gateway response.'));
        }
        Database::execute('INSERT INTO payments (order_id,gateway,transaction_id,amount,status,raw_response,created_at) VALUES (?,?,?,?,"pending",?,NOW())', [$order['id'],'sslcommerz',$transactionId,(float)$post['total_amount'],json_encode($response,JSON_UNESCAPED_SLASHES)]);
        return ['redirect_url'=>(string)$response['GatewayPageURL'],'transaction_id'=>$transactionId];
    }

    public static function handleSslCommerzCallback(array $payload, string $result): array {
        Database::safeExecute('INSERT INTO payment_logs (gateway,payload,created_at) VALUES (?,?,NOW())', ['sslcommerz', json_encode(['result'=>$result,'payload'=>$payload], JSON_UNESCAPED_SLASHES)]);
        $transactionId = trim((string)($payload['tran_id'] ?? ''));
        if ($transactionId === '') return ['ok'=>false,'message'=>'Missing transaction ID.'];
        $payment = Database::safeFetchOne('SELECT p.*,o.order_no,o.total_amount,o.due_amount,o.payment_status FROM payments p JOIN orders o ON o.id=p.order_id WHERE p.gateway="sslcommerz" AND p.transaction_id=? ORDER BY p.id DESC LIMIT 1', [$transactionId]);
        if (!$payment) return ['ok'=>false,'message'=>'Unknown payment transaction.'];
        if ((string)$payment['payment_status'] === 'paid' || (string)$payment['status'] === 'paid') return ['ok'=>true,'order_id'=>(int)$payment['order_id'],'message'=>'Payment was already confirmed.'];

        if (in_array($result, ['failed','cancelled'], true)) {
            Database::safeExecute('UPDATE payments SET status=?,raw_response=? WHERE id=?', [$result,json_encode($payload,JSON_UNESCAPED_SLASHES),$payment['id']]);
            return ['ok'=>false,'order_id'=>(int)$payment['order_id'],'message'=>'Payment was ' . $result . '.'];
        }
        $valId = trim((string)($payload['val_id'] ?? ''));
        if ($valId === '') return ['ok'=>false,'order_id'=>(int)$payment['order_id'],'message'=>'Payment validation ID is missing.'];
        $validation = self::validateSslCommerz($valId);
        $validStatus = in_array((string)($validation['status'] ?? ''), ['VALID','VALIDATED'], true);
        $amountMatches = abs((float)($validation['amount'] ?? -1) - (float)$payment['amount']) < 0.01;
        $currencyMatches = strtoupper((string)($validation['currency'] ?? '')) === 'BDT';
        $transactionMatches = hash_equals((string)$payment['transaction_id'], (string)($validation['tran_id'] ?? ''));
        if (!$validStatus || !$amountMatches || !$currencyMatches || !$transactionMatches) {
            Database::safeExecute('UPDATE payments SET status="failed",raw_response=? WHERE id=?', [json_encode(['callback'=>$payload,'validation'=>$validation],JSON_UNESCAPED_SLASHES),$payment['id']]);
            return ['ok'=>false,'order_id'=>(int)$payment['order_id'],'message'=>'Gateway validation failed.'];
        }
        Database::transaction(function() use ($payment, $payload, $validation) {
            $locked = Database::fetchOne('SELECT * FROM orders WHERE id=? FOR UPDATE', [$payment['order_id']]);
            if (!$locked) throw new RuntimeException('Order not found during payment confirmation.');
            if ((string)$locked['payment_status'] !== 'paid') {
                Database::execute('UPDATE orders SET payment_status="paid",paid_amount=total_amount,due_amount=0,updated_at=NOW() WHERE id=?', [$locked['id']]);
            }
            Database::execute('UPDATE payments SET status="paid",raw_response=? WHERE id=?', [json_encode(['callback'=>$payload,'validation'=>$validation],JSON_UNESCAPED_SLASHES),$payment['id']]);
            Database::safeExecute('INSERT INTO order_status_history (order_id,status,note,created_by,created_at) VALUES (?,"payment_confirmed","SSLCommerz payment validated",NULL,NOW())', [$locked['id']]);
        });
        AffiliateService::processOrder((int)$payment['order_id']);
        return ['ok'=>true,'order_id'=>(int)$payment['order_id'],'message'=>'Payment confirmed.'];
    }

    private static function validateSslCommerz(string $valId): array {
        if (SSLCOMMERZ_STORE_ID === '' || SSLCOMMERZ_STORE_PASSWORD === '') throw new RuntimeException('SSLCommerz credentials are missing.');
        if (!function_exists('curl_init')) throw new RuntimeException('The PHP cURL extension is required.');
        $base = PAYMENT_SANDBOX ? 'https://sandbox.sslcommerz.com' : 'https://securepay.sslcommerz.com';
        $url = $base . '/validator/api/validationserverAPI.php?' . http_build_query(['val_id'=>$valId,'store_id'=>SSLCOMMERZ_STORE_ID,'store_passwd'=>SSLCOMMERZ_STORE_PASSWORD,'v'=>1,'format'=>'json']);
        $ch=curl_init($url);
        curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_CONNECTTIMEOUT=>15,CURLOPT_TIMEOUT=>35,CURLOPT_SSL_VERIFYPEER=>true,CURLOPT_SSL_VERIFYHOST=>2]);
        $body=curl_exec($ch);$error=curl_error($ch);$http=(int)curl_getinfo($ch,CURLINFO_HTTP_CODE);curl_close($ch);
        $data=is_string($body)?json_decode($body,true):null;
        if($http!==200||!is_array($data))throw new RuntimeException('Could not validate SSLCommerz payment. '.$error);
        return $data;
    }
}
