<?php

/**
 * CS2 Bot League — Payment Agent
 * Upload this file to your Shaparak-registered domain host.
 * Version: 1.0.0
 */

declare(strict_types=1);

$config = json_decode('{{CONFIG_JSON}}', true);
if (!is_array($config)) {
    http_response_code(500);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['success' => false, 'message' => 'Invalid agent configuration']);
    exit;
}

const AGENT_VERSION = '1.2.0';

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function agent_self_url(): string
{
    $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
    $script = $_SERVER['SCRIPT_NAME'] ?? '/payment-agent.php';

    return $scheme . '://' . $host . $script;
}

function agent_data_dir(): string
{
    $dir = __DIR__ . '/payment-agent-data';
    if (!is_dir($dir)) {
        @mkdir($dir, 0755, true);
    }

    return $dir;
}

function agent_tokens_path(): string
{
    return agent_data_dir() . '/tokens.json';
}

/** @param array<string, mixed> $payload */
function agent_sign(array $payload, string $secret): string
{
    $timestamp = (int) ($payload['timestamp'] ?? time());
    $data = $payload;
    unset($data['signature']);
    $body = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

    return hash_hmac('sha256', $timestamp . '.' . $body, $secret);
}

/** @param array<string, mixed> $payload */
function agent_verify(array $payload, string $secret, int $maxAge = 300): bool
{
    $signature = (string) ($payload['signature'] ?? '');
    if ($signature === '') {
        return false;
    }
    $timestamp = (int) ($payload['timestamp'] ?? 0);
    if ($timestamp <= 0 || abs(time() - $timestamp) > $maxAge) {
        return false;
    }

    return hash_equals(agent_sign($payload, $secret), $signature);
}

function agent_json(array $data, int $status = 200): never
{
    http_response_code($status);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

function agent_html(string $html, int $status = 200): never
{
    http_response_code($status);
    header('Content-Type: text/html; charset=utf-8');
    echo $html;
    exit;
}

/** @param array<string, mixed> $data */
function agent_post(string $url, array $data, array $headers = []): array
{
    $httpHeaders = ['Content-Type: application/json', 'Accept: application/json'];
    foreach ($headers as $key => $value) {
        $httpHeaders[] = $key . ': ' . $value;
    }

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => $httpHeaders,
        CURLOPT_POSTFIELDS => json_encode($data, JSON_UNESCAPED_UNICODE),
        CURLOPT_TIMEOUT => 30,
    ]);
    $response = curl_exec($ch);
    curl_close($ch);

    $decoded = json_decode((string) $response, true);

    return is_array($decoded) ? $decoded : [];
}

function agent_to_rial(int $amountToman): int
{
    return $amountToman * 10;
}

/** @return array<string, mixed>|null */
function agent_load_payment(int $paymentId): ?array
{
    $path = agent_data_dir() . '/payment_' . $paymentId . '.json';
    if (!is_file($path)) {
        return null;
    }
    $raw = file_get_contents($path);
    if ($raw === false) {
        return null;
    }
    $data = json_decode($raw, true);

    return is_array($data) ? $data : null;
}

/** @param array<string, mixed> $data */
function agent_save_payment(int $paymentId, array $data): void
{
    $path = agent_data_dir() . '/payment_' . $paymentId . '.json';
    file_put_contents($path, json_encode($data, JSON_UNESCAPED_UNICODE));
}

function agent_delete_payment(int $paymentId): void
{
    $path = agent_data_dir() . '/payment_' . $paymentId . '.json';
    if (is_file($path)) {
        @unlink($path);
    }
}

/**
 * @param array<string, mixed> $config
 * @param array<string, mixed>|null $stored
 * @return array<string, mixed>
 */
function agent_with_gateways(array $config, ?array $stored = null): array
{
    $gateways = is_array($config['gateways'] ?? null) ? $config['gateways'] : [];

    if ($stored !== null && is_array($stored['gateways'] ?? null)) {
        $gateways = array_merge($gateways, $stored['gateways']);
    }

    $config['gateways'] = $gateways;

    return $config;
}

/** @param array<string, mixed> $config */
function agent_forward_to_main(array $config, array $payload): array
{
    $payload['timestamp'] = time();
    $payload['signature'] = agent_sign($payload, (string) $config['secret']);

    $url = rtrim((string) $config['main_service_url'], '/') . '/api/v1/payment-agent/callback';
    $response = agent_post($url, $payload);

    return is_array($response) ? $response : [];
}

/** @param array<string, mixed> $config */
function agent_finish_callback(array $config, array $payload, int $paymentId): never
{
    $response = agent_forward_to_main($config, $payload);
    agent_delete_payment($paymentId);

    $redirectUrl = (string) ($response['redirect_url'] ?? '');
    if ($redirectUrl !== '') {
        header('Location: ' . $redirectUrl);
        exit;
    }

    agent_html('<h1>پردازش انجام شد</h1><p>به ربات تلگرام برگردید.</p>');
}

// ---------------------------------------------------------------------------
// Gateway implementations (standalone)
// ---------------------------------------------------------------------------

/** @param array<string, mixed> $config */
function agent_zarinpal_base(array $config): string
{
    $sandbox = (bool) ($config['gateways']['zarinpal_sandbox'] ?? true);

    return $sandbox
        ? 'https://sandbox.zarinpal.com/pg/v4/payment'
        : 'https://payment.zarinpal.com/pg/v4/payment';
}

/** @param array<string, mixed> $config */
function agent_zarinpal_create(array $config, int $paymentId, int $finalAmountToman, string $description, string $callbackUrl): array
{
    $merchantId = (string) ($config['gateways']['zarinpal_merchant_id'] ?? '');
    if ($merchantId === '') {
        return ['success' => false, 'message' => 'Zarinpal merchant ID not configured'];
    }

    $payload = [
        'merchant_id' => $merchantId,
        'amount' => agent_to_rial($finalAmountToman),
        'callback_url' => $callbackUrl,
        'description' => $description,
        'metadata' => ['order_id' => (string) $paymentId],
    ];

    $response = agent_post(agent_zarinpal_base($config) . '/request.json', $payload);
    $code = (int) ($response['data']['code'] ?? 0);
    $authority = $response['data']['authority'] ?? null;

    if ($code !== 100 || !is_string($authority)) {
        return ['success' => false, 'message' => $response['errors'][0]['message'] ?? 'Zarinpal request failed', 'raw' => $response];
    }

    $sandbox = (bool) ($config['gateways']['zarinpal_sandbox'] ?? true);
    $payUrl = ($sandbox ? 'https://sandbox.zarinpal.com/pg/StartPay/' : 'https://payment.zarinpal.com/pg/StartPay/') . $authority;

    return ['success' => true, 'redirect_url' => $payUrl, 'authority' => $authority, 'raw' => $response];
}

/** @param array<string, mixed> $config */
function agent_zarinpal_verify(array $config, string $authority, int $finalAmountToman): array
{
    $merchantId = (string) ($config['gateways']['zarinpal_merchant_id'] ?? '');
    $payload = [
        'merchant_id' => $merchantId,
        'amount' => agent_to_rial($finalAmountToman),
        'authority' => $authority,
    ];

    $response = agent_post(agent_zarinpal_base($config) . '/verify.json', $payload);
    $code = (int) ($response['data']['code'] ?? 0);

    if (!in_array($code, [100, 101], true)) {
        return ['success' => false, 'message' => $response['errors'][0]['message'] ?? 'Verification failed', 'raw' => $response];
    }

    return ['success' => true, 'ref_id' => (string) ($response['data']['ref_id'] ?? ''), 'raw' => $response];
}

/** @param array<string, mixed> $config */
function agent_sepal_api_url(string $action): string
{
    return 'https://sepal.ir/api/' . $action . '.json';
}

/** @param array<string, mixed> $config */
function agent_sepal_create(array $config, int $paymentId, int $finalAmountToman, string $description, string $callbackUrl): array
{
    $apiKey = (string) ($config['gateways']['sepal_api_key'] ?? '');
    if ($apiKey === '') {
        return ['success' => false, 'message' => 'Sepal API key not configured'];
    }

    $payload = [
        'apiKey' => $apiKey,
        'amount' => agent_to_rial($finalAmountToman),
        'callbackUrl' => $callbackUrl,
        'invoiceNumber' => (string) $paymentId,
        'description' => $description,
    ];

    $response = agent_post(agent_sepal_api_url('request'), $payload);
    if ($response === []) {
        return ['success' => false, 'message' => 'Sepal connection failed', 'raw' => $response];
    }

    if (!($response['status'] ?? false)) {
        return ['success' => false, 'message' => (string) ($response['message'] ?? 'Sepal request failed'), 'raw' => $response];
    }

    $paymentNumber = (string) ($response['paymentNumber'] ?? '');
    if ($paymentNumber === '') {
        return ['success' => false, 'message' => 'No payment number returned', 'raw' => $response];
    }

    return [
        'success' => true,
        'redirect_url' => 'https://sepal.ir/payment/' . rawurlencode($paymentNumber),
        'authority' => $paymentNumber,
        'raw' => $response,
    ];
}

/** @param array<string, mixed> $config */
function agent_sepal_verify(array $config, string $token, int $finalAmountToman): array
{
    $apiKey = (string) ($config['gateways']['sepal_api_key'] ?? '');
    $payload = [
        'apiKey' => $apiKey,
        'paymentNumber' => $token,
        'amount' => agent_to_rial($finalAmountToman),
    ];

    $response = agent_post(agent_sepal_api_url('verify'), $payload);
    if ($response === []) {
        return ['success' => false, 'message' => 'Sepal connection failed', 'raw' => $response];
    }

    if (!($response['status'] ?? false)) {
        return ['success' => false, 'message' => (string) ($response['message'] ?? 'Verification failed'), 'raw' => $response];
    }

    return [
        'success' => true,
        'ref_id' => (string) ($response['refNumber'] ?? $response['paymentNumber'] ?? $token),
        'raw' => $response,
    ];
}

/** @param array<string, mixed> $config */
function agent_digipay_base(array $config): string
{
    $sandbox = (bool) ($config['gateways']['digipay_sandbox'] ?? true);

    return $sandbox
        ? 'https://uat.mydigipay.info/digipay/api'
        : 'https://api.mydigipay.com/digipay/api';
}

/** @param array<string, mixed> $config */
function agent_digipay_get_token(array $config): ?string
{
    $tokensPath = agent_tokens_path();
    if (is_file($tokensPath)) {
        $stored = json_decode((string) file_get_contents($tokensPath), true);
        if (is_array($stored)) {
            $expiresAt = $stored['expires_at'] ?? 0;
            $token = (string) ($stored['access_token'] ?? '');
            if ($token !== '' && (int) $expiresAt > time() + 60) {
                return $token;
            }
        }
    }

    $clientId = (string) ($config['gateways']['digipay_client_id'] ?? '');
    $clientSecret = (string) ($config['gateways']['digipay_client_secret'] ?? '');
    $username = (string) ($config['gateways']['digipay_username'] ?? '');
    $password = (string) ($config['gateways']['digipay_password'] ?? '');

    if ($clientId === '' || $clientSecret === '' || $username === '' || $password === '') {
        return null;
    }

    $auth = base64_encode($clientId . ':' . $clientSecret);
    $ch = curl_init(agent_digipay_base($config) . '/oauth/token');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Authorization: Basic ' . $auth],
        CURLOPT_POSTFIELDS => [
            'username' => $username,
            'password' => $password,
            'grant_type' => 'password',
        ],
        CURLOPT_TIMEOUT => 30,
    ]);
    $response = curl_exec($ch);
    curl_close($ch);

    $decoded = json_decode((string) $response, true);
    if (!is_array($decoded) || empty($decoded['access_token'])) {
        return null;
    }

    $expiresIn = (int) ($decoded['expires_in'] ?? 3600);
    file_put_contents($tokensPath, json_encode([
        'access_token' => $decoded['access_token'],
        'expires_at' => time() + $expiresIn,
    ], JSON_UNESCAPED_UNICODE));

    return (string) $decoded['access_token'];
}

/** @param array<string, mixed> $config */
function agent_digipay_headers(string $token): array
{
    return [
        'Authorization' => 'Bearer ' . $token,
        'Agent' => 'WEB',
        'Digipay-Version' => '2022-02-02',
    ];
}

/** @param array<string, mixed> $config */
function agent_digipay_create(array $config, int $paymentId, int $finalAmountToman, string $description, string $callbackUrl): array
{
    $token = agent_digipay_get_token($config);
    if ($token === null) {
        return ['success' => false, 'message' => 'DigiPay authentication failed'];
    }

    $payload = [
        'amount' => agent_to_rial($finalAmountToman),
        'cellNumber' => '09000000000',
        'providerId' => (string) $paymentId,
        'callbackUrl' => $callbackUrl,
    ];

    $response = agent_post(
        agent_digipay_base($config) . '/tickets/business?type=11',
        $payload,
        agent_digipay_headers($token)
    );

    $status = (int) ($response['result']['status'] ?? -1);
    if ($status !== 0) {
        return [
            'success' => false,
            'message' => (string) ($response['result']['message'] ?? 'DigiPay ticket creation failed'),
            'raw' => $response,
        ];
    }

    $ticketId = $response['ticket'] ?? $response['ticketId'] ?? null;
    $redirectUrl = $response['redirectUrl'] ?? $response['paymentUrl'] ?? null;

    if (!is_string($redirectUrl) || $redirectUrl === '') {
        return ['success' => false, 'message' => 'DigiPay ticket creation failed', 'raw' => $response];
    }

    return [
        'success' => true,
        'redirect_url' => (string) $redirectUrl,
        'authority' => is_string($ticketId) ? $ticketId : (string) $paymentId,
        'raw' => $response,
    ];
}

/** @param array<string, mixed> $config */
function agent_digipay_verify(array $config, string $token, int $paymentId): array
{
    $accessToken = agent_digipay_get_token($config);
    if ($accessToken === null) {
        return ['success' => false, 'message' => 'DigiPay authentication failed'];
    }

    $providerId = (string) $paymentId;

    $response = agent_post(
        agent_digipay_base($config) . '/purchases/verify?type=5',
        ['trackingCode' => $token, 'providerId' => $providerId],
        agent_digipay_headers($accessToken)
    );

    $status = (int) ($response['result']['status'] ?? $response['status'] ?? -1);
    if ($status !== 0) {
        return [
            'success' => false,
            'message' => (string) ($response['result']['message'] ?? 'DigiPay verification failed'),
            'raw' => $response,
        ];
    }

    $refId = (string) ($response['trackingCode'] ?? $response['providerId'] ?? $token);

    agent_post(
        agent_digipay_base($config) . '/purchases/deliver?type=5',
        ['trackingCode' => $refId, 'providerId' => $providerId],
        agent_digipay_headers($accessToken)
    );

    return ['success' => true, 'ref_id' => $refId, 'raw' => $response];
}

/** @param array<string, mixed> $config */
function agent_create_payment(array $config, int $paymentId, string $gateway, int $finalAmountToman, string $description): array
{
    $callbackUrl = agent_self_url() . '?action=callback&gateway=' . urlencode($gateway) . '&payment_id=' . $paymentId;

    $result = match ($gateway) {
        'zarinpal' => agent_zarinpal_create($config, $paymentId, $finalAmountToman, $description, $callbackUrl),
        'sepal' => agent_sepal_create($config, $paymentId, $finalAmountToman, $description, $callbackUrl),
        'digipay' => agent_digipay_create($config, $paymentId, $finalAmountToman, $description, $callbackUrl),
        default => ['success' => false, 'message' => 'Unknown gateway: ' . $gateway],
    };

    if ($result['success'] ?? false) {
        agent_save_payment($paymentId, [
            'gateway' => $gateway,
            'final_amount' => $finalAmountToman,
            'authority' => $result['authority'] ?? null,
            'gateways' => $config['gateways'] ?? [],
            'created_at' => date('c'),
        ]);
    }

    return $result;
}

/** @param array<string, mixed> $config */
function agent_handle_callback(array $config, string $gateway, int $paymentId): never
{
    $stored = agent_load_payment($paymentId);
    if ($stored === null) {
        agent_html('<h1>پرداخت یافت نشد</h1>', 404);
    }

    $config = agent_with_gateways($config, $stored);
    $finalAmount = (int) ($stored['final_amount'] ?? 0);
    $cancelled = false;
    $token = '';

    if ($gateway === 'zarinpal') {
        $status = $_GET['Status'] ?? '';
        $token = (string) ($_GET['Authority'] ?? '');
        $cancelled = $status !== 'OK';
    } elseif ($gateway === 'sepal') {
        $token = (string) ($_GET['paymentNumber'] ?? $_POST['paymentNumber'] ?? '');
        $status = $_GET['status'] ?? $_POST['status'] ?? 'success';
        $cancelled = $status !== 'success' && $status !== '1';
    } elseif ($gateway === 'digipay') {
        $token = (string) ($_GET['trackingCode'] ?? $_GET['providerId'] ?? '');
        $result = $_GET['result'] ?? 'success';
        $cancelled = $result !== 'success' && $result !== '0';
    } else {
        agent_html('<h1>درگاه نامعتبر</h1>', 400);
    }

    if ($cancelled) {
        agent_finish_callback($config, [
            'payment_id' => $paymentId,
            'gateway' => $gateway,
            'status' => 'cancelled',
            'authority' => $token,
            'gateway_response' => $_GET,
        ], $paymentId);
    }

    $verify = match ($gateway) {
        'zarinpal' => agent_zarinpal_verify($config, $token, $finalAmount),
        'sepal' => agent_sepal_verify($config, $token, $finalAmount),
        'digipay' => agent_digipay_verify($config, $token, $paymentId),
        default => ['success' => false, 'message' => 'Unknown gateway'],
    };

    if (!($verify['success'] ?? false)) {
        agent_finish_callback($config, [
            'payment_id' => $paymentId,
            'gateway' => $gateway,
            'status' => 'failed',
            'authority' => $token,
            'gateway_response' => $verify['raw'] ?? [],
        ], $paymentId);
    }

    agent_finish_callback($config, [
        'payment_id' => $paymentId,
        'gateway' => $gateway,
        'status' => 'paid',
        'ref_id' => $verify['ref_id'] ?? '',
        'authority' => $token,
        'gateway_response' => $verify['raw'] ?? [],
    ], $paymentId);
}

// ---------------------------------------------------------------------------
// Router
// ---------------------------------------------------------------------------

$action = (string) ($_GET['action'] ?? '');

if ($action === 'callback') {
    $gateway = (string) ($_GET['gateway'] ?? '');
    $paymentId = (int) ($_GET['payment_id'] ?? 0);
    if ($paymentId <= 0 || $gateway === '') {
        agent_html('<h1>درخواست نامعتبر</h1>', 400);
    }
    agent_handle_callback($config, $gateway, $paymentId);
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    agent_json(['success' => false, 'message' => 'Method not allowed'], 405);
}

$raw = file_get_contents('php://input');
$body = json_decode((string) $raw, true);
if (!is_array($body)) {
    agent_json(['success' => false, 'message' => 'Invalid JSON body'], 400);
}

if (!agent_verify($body, (string) $config['secret'])) {
    agent_json(['success' => false, 'message' => 'Invalid signature'], 403);
}

$bodyAction = (string) ($body['action'] ?? '');

if ($bodyAction === 'ping') {
    agent_json([
        'success' => true,
        'message' => 'pong',
        'version' => AGENT_VERSION,
        'agent_url' => agent_self_url(),
    ]);
}

if ($bodyAction === 'create') {
    $paymentId = (int) ($body['payment_id'] ?? 0);
    $gateway = (string) ($body['gateway'] ?? '');
    $finalAmount = (int) ($body['final_amount'] ?? 0);
    $description = (string) ($body['description'] ?? '');

    if ($paymentId <= 0 || $gateway === '' || $finalAmount <= 0) {
        agent_json(['success' => false, 'message' => 'Invalid create payload'], 400);
    }

    if (isset($body['gateways']) && is_array($body['gateways'])) {
        $config = agent_with_gateways($config, ['gateways' => $body['gateways']]);
    }

    $result = agent_create_payment($config, $paymentId, $gateway, $finalAmount, $description);
    agent_json($result, ($result['success'] ?? false) ? 200 : 422);
}

agent_json(['success' => false, 'message' => 'Unknown action'], 400);
