<?php namespace App\Libraries;

use App\Models\SubscribesModel;
use CodeIgniter\HTTP\CURLRequest;
use Config\Services;
use Exception;

class YooKassaClient
{
    private string $returnUrl;
    private string $cancelUrl;
    private string $apiShopId;
    private string $apiSecretKey;
    private string $baseApiUrl = 'https://api.yookassa.ru/v3/';
    private string $currencyCode;

    /**
     * Create models, config and library's
     */
    function __construct()
    {
        $depositMethods = new DepositMethods();
        $apiKeys = $depositMethods->get_deposit_method_apikeys("yookassa");

        $this->apiShopId = $apiKeys["api_value_2"];
        $this->apiSecretKey = $apiKeys["api_value_1"];

        $settings = new Settings();
        $this->currencyCode = $settings->get_config("currency_code");
        $frontUrl = rtrim($settings->get_config("site_url"), '/');
        $this->returnUrl = "$frontUrl/private/profile/subscribe";
        $this->cancelUrl = "$frontUrl/private/profile/subscribe";
    }

    /**************************************************************************************
     * PUBLIC FUNCTIONS
     **************************************************************************************/

    /**
     * Create a payment order
     * @param array $data Data for the order
     * @return array Response with order details or error
     */
    public function create_order(array $data): array
    {
        try {
            $uid = new Uid();
            
            $payload = [
                "amount" => [
                    "value" => $data['price'],
                    "currency" => $this->currencyCode
                ],
                "payment_method_data" => [
                    "type" => "bank_card"
                ],
                "confirmation" => [
                    "type" => "redirect",
                    "return_url" => $this->returnUrl,
                ],
                "capture" => true,
                "description" => lang("Fields.field_157") . " " . (new Settings())->get_config("site_name"),
                "save_payment_method" => true,
                "metadata" => [
                    "customer_id" => $data["user_id"],
                    "plan_id" => $data["plan_id"],
                    "app_id" => $data["app_id"],
                    "subscribe_id" => $uid->create(),
                    "type" => $data["type"],
                    "update_subscription" => false,
                ],
            ];
            
            $response = Services::curlrequest([
                "baseURI" => $this->baseApiUrl,
                "headers" => [
                    "Content-Type" => "application/json",
                    "Idempotence-Key" => $uid->create(),
                ],
                "auth" => [
                    $this->apiShopId,
                    $this->apiSecretKey
                ],
                "http_errors" => false,
            ])->setJSON($payload)->post("payments");

            $result = json_decode($response->getBody(), true);
            
            if ($response->getStatusCode() == 200) {
                return [
                    'event' => true,
                    'order_id' => $result['id'],
                    'status' => 'created',
                    'approval_url' => $result['confirmation']['confirmation_url'],
                    'data' => $result,
                ];
            }
            
            throw new Exception('Failed to create payment order: ' . $response->getBody());
        } catch (Exception $e) {
            return [
                'event' => false,
                'message' => $e->getMessage(),
            ];
        }
    }


    /**
     * Create a recurring order
     * @param array $data Data for the order
     * @param string $subscribe_external_id Subscribe external id
     * @param string $payment_token Payment token
     * @return array Response with order details or error
     */
    public function create_recurring_order(array $data, string $subscribe_external_id, string $payment_token): array
    {
        if ($data['type'] !== 'subscription') {
            return [
                'event' => false,
                'message' => 'Invalid order type',
            ];
        }

        try {
            $uid = new Uid();
            
            $payload = [
                "amount" => [
                    "value" => $data['price'],
                    "currency" => $this->currencyCode
                ],
                "capture" => true,
                "description" => lang("Fields.field_157") . " " . (new Settings())->get_config("site_name"),
                "payment_method_id" => $payment_token,
                "metadata" => [
                    "customer_id" => $data["user_id"],
                    "plan_id" => $data["plan_id"],
                    "app_id" => $data["app_id"],
                    "subscribe_id" => $subscribe_external_id,
                    "type" => $data["type"],
                    "update_subscription" => true,
                ],
            ];
            
            $response = Services::curlrequest([
                "baseURI" => $this->baseApiUrl,
                "headers" => [
                    "Content-Type" => "application/json",
                    "Idempotence-Key" => $uid->create(),
                ],
                "auth" => [
                    $this->apiShopId,
                    $this->apiSecretKey
                ],
                "http_errors" => false,
            ])->setJSON($payload)->post("payments");

            $result = json_decode($response->getBody(), true);
            
            if ($response->getStatusCode() == 200) {
                return [
                    'event' => true,
                    'order_id' => $result['id'],
                    'status' => 'created',
                    'data' => $result,
                ];
            }
            
            throw new Exception('Failed to create payment order: ' . $response->getBody());
        } catch (Exception $e) {
            return [
                'event' => false,
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Process webhook event
     * @param string $requestBody Raw request body
     * @param string $ipAddress IP address of the request
     * @return array Processed webhook event data or error
     */
    public function process_webhook(string $requestBody, string $ipAddress): array
    {
        // Verify IP address
        if (!$this->checkIPRange($ipAddress)) {
            return [
                'event' => false,
                'message' => 'Invalid IP address',
            ];
        }

        // Parse the webhook payload
        $payload = json_decode($requestBody, true);

        try {
            
            if (!$payload || !isset($payload['object'])) {
                return [
                    'event' => false,
                    'message' => 'Invalid payload',
                ];
            }
            
            $data = $payload['object'];
            $eventType = $payload['event'];
            
            // Process different event types
            if ($eventType === 'payment.succeeded') {
                $newEventType = $data['metadata']['type'] === "subscription" ? "subscription.succeeded" : "payment.succeeded";

                if ($data['metadata']['type'] === "subscription" && $data['metadata']['update_subscription'] == "true") {
                    $newEventType = "subscription.updated";
                }

                return [
                    'event' => true,
                    'event_type' => $newEventType,
                    'resource_id' => $data['id'],
                    'order_id' => $data['metadata']['subscribe_id'] ?? null,
                    'custom_id' => $data['metadata']["app_id"] . "_" . $data['metadata']["customer_id"] . "_" . $data['metadata']["plan_id"],
                    'amount' => (float)$data['amount']['value'],
                    'currency' => $data['amount']['currency'],
                    'payment_method_id' => $data['payment_method']['id'] ?? null,
                    'event_data' => $data,
                ];
            }
            
            // Default return for unhandled events
            return [
                'event' => false,
                'message' => 'Unhandled event type: ' . $eventType,
            ];
        } catch (Exception $e) {
            return [
                'event' => false,
                'message' => 'Failed to process webhook',
                'error' => $e->getMessage(),
            ];
        }
    }

    /**************************************************************************************
     * PRIVATE FUNCTIONS
     **************************************************************************************/

    /**
     * Check ip address
     * @param string $ip
     * @return bool
     */
    private function checkIPRange(string $ip): bool
    {
        $ranges = [
            ['185.71.76.0', '185.71.76.31'],
            ['185.71.77.0', '185.71.77.31'],
            ['77.75.153.0', '77.75.153.127'],
            ['77.75.156.11', '77.75.156.11'],
            ['77.75.156.35', '77.75.156.35'],
            ['77.75.154.128', '77.75.154.255'],
            ['2a02:5180::', '2a02:5180:ffff:ffff:ffff:ffff:ffff:ffff']
        ];

        $ipLong = ip2long($ip);

        foreach ($ranges as $range) {
            $start = ip2long($range[0]);
            $end = ip2long($range[1]);
            if ($ipLong >= $start && $ipLong <= $end) {
                return true;
            }
        }

        return false;
    }
}
