<?php

namespace Salesloo;

if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly.
}

use WP_Error;

/**
 * Checkout classes
 */
class Checkout
{
    private $data = [];

    private static $instance =  null;

    public $unique_number = null;

    /**
     * Instance.
     *
     * Ensures only one instance of the renew class is loaded or can be loaded.
     *
     * @since 1.0.0
     * @access public
     */
    public static function instance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    public function prepare()
    {
        $user = false;
        $guest_cookie_name = 'salesloo_guest';

        if (is_user_logged_in()) {
            $user = wp_get_current_user()->user_login;
        } else {
            if (isset($_COOKIE[$guest_cookie_name]) && $_COOKIE[$guest_cookie_name]) {
                $user = sanitize_text_field($_COOKIE[$guest_cookie_name]);
            } else {
                $guest_number = get_option('salesloo_guest_user_number', 0);
                $guest_number = intval($guest_number) + 1;

                $user = 'guest' . $guest_number;
                update_option('salesloo_guest_user_number', $guest_number);

                salesloo_set_cookie($guest_cookie_name, $user);
            }
        }

        $checkout = Models\Checkout::query('WHERE user = %s', $user)->first();
        if (empty($checkout->ID) || $checkout->ID <= 0) {
            $args = [
                'user' => $user,
            ];
            $checkout_id = Models\Checkout::data($args)->create();
            $checkout = Models\Checkout::query('WHERE ID = %d', $checkout_id)->first();
        }

        Models\Checkout::data(['updated_at' => date('Y-m-d H:i:s')])->update(['ID' => $checkout->ID]);

        $this->data = [
            'ID' => $checkout->ID,
            'user' => $checkout->user,
            'created_at' => $checkout->created_at,
            'updated_at' => $checkout->updated_at
        ];

        /**
         * set affiliate id if exists on cookie
         */
        $affiliate_username = salesloo_get_current_affiliate_cookie();
        if ($affiliate_username) {
            if ($affiliate_username == $checkout->user) {
                salesloo_delete_current_affiliate_cookie();
            } else {
                $affiliate = get_user_by('login', $affiliate_username);
                if ($affiliate) {
                    $this->update_meta('affiliate_id', $affiliate->ID);
                }
            }
        }

        /**
         * set coupon code if exists on cookie
         */
        $coupon_code = salesloo_get_current_coupon_cookie();
        if (isset($_GET['coupon'])) {
            $coupon_code = sanitize_text_field($_GET['coupon']);
        }

        if ($coupon_code) {
            $this->update_meta('coupon_code', $coupon_code);
        }


        $this->data = wp_parse_args($checkout->meta(), $this->data);
        self::$instance = $this;
    }

    /**
     * getter
     *
     * @param  string $name
     * @return mixed
     */
    public function __get($name)
    {
        if (array_key_exists($name, $this->data))
            return maybe_unserialize($this->data[$name]);

        return NULL;
    }

    /**
     * update_meta
     *
     * 
     * update current checkout meta
     * @param  string $meta_key
     * @param  mixed $meta_value
     * @return mixed
     */
    private function update_meta($meta_key, $meta_value)
    {
        $args = [
            'checkout_id' => $this->ID,
            'meta_key'    => wp_unslash($meta_key),
            'meta_value'  => wp_unslash($meta_value)
        ];

        /**
         * check if meta key is exists
         */
        $check_meta = Models\Checkout_Meta::query('WHERE checkout_id = %d AND meta_key = %s', $this->ID, $meta_key)->first();

        /**
         * update if meta key exists otherwise insert new one.
         */
        if ($check_meta->meta_id > 0) {

            Models\Checkout_Meta::data($args)->update([
                'meta_id' => $check_meta->meta_id
            ]);
            $checkout_meta_id = $check_meta->meta_id;
        } else {
            $checkout_meta_id = Models\Checkout_Meta::data($args)->create();
        }

        return $checkout_meta_id;
    }

    /**
     * delete_meta
     *
     * @param  string $meta_key
     * @param  mixed $meta_value
     * @return mixed
     */
    public function delete_meta($meta_key, $meta_value = '')
    {
        $args = [
            'checkout_id' => $this->ID,
            'meta_key' => wp_unslash($meta_key)
        ];

        if ($meta_value) {
            $args['meta_value'] = wp_unslash($meta_value);
        }

        return Models\Checkout_Meta::delete($args);
    }


    /**
     * choose_payment_method
     *
     * @param  string $method_name
     * @param  mixed $checkout_id
     * @return mixed
     */
    public function choose_payment_method($method_name, $checkout_id = false)
    {
        if (false === $checkout_id) {
            $checkout_id = $this->ID;
        }

        $payment_method = Plugin::instance()->payment_method->get($method_name);

        $updated = $this->update_meta('payment_method', $payment_method->get_id());

        return $updated;
    }

    /**
     * apply_coupon
     *
     * @param  string $code
     * @return mixed
     */
    public function apply_coupon($code)
    {
        $products = Cart::prepare()->get_items();
        $coupon_valid = false;

        if (!$code || $code == '%%') {
            $this->update_meta('coupon_code', '');
            salesloo_delete_current_coupon_cookie();

            return true;
        }

        foreach ($products as $product) {

            $coupon = salesloo_check_coupon($code, $product->ID);
            if ($coupon) {
                $this->update_meta('coupon_code', $code);
                //salesloo_set_current_coupon_cookie($code);
                if ($coupon->user_id) {
                    $affiliate = get_userdata($coupon->user_id);
                    if ($affiliate) {
                        if (!is_user_logged_in() || is_user_logged_in() && get_current_user_id() != $affiliate->ID) {
                            $this->update_meta('affiliate_id', $affiliate->ID);
                            salesloo_set_affiliate_cookie($affiliate->user_login);
                        }
                    }
                }

                $coupon_valid = true;

                break;
            } else {
                $this->update_meta('coupon_code', '');
            }
        }

        salesloo_delete_current_coupon_cookie();

        if ($coupon_valid) {
            return true;
        } else {
            return new WP_Error('error', __('Coupon code not found, expired or has exceeded usage, Please try with another code', 'salesloo'));
        }
    }

    /**
     * summary
     *
     * @return array
     */
    public function summary()
    {
        $items = [];
        $subtotal = 0;
        $additional_calculations = [];
        $payment_method = null;

        if ($this->payment_method) {
            $payment_method = Plugin::instance()->payment_method->get($this->payment_method);
        }

        $coupon_code = salesloo_get_checkout_meta($this->ID, 'coupon_code', true);

        foreach (Cart::prepare()->get_items() as $product) {

            $product_price = $product->get_price();

            $items[$product->ID] = [
                'label' => $product->title,
                'value' => salesloo_convert_price($product_price),
                'print' => salesloo_convert_money($product_price),
                'price' => [
                    'value' => salesloo_convert_price($product_price),
                    'print' => salesloo_convert_money($product_price)
                ],
                'duration' => salesloo_translate_product_duration($product->duration)
            ];

            $rebate = 0;

            if ($coupon_code) {

                $coupon = salesloo_check_coupon($coupon_code, $product->ID);

                if ($coupon) {
                    $raw_rebate = $coupon->get_raw_rebate();
                    if ('percen' == $raw_rebate['type']) {
                        $rebate = floatval($raw_rebate['value']) * floatval($product_price);
                        $rebate = $rebate / 100;
                    } else {
                        $rebate = $raw_rebate['value'];
                    }

                    $this->update_meta('coupon_id_product_' . $product->ID, $coupon->ID);

                    $rebate = salesloo_convert_price($rebate);

                    $additional_calculations['rebate-' . $product->ID]   = [
                        'label'     => __('Discount', 'salesloo'),
                        'value'     => $rebate,
                        'print'     => '-' . salesloo_convert_money($rebate),
                        'message'   => sprintf(__('Discount for product %s, code: %s', 'salesloo'), $product->title, $coupon->code),
                        'operation' => '-'
                    ];
                }
            }

            if ($product->affiliate == 1) {
                $product_price_after_discount = floatval($product_price) - floatval($rebate);
                $raw_commission = $product->get_raw_affiliate_commission();
                if ('percen' == $raw_commission['type']) {
                    $commission = floatval($raw_commission['value']) * $product_price_after_discount;
                    $commission = $commission / 100;
                } else {
                    $commission = $raw_commission['value'];
                }
                $items[$product->ID]['commission'] = [
                    'value' => $commission,
                    'print' => salesloo_convert_money($commission)
                ];
            } else {
                $items[$product->ID]['commission'] = false;
            }

            $subtotal = $subtotal + floatval($product_price);
        }

        $subtotal = salesloo_convert_price($subtotal);

        if ($payment_method && $payment_method->use_unique_number()) {
            if (!$this->unique_number) {
                $this->unique_number = salesloo_generate_unique_number();
                $this->update_meta('unique_number', $this->unique_number);
            }

            $unique_number = salesloo_convert_price($this->unique_number);
            $additional_calculations['unique']  = [
                'label'     => salesloo_get_option('unique_number_label'),
                'value'     => $unique_number,
                'print'     => salesloo_get_option('unique_number_operation') . salesloo_convert_money($unique_number),
                'message'   => '',
                'operation' => salesloo_get_option('unique_number_operation')
            ];
        }

        $first_item = reset($items);

        $summary = [
            'products' => $items,
            'subtotal' => [
                'label'   => $first_item['label'],
                'value'   => $subtotal,
                'print'   => salesloo_convert_money($subtotal),
                'message' => $first_item['duration'],
            ]
        ];

        $additional_calculations = apply_filters('salesloo/summary', $additional_calculations, $this);

        $total = $subtotal;
        foreach ($additional_calculations as $key => $val) {
            if ($val['operation'] == '+') {
                $total = $total + salesloo_convert_price($val['value']);
            } else {
                $minus = salesloo_convert_price($val['value']);
                if ($total >= $minus) {
                    $total = $total - salesloo_convert_price($val['value']);
                } else {
                    unset($additional_calculations[$key]);
                }
            }
        }

        $total_message = '';
        $converted = [];
        $default_currency = salesloo_get_option('currency');
        if ($payment_method && $default_currency != $payment_method->get_currency()) {
            $total_message = sprintf(
                __('%s Converted to %s with rate %s/%s 1'),
                salesloo_convert_money($total),
                $payment_method->get_currency(),
                $payment_method->get_currency_rate(),
                $default_currency
            );

            $rate = str_replace(',', '.', $payment_method->get_currency_rate());
            $rate = floatval($rate);

            if ($rate) {
                $converted_total = $total * $rate;
            } else {
                $converted_total = $total;
            }

            $converted = [
                'value'    => salesloo_convert_price($converted_total),
                'print'    => salesloo_convert_money($converted_total, $payment_method->get_currency_symbol()),
                'currency' => $payment_method->get_currency_symbol()
            ];
        }

        $total = salesloo_convert_price($total);

        $summary = wp_parse_args($additional_calculations, $summary);

        $summary['total'] = [
            'label'     => __('Total', 'salesloo'),
            'value'     => $total,
            'print'     => salesloo_convert_money($total),
            'message'   => $total_message,
            'converted' => $converted
        ];

        return $summary;
    }

    public function order()
    {
        global $___salesloo;

        if ('POST' != $_SERVER['REQUEST_METHOD']) return;
        if (!isset($_POST['__nonce']) || !isset($_POST['_wp_http_referer'])) return;

        if (strpos($_POST['_wp_http_referer'], '/checkout/') === false) return;
        if (wp_verify_nonce($_POST['__nonce'], 'salesloo-checkout')) {

            if (NULL == $this->payment_method) {
                return $___salesloo['warning'] = __('Please choose payment method', 'salesloo');
            }

            if (is_user_logged_in()) {
                $user_id = get_current_user_id();
            } else {

                $user_id = User::register($_POST['user']);
                if (is_wp_error($user_id)) {
                    return $___salesloo['warning'] = $user_id->get_error_message();
                }
            }

            do_action('salesloo/checkout/before', $this->ID, $user_id);

            $summary = $this->summary();

            $invoice_args = [
                'number'         => '',
                'user_id'        => $user_id,
                'summary'        => $summary,
                'total'          => floatval($summary['total']['value']),
                'payment_method' => $this->payment_method,
                'status'         => 'unpaid',
                'due_date_at'    => salesloo_set_invoice_due_date(),
            ];

            $invoice_id = salesloo_insert_invoice($invoice_args);

            if (is_wp_error($invoice_id)) {
                return $___salesloo['warning'] = $invoice_id->get_error_message();
            }

            do_action('salesloo/insert/invoice/after', $invoice_id);

            $items = $summary['products'];

            $orders = [];
            foreach ($items as $product_id => $item) {
                $product = salesloo_get_product($product_id);

                $price = $product->get_price();

                $raw_commission = $product->get_raw_affiliate_commission();
                if ('percen' == $raw_commission['type']) {
                    $commission = floatval($raw_commission['value']) * $price;
                    $commission = $commission / 100;
                } else {
                    $commission = $raw_commission['value'];
                }

                $order_args = [
                    'code'                 => salesloo_generate_order_code(),
                    'user_id'              => $user_id,
                    'product_id'           => $product_id,
                ];

                do_action('salesloo/insert/order/before', $order_args);
                $order_id = salesloo_insert_order($order_args);

                if (!is_wp_error($order_id)) {
                    $affiliate_id = $this->affiliate_id;
                    salesloo_add_order_meta($order_id, 'affiliate_id', $affiliate_id);

                    $renew_price = salesloo_convert_price($product->get_price('renew'));
                    salesloo_add_order_meta($order_id, 'renew_price', $renew_price);

                    $license_limit = intval($product->meta('license_limit'));
                    salesloo_add_order_meta($order_id, 'license_limit', $license_limit);

                    $duration = $product->duration ? $product->duration : 'onetime';
                    salesloo_add_order_meta($order_id, 'duration', $duration);

                    do_action('salesloo/insert/order/after', $order_args, $order_id);

                    $orders[] = $order_id;

                    if ($affiliate_id && $product->affiliate == 1) {
                        if (isset($item['commission']['value']) && $item['commission']['value']) {
                            $commission = floatval($item['commission']['value']);
                        }
                        $commission_args = [
                            'user_id'    => $affiliate_id,
                            'invoice_id' => $invoice_id,
                            'order_id'   => $order_id,
                            'product_id' => $product->ID,
                            'amount'     => $commission,
                            'status'     => 'pending',
                            'note'       => sprintf(__('Commission for product sales %s', 'salesloo'), $product->title)
                        ];

                        salesloo_insert_commission($commission_args);
                    }

                    $coupon_id = salesloo_get_checkout_meta($this->ID, 'coupon_id_product_' . $product->ID, true);

                    if ($affiliate_id && $coupon_id) {
                        $query = Models\Coupon_Code::query('WHERE coupon_id = %d AND user_id = %d', $coupon_id, $affiliate_id)->first();
                        if ($query->code_id > 0) {
                            salesloo_insert_coupon_usage($coupon_id, $query->code_id, $invoice_id);
                        }
                    }
                }
            }

            $update_invoice_args = [
                'ID' => $invoice_id,
                'number' => salesloo_generate_invoice_format($invoice_id),
                'orders' => $orders
            ];

            salesloo_update_invoice($update_invoice_args);

            $customer = get_userdata($user_id);
            $encoded_invoice_id = salesloo_encrypt($invoice_id);

            unset($summary['products']);

            $data = [
                'invoice_id'     => $invoice_id,
                'number'         => $update_invoice_args['number'],
                'payment_method' => $this->payment_method,
                'products'       => $items,
                'summary'        => $summary,
                'due_date'       => $invoice_args['due_date_at'],
                'customer_name'  => salesloo_user_get_name($customer),
                'customer_email' => $customer->user_email,
                'status'         => 'unpaid',
                'payment_url'    => salesloo_url_payment($encoded_invoice_id),
                'invoice_url'    => admin_url('admin.php?page=salesloo-payment')
            ];

            salesloo_add_notification('place_order', $data);
            salesloo_add_event('place_order', $data);

            /**
             * cleanest all currenct checkout data
             */

            $this->clean();

            wp_redirect(salesloo_url_payment($encoded_invoice_id));
            exit;
        }
    }

    public function clean()
    {
        salesloo_delete_checkout($this->ID);
        salesloo_delete_current_coupon_cookie();
        salesloo_delete_current_affiliate_cookie();
    }

    public function get_warning()
    {
        global $___salesloo;

        if (isset($___salesloo['warning']) && $___salesloo['warning']) {
            $warning = sanitize_text_field($___salesloo['warning']);
            $warning = str_replace('\r', '', $warning);
            $warning = str_replace('\n', '', $warning);
            return $warning;
        }

        return false;
    }
}
