<?php

namespace Hura8\Components\Customer\Model;

use ClientExtend\UserLoyaltyPointCalculation;
use Hura8\Database\iConnectDB;
use Hura8\Interfaces\TableName;

class CustomerLoyaltyModel
{

    /* @var iConnectDB $db */
    protected  $db;

    protected $tb_point    = TableName::CUSTOMER_POINT; // "idv_customer_point";
    protected $tb_customer = TableName::CUSTOMER; // "idv_customer";

    public static $POINT_NAME = 'điểm';

    protected $point_setting = [];
    protected $point_setting_config_file = ROOT_DIR . "/config/build/customer_point.php";

    protected $level_setting = [];
    protected $level_setting_config_file = ROOT_DIR . "/config/client/customer_level.php";

    protected $level_by = '';// point|total_purchase_value as set by constant CHANGE_CUSTOMER_LEVEL_BY

    /* @var $objUserLoyaltyPointCalculation UserLoyaltyPointCalculation */
    protected $objUserLoyaltyPointCalculation;


    public function __construct()
    {
        $this->db = get_db("", ENABLE_DB_DEBUG);

        // import settings
        //$new_info_file = "../config/build/customer_point.php" ;
        //$config_file = ROOT_DIR . "/config/build/customer_point.php";
        if(@file_exists($this->point_setting_config_file)) {
            $this->point_setting = include $this->point_setting_config_file;
        }

        // customer level based on point gain
        if( defined("ENABLE_CUSTOMER_POINT") && ENABLE_CUSTOMER_POINT ) {
            if(@file_exists($this->level_setting_config_file)) {
                $this->level_setting = include $this->level_setting_config_file;
            }
        }

        // default is point
        $this->level_by = (defined('CHANGE_CUSTOMER_LEVEL_BY')) ? CHANGE_CUSTOMER_LEVEL_BY : 'point';

        $this->objUserLoyaltyPointCalculation = new UserLoyaltyPointCalculation($this->point_setting);
    }


    public function getPointSettingConfigFile() {
        return $this->point_setting_config_file;
    }

    public function getPointSetting() {
        return $this->point_setting;
    }

    public function getLevelSetting(){
        return $this->level_setting;
    }

    // show estimate cart point
    public function getEstimateCartPoint($cart_value){
        $conversion_rate = (isset($this->point_setting['reward']['buy']['rate'])) ? $this->point_setting['reward']['buy']['rate'] : 0;
        return ($conversion_rate) ? round($cart_value / $conversion_rate) : 0;
    }

    public function getUserPoint($user_id, array $condition, $return_type)
    {

        if($return_type == "total") {
            //Lay tong so
            $query = $this->db->runQuery("SELECT COUNT(*) AS total FROM `". $this->tb_point ."` WHERE `customer_id` = ? " , ['d'], [$user_id]);
            if($resultTotal = $this->db->fetchAssoc($query)){
                return $resultTotal['total'];
            }
            return 0;

        } else {

            $numPerPage = (isset($condition['numPerPage'])) ? intval($condition['numPerPage']) : 50;
            $page = getPageId();

            $query = $this->db->runQuery("
							SELECT * FROM `". $this->tb_point ."`
							WHERE `customer_id` = ?
							ORDER BY id DESC
							LIMIT ".($page - 1) * $numPerPage .", ".$numPerPage." " , ['d'], [$user_id]);

            $result =  array();
            $i = ($page - 1) * $numPerPage;
            foreach ( $this->db->fetchAll($query) as $rs){
                $i++;
                $rs['counter']	= $i;
                $rs['activity_type_name'] = (isset($this->point_setting[$rs['operation']][$rs['activity_type']])) ? $this->point_setting[$rs['operation']][$rs['activity_type']]['name'] : '--';
                $result[] = $rs;
            }

            return $result;
        }

    }


    public function usePoint($user_id, $use_point, $activity_type, $activity_type_tracker, $reason = '', $point_args = ['order_value' => 0]){
        // no user or no config
        if(!$user_id || !ENABLE_CUSTOMER_POINT) return false;

        $result = $this->objUserLoyaltyPointCalculation->calculateUsePoint($user_id, $use_point, $activity_type, $activity_type_tracker, $point_args);

        $this->pointOp('use', $user_id, $result['use_point'], $activity_type, $activity_type_tracker, $reason);

        return $result;
    }


    public function rewardPoint($user_id, $activity_type, $activity_type_tracker, $reason = '', $point_args = ['order_id' => 0]){
        // no user or no config
        if(!$user_id || !ENABLE_CUSTOMER_POINT) return false;

        $point = $this->objUserLoyaltyPointCalculation->calculateRewardPoint($user_id, $activity_type, $activity_type_tracker, $point_args);

        $this->pointOp('reward', $user_id, $point, $activity_type, $activity_type_tracker, $reason);

        return $point;
    }


    // $operation: reward|use
    // $change_point: positive (reward) or nagative (use)
    protected function pointOp($operation, $user_id, $change_point, $activity_type, $activity_type_tracker, $reason = '') {

        if(!$change_point) return false;

        $reason_prefix = ($operation == 'use') ? 'Sử dụng' : 'Thưởng';
        if($activity_type == 'return') $reason_prefix = 'Hoàn lại';
        $full_reason = join(" ", [$reason_prefix, $change_point, static::$POINT_NAME, ":", $reason]);

        if($operation == 'use') $change_point = -1 * $change_point;

        // security: hash the row to avoid editing point directly in the database
        $hash_value = sha1(join(".", [
            $operation,
            $user_id,
            $change_point,
            $activity_type,
            $activity_type_tracker,
            CURRENT_TIME,
            'ass@ss'
        ]));

        $new_id =  $this->db->insert(
            $this->tb_point ,
            [
                'customer_id' => $user_id ,
                'activity_type' => $activity_type,
                'activity_type_tracker' => $activity_type_tracker ,
                'operation' => $operation,
                'point' => $change_point,
                'create_time' => CURRENT_TIME,
                'reason' => substr($full_reason, 0, 200),
                'referer_url' => substr(REFERER_URL, 0, 150) ,
                'hash_value' => $hash_value ,
            ]
        );

        //update user reward balance
        if($new_id) {
            $this->updateStat($user_id, $change_point);
        }

        return $new_id;
    }


    public function updateStat($user_id, $changed_point, $changed_order_value=0) {
        $user_id = intval($user_id);

        $query = $this->db->runQuery("SELECT 
                                                `loyalty_point`,
                                                `loyalty_level`,
                                                `total_value_success` 
                                            FROM ".$this->tb_customer." 
                                            WHERE `id` = ?
                                            LIMIT 1 " , ['d'], [$user_id]);

        if($current = $this->db->fetchAssoc($query)){
            $new_point  = $current['loyalty_point'] + $changed_point;
            $new_purchase_value = $current['total_value_success'] + $changed_order_value;

            $level = $current['loyalty_level'];
            if($this->level_by == 'point' && $changed_point != 0) $level = $this->calculateLevelByPoint($new_point);
            else if($changed_order_value) $level = $this->calculateLevelByOrderValue($new_purchase_value);

            $this->db->update(
                $this->tb_customer ,
                [
                    'loyalty_point' => $new_point,
                    'loyalty_level' => $level,
                    'total_value_success' => $new_purchase_value,
                ],
                [
                    'id' => $user_id,
                ],
                1
            );
        }

        return true;
    }


    private function calculateLevelByPoint($point) {
        //if the point in between  -> return the lowest level
        $all_level = array_keys($this->level_setting);

        foreach ( $all_level as $level) {
            $next_level = $level + 1;
            if(!in_array($next_level, $all_level)) $next_level = 0;

            if($next_level) {
                if( $point >= $this->level_setting[$level]["point_require"]
                    && $point < $this->level_setting[$next_level]["point_require"] ) {
                    return $level;
                }
            }else{
                if($point >= $this->level_setting[$level]["point_require"]) {
                    return $level;
                }
            }
        }

        return 0;
    }

    private function calculateLevelByOrderValue($aggregate_purchase_value = 0) {

        //if the point in between  -> return the lowest level
        $all_level = array_keys($this->level_setting);

        //tinh hang thanh vien theo so tien tich luy
        foreach ( $all_level as $level ) {
            $next_level = $level + 1;
            if(!in_array($next_level, $all_level)) $next_level = 0;

            if($next_level) {
                if( $aggregate_purchase_value >= $this->level_setting[$level]["total_order_value"] &&
                    $aggregate_purchase_value < $this->level_setting[$next_level]["total_order_value"]
                ) {
                    return $level;
                }
            }else{
                if( $aggregate_purchase_value >= $this->level_setting[$level]["total_order_value"]) {
                    return $level;
                }
            }
        }

        return 0;
    }
}
