<?php
namespace App\Libraries;

use App\Models\AppsModel;
use App\Models\PaymentIntentModel;
use App\Models\PlansModel;
use App\Models\SubscribesModel;
use App\Models\TransactionsModel;
use App\Models\UsersModel;
use App\Libraries\Uid;

class Payment
{
    private $user_id;
    private $app;
    private $plan;
    private $apps;
    private $plans;
    private $transactions;
    private $users;
    private $uid;
    private $subscribes;
    private $currencyCode;

    function __construct(array $paymentData)
    {
        $this->apps = new AppsModel();
        $this->plans = new PlansModel();
        $this->transactions = new TransactionsModel();
        $this->users = new UsersModel();
        $this->uid = new Uid();
        $this->subscribes = new SubscribesModel();

        if (!isset($paymentData["user_id"])) {
            throw new \Exception("User id is required");
        }

        $this->user_id = $paymentData["user_id"];

        if (!isset($paymentData["app_uid"]) && !isset($paymentData["app_id"])) {
            throw new \Exception("App uid or app id is required");
        }

        $this->app = isset($paymentData["app_id"]) ? $this->get_app_by_id($paymentData["app_id"]) : $this->get_app_by_uid($paymentData["app_uid"]);

        if (!$this->app) {
            throw new \Exception(lang("Message.message_14"));
        }

        if (!isset($paymentData["plan_id"])) {
            throw new \Exception("Plan id is required");
        }

        $this->plan = $this->get_plan($paymentData["plan_id"]);

        if (!$this->plan) {
            throw new \Exception(lang("Message.message_83"));
        }

        $settings = new Settings();
        $this->currencyCode = $settings->get_config("currency_code");
    }

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

    /**
     * Get payment data
     * @return array
     */
    public function get_payment_request_data(): array
    {
        return [
            "event" => true,
            "data" => [
                "plan_id" => $this->plan["id"],
                "app_id" => $this->app["id"],
                "user_id" => $this->user_id,
                "price" => $this->plan["price"],
                "currency_code" => $this->currencyCode,
                "plan_api_id" => $this->plan["api_id"],
                "type" => $this->plan["type"],
            ]
        ];
    }

    /**
     * Get current user
     * @return array
     */
    public function get_current_user(): array
    {
        return $this->users->where("id", $this->user_id)->first();
    }

    /**
     * Create subscription
     * @return array
     */
    public function create_subscription(array $data): array
    {
        if ($data["method_route"] === "gift") {
            $method_id = 0;
        } else {
            $depositMethods = new DepositMethods();
            $method_id = $depositMethods->get_deposit_method_id($data["method_route"]);

            if (!$method_id) {
                return [
                    "event" => false,
                    "message" => "Method id not found",
                ];
            }
        }

        
        $expires_at = $this->plan["type"] === "subscription"
            ? strtotime('+' . $this->plan['count'] . ' month', strtotime(date('c')))
            : 0;
        
        $this->subscribes->insert([
            "subscribe_external_id" => $data["subscribe_external_id"],
            "customer_external_id"  => $data["customer_external_id"],
            "plan_id"               => $this->plan["id"],
            "user_id"               => $this->user_id,
            "expires_at"            => $expires_at,
            "app_id"                => $this->app["id"],
            "price"                 => $data["price"],
            "uid"                   => $this->uid->create(),
            "is_active"             => $this->plan["type"] === "subscription" ? 1 : 0,
            "remaining_count"       => $this->plan["build_count"] ?? 0,
            "method_id"             => $method_id
        ]);

        return [
            "event" => true,
        ];
    }

    /**
     * Create transaction
     * @param array $data Data for the transaction
     * @param bool $updateSubscription Update subscription data
     * @return array Response with transaction details or error
     */
    public function create_transaction(array $data, bool $updateSubscription = false): array
    {
        $depositMethods = new DepositMethods();
        $method_id = $depositMethods->get_deposit_method_id($data["method_route"]);

        if (!$method_id) {
            return [
                "event" => false,
                "message" => "Method id not found",
            ];
        }

        try {
            $this->transactions->insert([
                'uid' => $this->uid->create(),
                'amount' => $data["price"],
                'status' => 1,  
                'created_at' => time(),
                'updated_at' => time(),
                'method_id' => $method_id,
                'subscribe_external_id' => $data["subscribe_external_id"],
                'external_uid' => $data["external_uid"],
                'data_json' => json_encode($data['event_data']),
            ]);

            if ($updateSubscription && $this->plan["type"] === "subscription") {
                $newExpiresAt = strtotime('+' . $this->plan['count'] . ' month', strtotime(date('c')));
                $result = $this->update_subscription([
                    "expires_at" => $newExpiresAt,
                ], true);
            }

            if (!$result["event"]) {
                return $result;
            }

            return [
                "event" => true,
            ];
        } catch (\Exception $e) {
            return [
                "event" => false,
                "message" => $e->getMessage(),
            ];
        }
    }

    /**
     * Create payment intent
     * @return array
     */
    public function create_payment_intent(array $data): array
    {
        $depositMethods = new DepositMethods();
        $method_id = $depositMethods->get_deposit_method_id($data["method_route"]);

        if (!$method_id) {
            return [
                "event" => false,
                "message" => "Method id not found",
            ];
        }

        $subscribe = $this->subscribes
            ->where("subscribe_external_id", $data["subscribe_external_id"])
            ->first();

        if (!$subscribe) {
            return [
                "event" => false,
                "message" => lang("Message.message_87"),
            ];
        }
        
        try {
            $intents = new PaymentIntentModel();

            // create payment intent
            $intents->insert([
                "subscribe_id"  => $subscribe["id"],
                "method_id"     => $method_id,
                "is_pending"    => 1,
                "payment_token" => $data["payment_token"],
                "planned_at"    => strtotime('+' . $this->plan['count'] . ' month', strtotime(date('c'))),
            ]);

            return [
                "event" => true,
            ];
        } catch (\Exception $e) {
            return [
                "event" => false,
                "message" => $e->getMessage(),
            ];
        }
    }

    public function update_payment_intent(int $id, array $data, bool $updatePlannedAt = false): array
    {
        try {
            $intents = new PaymentIntentModel();

            if ($updatePlannedAt) {
                $data["planned_at"] = strtotime('+' . $this->plan['count'] . ' month', strtotime(date('c')));
            }

            $intents->update($id, $data);

            return [
                "event" => true,
            ];
        } catch (\Exception $e) {
            return [
                "event" => false,
                "message" => $e->getMessage(),
            ];
        }
    }

    /**
     * Cancel subscription
     * @return array
     */
    public function cancel_subscription(array $data): array
    {
        try {
            $result = $this->update_subscription([
                "is_active" => 0,
                "expires_at" => $data["cancel_at"],
            ]);

            if (!$result["event"]) {
                return $result;
            }

            return [
                "event" => true,
            ];
        } catch (\Exception $e) {
            return [
                "event" => false,
                "message" => $e->getMessage(),
            ];
        }
    }

    /**
     * Update subscription
     * @param array $data Data for the subscription
     * @return array Response with subscription details or error
     */
    public function update_subscription(array $data, bool $updateRemainingCount = false): array
    {
        $subscribe = $this->subscribes
            ->where("app_id", $this->app["id"])
            ->where("user_id", $this->user_id)
            ->where("plan_id", $this->plan["id"])
            ->first();

        if (!$subscribe) {
            return [
                "event" => false,
                "message" => lang("Message.message_87"),
            ];
        }

        try {
            $this->subscribes->update($subscribe["id"], [
                "expires_at" => $data["expires_at"] ?? $subscribe["expires_at"],
                "is_active" => $data["is_active"] ?? $subscribe["is_active"],
                "remaining_count" => $updateRemainingCount && $subscribe["expires_at"] ? $this->plan["build_count"] ?? 0 : $subscribe["remaining_count"],
            ]);

            return [
                "event" => true,
            ];
        } catch (\Exception $e) {
            return [
                "event" => false,
                "message" => $e->getMessage(),
            ];
        }
    }

    /**
     * Make gift subscription
     * @return array
     */
    public function make_gift(): array
    {
        try {
            $subscribeExternalId = "gift_".$this->uid->create();

            $result = $this->create_subscription([
                "method_route" => "gift",
                "subscribe_external_id" => $subscribeExternalId,
                "customer_external_id" => $subscribeExternalId,
                "price" => 0,
            ]);

            if (!$result["event"]) {
                return $result;
            }

            return [
                "event" => true,
            ];
        } catch (\Exception $e) {
            return [
                "event" => false,
                "message" => $e->getMessage(),
            ];
        }
    }

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

    /**
     * Get plan
     * @return array|null
     */
    private function get_plan($plan_id): ?array
    {
        $plan = $this->plans
            ->where("id", $plan_id)
            ->where("status", 1)
            ->where("deleted_at", 0)
            ->first();
        
        if (!$plan) {
            return null;
        }

        return $plan;
    }

    /**
     * Get app by id
     * @return array|null
     */
    private function get_app_by_id(int $app_id): ?array
    {
        $app = $this->apps
            ->where("id", $app_id)
            ->where("user", $this->user_id)
            ->where("deleted_at", 0)
            ->first();
        
        if (!$app) {
            return null;
        }

        return $app;
    }

    /**
     * Get app by uid
     * @return array|null
     */
    private function get_app_by_uid(string $app_uid): ?array
    {
        $app = $this->apps
            ->where("uid", $app_uid)
            ->where("user", $this->user_id)
            ->where("deleted_at", 0)
            ->first();
        
        if (!$app) {
            return null;
        }

        return $app;
    }
}
