<?php


class TaxManager
{

    private static $disabled_modules = array();

    public static function disable($module)
    {
        if (!isset(self::$disabled_modules[$module])) {
            self::$disabled_modules[$module] = $module;
        }
    }

    public static function isDisabled($module)
    {

        if (isset(self::$disabled_modules[$module])) {
            return TRUE;
        }

        return FALSE;
    }


}

class PaymentStatus
{


    const PENDING = 0;
    const CONFIRMED = 1;
    const DENIED = -1;


}


class PaymentsProvider
{

    private static $codes = array(
        "paypal" => self::PAYPAL_ID,
        "stripe" => self::STRIPE_ID,
        "cod" => self::COD_ID,
        "wallet" => self::WALLET_ID,
        "razorpay" => self::RAZORPAY_ID,
        "flutterwave" => self::FLUTTERWAVE,
        "hyperpay" => self::HYPERPAY,
        "paytm" => self::PAYTM,
        "transferBank" => self::BANK_TRANSFER,
        "paystack" => self::PAY_STACK,
        "mercadopago" => self::MERCADO_PAGO,
    );


    const  PAYPAL_ID = 1;
    const  STRIPE_ID = 2;
    const  COD_ID = 3;
    const  WALLET_ID = 5;
    const  RAZORPAY_ID = 6;
    const  FLUTTERWAVE = 7;
    const  HYPERPAY = 8;
    const  PAYTM = 9;
    const  APPLE_PAY = 10012;
    const  BANK_TRANSFER = 10013;
    const  PAY_STACK = 10014;
    const  MERCADO_PAGO = 10015;


    private static $payments = array();
    private static $redirections = array();
    private static $success_callbacks = array();
    private static $error_callbacks = array();
    private static $payment_processors = array();


    public static function findKeyById($id){
       foreach (self::$codes as $name => $code){
           if($code == $id){
               return ($name);
           }
       }
       return "";
    }

    public static function findIdByKey($name){
        foreach (self::$codes as $key => $code){
            if($key == $name){
                return ucfirst($code);
            }
        }

        return "";
    }

    /**
     * @return array
     */

    public static function updatePayments($module,$payments)
    {
        if (isset(self::$payments[$module])) {
            self::$payments[$module] = $payments;
        }else{
            die("The module <$module> doesn't has registered payments");
        }
    }

    public static function getPayments($module)
    {
        $list = array();

        if (isset(self::$payments[$module])) {

            foreach (self::$payments[$module] as $m => $p) {
                if (self::isEnabled($p['id'])) {
                    $list[] = $p;
                }
            }

            return $list;

        } else {
            return array();
        }
    }

    public static function excludePayments($module,$excludeId=array())
    {

        if (isset(self::$payments[$module])) {
            foreach (self::$payments[$module] as $ke => $payment){
                if(in_array($payment["id"],$excludeId) ) {
                    unset(self::$payments[$module][$ke]);
                }
            }

        }else{
            throw new Exception("The module <$module> hasn't registered payments");
        }

    }


    public static function getDefault($module="default")
    {
        $list = array();

        if (isset(self::$payments[$module])) {

            foreach (self::$payments[$module] as $m => $p) {
                if (self::isEnabled($p['id'])) {
                    $list[] = $p;
                }
            }

            return $list;

        } else {
            return array();
        }
    }


    public static function getAll()
    {
        return self::$payments;
    }

    private static $replaced = array();

    public static function provide($module, $payments, $redirection = "", $payment_callback_success = "", $payment_callback_error = "")
    {

        if (isset(self::$replaced[$module]))
            return;

        if (!isset(self::$payments[$module])) {
            self::$payments[$module] = $payments;

            $ctx = &get_instance();

            foreach (self::$payments[$module] as $k => $p) {

                if (defined('DEFAULT_TAX') AND DEFAULT_TAX > 0) {
                    $p["taxes"] = $tax = $ctx->mTaxModel->getTax(DEFAULT_TAX);
                } elseif (defined('DEFAULT_TAX') AND DEFAULT_TAX == -2) {
                    $p["taxes"] = json_decode(ConfigManager::getValue("MULTI_TAXES"), JSON_OBJECT_AS_ARRAY);
                }


                //register payment processor
                if(!isset( self::$payment_processors[$p['id']]) && isset($p['processorCallback'])){
                    self::$payment_processors[$p['id']] = $p['processorCallback'];
                    unset($p['processorCallback']);
                }

                self::$payments[$module][$k] = $p;
            }


            self::$redirections[$module] = $redirection;
            self::$success_callbacks[$module] = $payment_callback_success;
            self::$error_callbacks[$module] = $payment_callback_error;

        }
    }


    public static function plug_payment_method($module,$plug_payment, $first = false)
    {

        $payments = self::$payments[$module];

        if ($first) {
            $np[] = $plug_payment;
        }

        foreach ($payments as $p) {
            $np[] = $p;
        }

        if (!$first) {


            if (defined('DEFAULT_TAX') AND DEFAULT_TAX > 0) {
                $plug_payment["taxes"] = $tax = &get_instance()->mTaxModel->getTax(DEFAULT_TAX);
            } elseif (defined('DEFAULT_TAX') AND DEFAULT_TAX == -2) {
                $plug_payment["taxes"] = json_decode(ConfigManager::getValue("MULTI_TAXES"), JSON_OBJECT_AS_ARRAY);
            }


            $np[] = $plug_payment;
        }

        self::$payments[$module] = $np;

    }

    public static function replace($module, $payments, $redirection = "", $payment_callback_success = "", $payment_callback_error = "")
    {

        if (!isset(self::$payments[$module])) {
            self::$payments[$module] = $payments;

            $ctx = &get_instance();

            foreach (self::$payments[$module] as $k => $p) {

                if (defined('DEFAULT_TAX') AND DEFAULT_TAX > 0) {
                    $p["taxes"] = $tax = $ctx->mTaxModel->getTax(DEFAULT_TAX);
                } elseif (defined('DEFAULT_TAX') AND DEFAULT_TAX == -2) {
                    $p["taxes"] = json_decode(ConfigManager::getValue("MULTI_TAXES"), JSON_OBJECT_AS_ARRAY);
                }


                //register payment processor
                if(isset($p['processorCallback'])){
                    self::$payment_processors[$p['id']] = $p['processorCallback'];
                    unset($p['processorCallback']);
                }

                self::$payments[$module][$k] = $p;
            }


            self::$redirections[$module] = $redirection;
            self::$success_callbacks[$module] = $payment_callback_success;
            self::$error_callbacks[$module] = $payment_callback_error;

            self::$replaced[$module] = $module;
        }
    }


    public static function getModules()
    {

        $payment = PaymentsProvider::getAll();
        $list = array();

        foreach ($payment as $modules) {

            foreach ($modules as $provided_payments) {
                if (!isset($list[$provided_payments['id']])) {
                    $list[$provided_payments['id']] = array(
                        'payment' => $provided_payments['payment'],
                        'image' => $provided_payments['image'],
                        'id' => $provided_payments['id'],
                    );
                }
            }
        }

        return $list;
    }


    public static function isProvided($module, $payment)
    {

        if (isset(self::$payments[$module])) {
            if (self::isEnabled($payment))
                foreach (self::$payments[$module] as $m => $p) {
                    if (isset($p['payment']) && ($p['payment'] == $payment OR $p['id'] == $payment))
                        return TRUE;
                }
        }

        return FALSE;
    }


    public static function isEnabled($payment)
    {

        //METHOD_PAYMENTS_ENABLED_LIST
        $list = ConfigManager::getValue('METHOD_PAYMENTS_ENABLED_LIST');
        $list = json_decode($list, JSON_OBJECT_AS_ARRAY);

        if (in_array($payment, $list))
            return TRUE;

        if (!is_numeric($payment) && is_string($payment)) {
            if (isset(self::$codes[$payment]))
                $payment = self::$codes[$payment];
        }

        if (!empty($list) && in_array($payment, $list))
            return TRUE;


        return FALSE;
    }

    public static function getRedirection($module)
    {

        if (isset(self::$redirections[$module])) {
            return self::$redirections[$module];
        }

        return FALSE;
    }

    public static function getSuccessCallback($module)
    {

        if (isset(self::$success_callbacks[$module])) {
            return self::$success_callbacks[$module];
        }

        return FALSE;
    }


    public static function getPaymentProcessorCallback($pid)
    {

        if (isset(self::$payment_processors[$pid])) {
            return self::$payment_processors[$pid];
        }

        return NULL;
    }


    public static function getErrorCallback($module)
    {

        if (isset(self::$error_callbacks[$module])) {
            return self::$error_callbacks[$module];
        }

        return FALSE;
    }


}


function calculate_total_items($array)
{


}


class PaymentSubscription
{

    const PENDING = 0;
    const CONFIRMED = 1;
    const SUSPENDED = -1;


}


if (!function_exists('url_get_content')) {

    function url_get_content($url)
    {

        $arrContextOptions = array(
            "ssl" => array(
                "verify_peer" => false,
                "verify_peer_name" => false,
            ),
        );

        return file_get_contents($url, false, stream_context_create($arrContextOptions));
    }
}


    function payPalProcessorCallback(){

        $callback = function ($params, $inputs){
            $items = $params['articles'];
            $invoice = $params['invoice'];

            foreach ($items as $k => $item) {
                $params["items"][$k]["name"] = $item->item_name;
                $params["items"][$k]["quantity"] = $item->qty;
                $params["items"][$k]["price"] = $item->price;
                $params["items"][$k]["sku"] = $item->item_id;
                $params["items"][$k]["description"] = $item->item_name;
                $params["items"][$k]["currency"] = $invoice->currency;
            }

            Modules::run('payment/paypal/create_payment_with_paypal', $params);

            return TRUE;
        };

        return $callback;
    };


    function stripeProcessorCallback(){

        $callback = function ($params, $inputs){

            $ctx = &get_instance();

            $items = $params['articles'];
            $invoice = $params['invoice'];

            foreach ($items as $k => $item) {
                $params["items"][$k]["name"] = $item->item_name;
                $params["items"][$k]["quantity"] = $item->qty;
                $params["items"][$k]["price"] = $item->price;
                $params["items"][$k]["sku"] = $item->item_id;
                $params["items"][$k]["description"] = $item->item_name;
                $params["items"][$k]["currency"] = $invoice->currency;
            }

            $ctx->session->set_userdata(array(
                'payment_stripe_cart' => $params
            ));

            $ctx->load->view("payment/stripe/charge");

            return TRUE;
        };

        return $callback;
    };



    function codProcessorCallback(){

        $callback = function ($params, $inputs){

            $ctx = &get_instance();

            $items = $params['articles'];
            $invoice = $params['invoice'];

            $ctx->mPaymentModel->updateInvoiceCOD($invoice->id, "cod");

            if(isset($params['callback_success_url'])){
                $callback = $params['callback_success_url'];
            }else{
                $callback = PaymentsProvider::getSuccessCallback($invoice->module);
                $callback = $callback . '?invoiceid=' . $invoice->id . '&method=cod&key=' . $inputs['token'];
            }

            $result = Modules::run($invoice->module . '/payment_success', array(
                'invoiceId' => $invoice->id
            ));


            if ($result == TRUE) {
                redirect($callback);
            } else {
                $callback_error = PaymentsProvider::getErrorCallback($invoice->module);
                redirect($callback_error);
            }

            return TRUE;
        };

        return $callback;
    };


    function walletProcessorCallback(){

        $callback = function ($params, $inputs){

            $ctx = &get_instance();

            $items = $params['articles'];
            $invoice = $params['invoice'];

            $callback = PaymentsProvider::getSuccessCallback($invoice->module);
            $callback = $callback . '?invoiceid=' .  $invoice->id . '&method=wallet&key=' . $inputs['token'];

            //check if has balance first
            $invoice = $ctx->mPaymentModel->getInvoice($invoice->id);
            if(!$ctx->mWalletModel->hasBalance(SessionManager::getData("id_user"), $invoice->amount)){
                $callback_error = PaymentsProvider::getErrorCallback($invoice->module);
                redirect($callback_error."?error=29782");return;
            }

            if($invoice->module=="order_payment"){
                $result = $ctx->mWalletModel->releaseBalanceTransaction(
                    SessionManager::getData("id_user"),
                    $invoice->amount,
                    "Order payment #".str_pad($invoice->module_id, 6, 0, STR_PAD_LEFT)
                );
            }else  if($invoice->module=="booking_payment"){
                $result = $ctx->mWalletModel->releaseBalanceTransaction(
                    SessionManager::getData("id_user"),
                    $invoice->amount,
                    "Booking payment #". str_pad($invoice->module_id, 6, 0, STR_PAD_LEFT)
                );
            }else  if($invoice->module=="pack"){

                $items = json_decode($invoice->items,JSON_OBJECT_AS_ARRAY);
                $itemName = $items[0]['item_name'];

                $result = $ctx->mWalletModel->releaseBalanceTransaction(
                    SessionManager::getData("id_user"),
                    $invoice->amount,
                    "Subscription payment: ".$itemName
                );

            }else{
                $result = $ctx->mWalletModel->releaseBalanceTransaction(
                    SessionManager::getData("id_user"),
                    $invoice->amount
                );
            }

            if ($result) {
                $ctx->mPaymentModel->updateInvoice($invoice->id, "wallet", "wallet:" .$result, $inputs['token']);
                redirect($callback);
            } else {
                $callback_error = PaymentsProvider::getErrorCallback($invoice->module);
                redirect($callback_error);
            }

            return TRUE;
        };

        return $callback;
    };



    function razorpayProcessorCallback(){

        $callback = function ($params, $inputs){

            $ctx = &get_instance();


            $items = $params['articles'];
            $invoice = $params['invoice'];

            foreach ($items as $k => $item) {

                if (isset($tax))
                    $tax_value = ($tax['value'] / 100) * $item->price;
                else if (isset($multiTaxes) && $multiTaxes > 0)
                    $tax_value = $multiTaxes;
                else
                    $tax_value = 0;

                $params["items"][$k]["name"] = $item->item_name;
                $params["items"][$k]["quantity"] = $item->qty;
                $params["items"][$k]["price"] = $item->price + $tax_value;
                $params["items"][$k]["sku"] = $item->item_id;
                $params["items"][$k]["description"] = $item->item_name;
                $params["items"][$k]["currency"] = $invoice->currency;
            }

            $ctx->session->set_userdata(array(
                'payment_razorpay_cart' => $params
            ));


            $callback = PaymentsProvider::getSuccessCallback($invoice->module);
            $params['callback_success_url'] = $callback . '?invoiceid=' . $invoice->id . '&method=cod&key=' . $inputs['token'];

            echo Modules::run("payment/razorpay/create_order", $params);

            return TRUE;
        };

        return $callback;
    };


    function flutterwaveProcessorCallback(){

        $callback = function ($params, $inputs){

            $ctx = &get_instance();

            $items = $params['articles'];
            $invoice = $params['invoice'];

            foreach ($items as $k => $item) {

                if (isset($tax))
                    $tax_value = ($tax['value'] / 100) * $item->price;
                else if (isset($multiTaxes) && $multiTaxes > 0)
                    $tax_value = $multiTaxes;
                else
                    $tax_value = 0;

                $params["items"][$k]["name"] = $item->item_name;
                $params["items"][$k]["quantity"] = $item->qty;
                $params["items"][$k]["price"] = $item->price + $tax_value;
                $params["items"][$k]["sku"] = $item->item_id;
                $params["items"][$k]["description"] = $item->item_name;
                $params["items"][$k]["currency"] = $invoice->currency;
            }

            $ctx->session->set_userdata(array(
                'payment_flutterwave_cart' => $params
            ));



            $callback = PaymentsProvider::getSuccessCallback($invoice->module);
            $params['callback_success_url'] = $callback . '?invoiceid=' .  $invoice->id . '&method=cod&key=' . $inputs['token'];


            $_SESSION['payable_amount'] = $params['details_subtotal'];
            $_SESSION['callback_success_url'] = $params['callback_success_url'];
            $_SESSION['callback_error_url'] = $params['callback_error_url'];


            $ctx->load->view("payment/flutterwave/charge",$params);

            return TRUE;
        };

        return $callback;
    };


    function hyperpayProcessorCallback(){

        $callback = function ($params, $inputs){

            $ctx = &get_instance();


            $items = $params['articles'];
            $invoice = $params['invoice'];

            foreach ($items as $k => $item) {

                if (isset($tax))
                    $tax_value = ($tax['value'] / 100) * $item->price;
                else if (isset($multiTaxes) && $multiTaxes > 0)
                    $tax_value = $multiTaxes;
                else
                    $tax_value = 0;

                $params["items"][$k]["name"] = $item->item_name;
                $params["items"][$k]["quantity"] = $item->qty;
                $params["items"][$k]["price"] = $item->price + $tax_value;
                $params["items"][$k]["sku"] = $item->item_id;
                $params["items"][$k]["description"] = $item->item_name;
                $params["items"][$k]["currency"] = $invoice->currency;
            }

            $ctx->session->set_userdata(array(
                'payment_hyperpay_cart' => $params
            ));



            $callback = PaymentsProvider::getSuccessCallback($invoice->module);
            $params['callback_success_url'] = $callback . '?invoiceid=' . $invoice->id . '&method=cod&key=' . $inputs['token'];


            $_SESSION['payable_amount'] = $params['details_subtotal'];
            $_SESSION['callback_success_url'] = $params['callback_success_url'];
            $_SESSION['callback_error_url'] = $params['callback_error_url'];

            $result = Modules::run("payment/hyperpay/hyperpay_request",$params['details_subtotal']);

            $result = json_decode($result,JSON_OBJECT_AS_ARRAY);

            $params['ndc'] = $result['ndc'];
            $params['id'] = $result['id'];

            $ctx->load->view("payment/hyperpay/charge",$params);

            return TRUE;
        };

        return $callback;
    };


    function paytmProcessorCallback(){

        $callback = function ($params, $inputs){

            $ctx = &get_instance();


            $items = $params['articles'];
            $invoice = $params['invoice'];

            foreach ($items as $k => $item) {

                if (isset($tax))
                    $tax_value = ($tax['value'] / 100) * $item->price;
                else if (isset($multiTaxes) && $multiTaxes > 0)
                    $tax_value = $multiTaxes;
                else
                    $tax_value = 0;

                $params["items"][$k]["name"] = $item->item_name;
                $params["items"][$k]["quantity"] = $item->qty;
                $params["items"][$k]["price"] = $item->price + $tax_value;
                $params["items"][$k]["sku"] = $item->item_id;
                $params["items"][$k]["description"] = $item->item_name;
                $params["items"][$k]["currency"] = $invoice->currency;
            }

            $ctx->session->set_userdata(array(
                'payment_paytm_cart' => $params
            ));

            $callback = PaymentsProvider::getSuccessCallback($invoice->module);
            $callback = $callback . '?invoiceid=' .  $invoice->id . '&method=cod&key=' . $inputs['token'];

            $params['callback'] = $callback . '?invoiceid=' . $invoice->id . '&method=cod&key=' . $inputs['token'];
            $params['amount'] = $params['details_subtotal'];
            $params['currency'] = ConfigManager::getValue("DEFAULT_CURRENCY");
            $params['order_id'] = rand(1000000,999999999);

            $result = Modules::run("payment/paytm/paytm_request",$params);
            $result = json_decode($result,JSON_OBJECT_AS_ARRAY);


            if(!isset($result['body']['txnToken'])){
                $ctx->payment_error();
                return;
            }


            $params['txnToken'] = $result['body']['txnToken'];


            $_SESSION['payable_amount'] = $params['details_subtotal'];
            $_SESSION['callback_success_url'] = $params['callback'];
            $_SESSION['callback_error_url'] = $params['callback_error_url'];

            AdminTemplateManager::addCssLibs(
                "https://securegw-stage.paytm.in//merchantpgpui/checkoutjs/merchants/".ConfigManager::getValue('PAYTM_KEY_ID').".js"
            );

            $ctx->load->view("payment/paytm/charge",$params);

            return TRUE;
        };

        return $callback;
    };



    function bankTransferProcessorCallback(){

        $callback = function ($params, $inputs){

            $ctx = &get_instance();


            $items = $params['articles'];
            $invoice = $params['invoice'];

            $ctx->mPaymentModel->updateInvoiceCOD($invoice->id, "transferBank");
            $ctx->mPaymentModel->sendBankInformationEmail($invoice->id);
            $ctx->mPaymentModel->sendBankInformationChat($invoice->id);


            if(isset($params['callback_success_url'])){
                $callback = $params['callback_success_url'];
            }else{
                $callback = PaymentsProvider::getSuccessCallback($invoice->module);
                $callback = $callback . '?invoiceid=' .  $invoice->id . '&method=cod&key=' . $inputs['token'];
            }

            $result = Modules::run($invoice->module . '/payment_success', array(
                'invoiceId' => $invoice->id
            ));

            if ($result == TRUE) {
                redirect($callback);
            } else {
                $callback_error = PaymentsProvider::getErrorCallback($invoice->module);
                redirect($callback_error);
            }
            return TRUE;
        };

        return $callback;
    };

    function paystackProcessorCallback(){

        $callback = function ($params, $inputs){

            $ctx = &get_instance();

            $items = $params['articles'];
            $invoice = $params['invoice'];

            foreach ($items as $k => $item) {

                if (isset($tax))
                    $tax_value = ($tax['value'] / 100) * $item->price;
                else if (isset($multiTaxes) && $multiTaxes > 0)
                    $tax_value = $multiTaxes;
                else
                    $tax_value = 0;

                $params["items"][$k]["name"] = $item->item_name;
                $params["items"][$k]["quantity"] = $item->qty;
                $params["items"][$k]["price"] = $item->price + $tax_value;
                $params["items"][$k]["sku"] = $item->item_id;
                $params["items"][$k]["description"] = $item->item_name;
                $params["items"][$k]["currency"] = $invoice->currency;

            }

            $params['client']['email'] = SessionManager::getData('email');

            $ctx->session->set_userdata(array(
                'payment_paystack_cart' => $params
            ));

            $ctx->load->view("payment/paystack/charge",$params);

            return TRUE;
        };

        return $callback;
    };


    function mercadopagoProcessorCallback(){

        $callback = function ($params, $inputs){


            $ctx = &get_instance();

            $items = $params['articles'];
            $invoice = $params['invoice'];

            foreach ($items as $k => $item) {

                if (isset($tax))
                    $tax_value = ($tax['value'] / 100) * $item->price;
                else if (isset($multiTaxes) && $multiTaxes > 0)
                    $tax_value = $multiTaxes;
                else
                    $tax_value = 0;

                $params["items"][$k]["name"] = $item->item_name;
                $params["items"][$k]["quantity"] = $item->qty;
                $params["items"][$k]["price"] = $item->price + $tax_value;
                $params["items"][$k]["sku"] = $item->item_id;
                $params["items"][$k]["description"] = $item->item_name;
                $params["items"][$k]["currency"] = $invoice->currency;

            }

            $params['client']['email'] = SessionManager::getData('email');

            $ctx->session->set_userdata(array(
                'payment_mercadopago_cart' => $params
            ));

            $ctx->load->view("payment/mercadopago/charge",$params);
            return TRUE;
        };

        return $callback;
    };