<?php

namespace Hura8\System\Model;


use Hura8\Interfaces\AppResponse;
use Hura8\System\Security;
use Hura8\System\Security\DataType;


abstract class aCategoryBaseModel extends aEntityBaseModel
{

    public function __construct($entity_type, $tb_name = "") {
        parent::__construct($entity_type, $tb_name);
    }


    protected function extendedFilterOptions() : array
    {
        return [
            // empty for now
        ];
    }


    // get all categories and group by parents
    public function getAllByParent(array $condition = array()) : array
    {
        $item_list = array(); // parentId=>array(childId)
        foreach ( $this->getAll($condition) as $item ) {
            $item_list[$item['parent_id']][$item['id']] = $item;
        }

        return $item_list;
    }


    // get all categories
    public function getAll(array $condition = array()){
        /*$condition = [
            "parent_id" => 1,
            "status" => 1,
        ];*/

        $bind_types =   [];
        $bind_values =   [];
        $where_clause = '';

        if(isset($condition['status'])) {
            $where_clause .= " AND `status` = ? ";
            $bind_types[] = 'd';
            $bind_values[] = intval($condition['status']);
        }

        $query = $this->db->runQuery(
            " SELECT *  FROM `". $this->tb_entity ."`  WHERE 1 ".$where_clause."   ORDER BY `ordering` DESC LIMIT 5000 ",
            $bind_types, $bind_values
        );

        return $this->db->fetchAll($query);
    }


    protected function beforeCreateItem(array $input_info) : AppResponse
    {
        $parent_id = isset($input_info['parent_id']) ? $input_info['parent_id'] : 0;
        $api_key = (isset($input_info['api_key']) && $input_info['api_key']) ? $input_info['api_key'] : $input_info['title'];
        $api_key = Security\DataClean::makeInputSafe($api_key, DataType::ID);

        $api_key = $this->createUniqueAPIKey(0, $api_key);

        if(!isset($input_info['url_index']) || !$input_info['url_index']) {
            $input_info['url_index'] = $this->createUniqueUrlIndex(0, $input_info['title']);
        }else{
            $input_info['url_index'] = $this->createUniqueUrlIndex(0, $input_info['url_index']);
        }

        $info =  array_merge($input_info, array(
            "parent_id"         => $parent_id,
            "api_key"           => $api_key,
            "create_by"         => ADMIN_NAME,
            "create_time"       => CURRENT_TIME,
            "last_update_by"    => ADMIN_NAME,
            "last_update"       => CURRENT_TIME,
        ) );

        return new AppResponse('ok', null, $info);
    }


    protected function afterCreateItem($new_item_id, $new_item_info) {
        //update path&child
        $this->updatePath($new_item_id);
        $this->updateChild($new_item_id);
        $this->updateChild($new_item_info['parent_id']);
    }


    protected function beforeUpdateItem($item_id, $current_item_info, $new_input_info) : AppResponse
    {
        $info = $new_input_info;

        if(isset($info['url_index'])) {
            if(!$info['url_index']) {
                $info['url_index'] = $this->createUniqueUrlIndex($item_id, $info['title']);
            }else{
                $info['url_index'] = $this->createUniqueUrlIndex($item_id, $info['url_index']);
            }
        }

        $info['last_update'] = CURRENT_TIME;
        $info['last_update_by'] = ADMIN_NAME;

        return new AppResponse('ok', null, $info);
    }


    protected function afterUpdateItem($item_id, $old_item_info, $new_item_info) {
        //update cat-path for category
        $this->updatePath($item_id);
        $this->updateChild($item_id);

        //update child_ids for new/old parents and parents of parent
        if($new_item_info['parent_id'] != $old_item_info['parent_id']) {
            $this->updateChild($new_item_info['parent_id']);
            $this->updateChild($old_item_info['parent_id']);
        }
    }


    protected function afterDeleteItem($item_id, $item_info){
        $this->updateChild($item_info["parent_id"]);
    }


    //create an unique request-path
    protected function createUniqueAPIKey($id, $api_key){

        //if exist and belong other id, create a new one
        $query = $this->db->runQuery("SELECT `id` FROM `".$this->tb_entity."` WHERE `api_key` = ? LIMIT 1 ", ['s'], [$api_key]) ;
        if($info = $this->db->fetchAssoc($query)){
            if($info['id'] != $id) {
                $new_api_key = $api_key."-1";
                return $this->createUniqueAPIKey($id, $new_api_key);
            }
        }

        return $api_key;
    }


    protected function updatePathAndChildAll($id, $child_ids, $parent_id, $old_parent_id) {
        $this->updatePathAndChild($id);

        //update for childs
        $list_child_to_update = array_filter(explode(",", $child_ids));
        foreach($list_child_to_update as $_id) {
            if($_id != $id)  $this->updatePathAndChild($_id);
        }

        //cap nhat lai child list cua danh muc old_parent and new parent id, and parent of these parents
        $query = $this->db->runQuery(
            "SELECT cat_path FROM `". $this->tb_entity ."` WHERE `id`= ? OR `id`= ? ",
            ['d', 'd'], [$parent_id, $old_parent_id]
        );

        $cat_path_all = join(":", array_map(function ($item){
            return $item['cat_path'];
        } , $this->db->fetchAll($query)));


        $list_parent_to_update = array_unique(array_filter(explode(":", $cat_path_all)));

        foreach($list_parent_to_update as $_id) {
            if($_id > 0) $this->updatePathAndChild($_id);
        }
    }


    public function updatePathAndChild($id) {
        if(!$id) return false;

        $new_cat_path 	= $this->findCatPath($id);
        $new_child_list = $this->findChildList($id);
        $is_parent 		= ( $new_child_list ===  $id) ? 0 : 1;

        return $this->db->update(
            $this->tb_entity ,
            [
                'cat_path' => $new_cat_path,
                'child_ids' => $new_child_list,
                'is_parent' => $is_parent,
            ],
            [
                'id' => $id,
            ]
        );
    }


    protected function updatePath($id) {
        if(!$id) return false;

        $new_cat_path 	= $this->findCatPath($id);

        return $this->db->update(
            $this->tb_entity ,
            [
                'cat_path' => $new_cat_path,
            ],
            [
                'id' => $id,
            ]
        );
    }

    //update childs for current id and its parents and its parents' parents....
    protected function updateChild($id) {
        if(!$id) return false;

        $query = $this->db->runQuery("SELECT `cat_path` FROM `".$this->tb_entity."` WHERE `id` = ? LIMIT 1 ", ['d'], [$id]) ;

        if($item_info = $this->db->fetchAssoc($query)){
            $cat_id_list = array_filter(explode(":", $item_info['cat_path']));

            foreach ($cat_id_list as $_id) {
                $new_child_list = $this->findChildList($_id);
                $is_parent 		= ( $new_child_list ===  $_id) ? 0 : 1;

                $this->db->update(
                    $this->tb_entity ,
                    [
                        'child_ids' => $new_child_list,
                        'is_parent' => $is_parent,
                    ],
                    [
                        'id' => $_id,
                    ]
                );
            }
        }

        return true;
    }


    protected function _buildQueryConditionExtend(array $condition) : ?array
    {
        $catCondition = [];
        $bind_types = [];
        $bind_values = [];

        return array(join(" ", $catCondition), $bind_types, $bind_values);
    }


    //build category path 0:parent:categoryId
    protected function findCatPath($categoryId){
        $path = ":".$categoryId;
        $query = $this->db->runQuery("SELECT `parent_id` FROM `".$this->tb_entity."` WHERE `id` = ? LIMIT 1", ['d'], [$categoryId]);
        if($rs = $this->db->fetchAssoc($query)){
            if($rs['parent_id']) $path .= $this->findCatPath($rs['parent_id']);
        }

        return $path;
    }

    //lay toan bo cac muc la con
    protected function findChildList($p_category_id){
        $all_categories = $this->getAllByParent();

        $list = $p_category_id;
        if(isset($all_categories[$p_category_id])) {
            foreach ( $all_categories[$p_category_id] as $rs ) {
                $list .= ",". $this->findChildList($rs['id']);
            }
        }

        return $list;
    }


    protected function getAllParent(array $condition = []) {
        $cache_key = 'all-category-by-parent-'.$this->getEntityType();

        return self::getCache($cache_key, function () use ($condition){
            return $this->getAllByParent($condition);
        });
    }

}
