<?php
namespace App\Controllers\Api\Ipn;

use App\Controllers\BaseController;
use App\Libraries\DepositMethods;
use App\Libraries\Payment;
use App\Libraries\YooKassaClient;
use App\Models\DepositMethodsModel;
use App\Models\PaymentIntentModel;
use App\Models\SubscribesModel;
use CodeIgniter\HTTP\ResponseInterface;
use ReflectionException;

class YooKassa extends BaseController
{
    private $yookassa;

    public function __construct()
    {
        $this->yookassa = new YooKassaClient();
    }
    /**************************************************************************************
     * PUBLIC FUNCTIONS
     **************************************************************************************/

    /**
     * YooKassa webhook handler
     * @return ResponseInterface
     * @throws ReflectionException
     */
    public function index(): ResponseInterface
    {
        $methods = new DepositMethodsModel();

        $method = $methods
            ->where("id", 5)
            ->where("status", 1)
            ->first();

        if (!$method) {
            return $this->respond(["message" => lang("Message.message_84")], 400);
        }

        $payload = @file_get_contents('php://input');

        $result = $this->yookassa->process_webhook($payload, $this->request->getIPAddress());

        if (!$result["event"]) {
            // Webhook not processed, skip
            return $this->respond(["status" => "ok"], 200);
        }

        $customValues = explode("_", $result["custom_id"]);

        $app_id = $customValues[0];
        $user_id = $customValues[1];
        $plan_id = $customValues[2];

        $payment = new Payment([
            "user_id" => $user_id,
            "app_id" => $app_id,
            "plan_id" => $plan_id,
        ]);

        switch ($result["event_type"]) {
            case 'payment.succeeded':
                $payment->create_subscription([
                    "subscribe_external_id" => $result["order_id"],
                    "customer_external_id" => '',
                    "price" => (float)$result["amount"],
                    "method_route" => "yookassa",
                ]);
                
                $payment->create_transaction([
                    "subscribe_external_id" => $result["order_id"],
                    "external_uid" => $result["resource_id"],
                    "price" => (float)$result["amount"],
                    "method_route" => "yookassa",
                    "event_data" => $result["event_data"],
                ]);
                break;
                
            case 'subscription.succeeded':
                $payment->create_subscription([
                    "subscribe_external_id" => $result["order_id"],
                    "customer_external_id" => '',
                    "price" => (float)$result["amount"],
                    "method_route" => "yookassa",
                ]);
                
                $payment->create_payment_intent([
                    "subscribe_external_id" => $result["order_id"],
                    "payment_token" => $result["payment_method_id"],
                    "method_route" => "yookassa",
                ]);
                
                $payment->create_transaction([
                    "subscribe_external_id" => $result["order_id"],
                    "external_uid" => $result["resource_id"],
                    "price" => (float)$result["amount"],
                    "method_route" => "yookassa",
                    "event_data" => $result["event_data"],
                ]);
                break;
                
            case 'subscription.updated':
                $payment->create_transaction([
                    "subscribe_external_id" => $result["order_id"],
                    "external_uid" => $result["resource_id"],
                    "price" => (float)$result["amount"],
                    "method_route" => "yookassa",
                    "event_data" => $result["event_data"],
                ], true);
                break;

            default:
                return $this->respond(["message" => "Unhandled event type"], 400);
        }

        return $this->respond(["status" => "ok"], 200);
    }

    /**
     * Create planned payment
     * @return ResponseInterface
     * @throws ReflectionException
     */
    public function payment(): ResponseInterface
    {
        $depositMethods = new DepositMethods();
        $method_id = $depositMethods->get_deposit_method_id("yookassa");

        $intents = new PaymentIntentModel();
        $subscribes = new SubscribesModel();

        $payment_intents = $intents
            ->where("is_pending", 1)
            ->where("planned_at <", time())
            ->findAll();

        foreach ($payment_intents as $payment_intent) {
            $subscribe = $subscribes
                ->where("id", $payment_intent["subscribe_id"])
                ->where("method_id", $method_id)
                ->first();

            $payment = new Payment([
                "user_id" => $subscribe["user_id"],
                "app_id" => $subscribe["app_id"],
                "plan_id" => $subscribe["plan_id"],
            ]);

            $payment_data = $payment->get_payment_request_data();

            if (!$payment_data["event"]) {
                return $this->respond(["message" => $payment_data["message"]], 400);
            }

            $result = $this->yookassa->create_recurring_order($payment_data["data"], $subscribe["subscribe_external_id"], $payment_intent["payment_token"]);
            
            if ($result["event"]) {
                $payment->update_payment_intent($payment_intent["id"], [
                    "is_pending" => 1,
                ], true);
            } else {
                // cancel subscribe
                $subscribes->update($subscribe["id"], [
                    "is_active" => 0
                ]);
                
                // remove intent
                $payment->update_payment_intent($payment_intent["id"], [
                    "is_pending" => 0,
                ]);
            }
        }

        return $this->respond(["status" => "ok"], 200);
    }

    /**************************************************************************************
     * 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;
    }
}