<?php

namespace Hura8\System\Model;

use Hura8\Database\iConnectDB;
use Hura8\System\Security\DataClean;
use Hura8\System\Security\DataType;


class RelationModel
{

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

    protected $tb_relation = "tb_relation";

    protected $main_item_type= '';
    protected $main_item_id= 0;

    public function __construct($main_item_type, $main_item_id = 0) {
        $this->db = get_db('', ENABLE_DB_DEBUG);
        $this->main_item_type = $main_item_type;
        $this->main_item_id = $main_item_id;
    }


    //given related_type and type_id, get all the main item ids
    public function getMainItems($related_item_type, $related_item_id = 0, array $condition = [], $return_type = 'list') {

        $where_clause = " AND `main_item_type` = '" . $this->db->escape($this->main_item_type) . "'
                          AND `main_item_id` = '".intval($this->main_item_id)."'  ";

        $where_clause .= " AND `related_item_type` = '" . $this->db->escape($related_item_type) . "' ";

        if($related_item_id) $where_clause .= " AND `related_item_id` = '".intval($related_item_id)."'  ";

        //excluded_ids
        if(isset($condition["excluded_ids"]) && $condition["excluded_ids"] ){
            $list_ids = DataClean::makeListOfInputSafe(explode(",", $condition["excluded_ids"]), DataType::INTEGER);

            if(sizeof($list_ids)) $where_clause .= " AND `related_item_id` NOT IN (".join(',', $list_ids ).") ";
        }

        //included_ids
        if(isset($condition["included_ids"]) && $condition["included_ids"] ){
            $list_ids = DataClean::makeListOfInputSafe(explode(",", $condition["included_ids"]), DataType::INTEGER);
            if(sizeof($list_ids)) $where_clause .= " AND `related_item_id` IN (".join(',', $list_ids ).") ";
        }


        if($return_type == 'total') {

            $query = $this->db->query("
                        SELECT COUNT(*) as total FROM `".$this->tb_relation."`
                        WHERE 1 " . $where_clause . " ");

            $total = 0;
            if ($rs = $this->db->fetchAssoc($query)) {
                $total = $rs['total'];
            }

            return $total;

        } else {

            $page = isset($condition['page']) ? intval($condition['page']) : 1;
            $numPerPage = isset($condition['numPerPage']) ? intval($condition['numPerPage']) : 10;

            $query = $this->db->query("
                                    SELECT `related_item_id` FROM `". $this->tb_relation ."`
                                    WHERE 1 " . $where_clause . "
                                    ORDER BY `ordering` DESC, `id` DESC
                                    LIMIT " . ($page - 1) * $numPerPage . ", ". $numPerPage ."
                             ");

            $result = array();
            foreach ( $this->db->fetchAll($query) as $info ) {
                $result[] = $info['related_item_id'];
            }

            return $result;
        }
    }

    public function updateOrdering($related_item_id, $new_order) {

        $this->db->runQuery(
            "UPDATE `".$this->tb_relation."` SET
                        `ordering` = ?
                   WHERE `main_item_type` = ?  AND `main_item_id` = ? AND `related_item_id` = ?  ",
            ['d', 's', 'd', 'd'],
            [  $new_order, $this->main_item_type, $this->main_item_id, $related_item_id ]
        );

        return [
            'status' => 'success',
            'message' => ''
        ];
    }

    //@warn: this does not check if records exist.
    public function create(array $related_items, $both_way_relation = true) {

        $build_insert = array();
        foreach ($related_items as $item) {
            if(!$this->checkExist($this->main_item_type, $this->main_item_id, $item['type'], $item['id'])) {
                $build_insert[] = [
                    "main_item_type" => $this->main_item_type,
                    "main_item_id" => intval($this->main_item_id),
                    "related_item_type" => $item['type'],
                    "related_item_id" => intval($item['id']),
                    "create_time" => CURRENT_TIME,
                    "create_by" => ADMIN_NAME,
                ];
            }

            //if 2-way relation, create item->main
            if($both_way_relation) {
                if(!$this->checkExist($item['type'], $item['id'], $this->main_item_type, $this->main_item_id)) {

                    $build_insert[] = [
                        "main_item_type" => $item['type'],
                        "main_item_id" => intval($item['id']),
                        "related_item_type" => $this->main_item_type,
                        "related_item_id" => intval($this->main_item_id),
                        "create_time" => CURRENT_TIME,
                        "create_by" => ADMIN_NAME,
                    ];

                }
            }
        }

        if(sizeof($build_insert)) {
            $this->db->bulk_insert($this->tb_relation, $build_insert);

            return [
                'status' => 'success',
                'message' => ''
            ];
        }

        return [
            'status' => 'error',
            'message' => 'Đã tồn tại'
        ];
    }


    public function checkExist($main_item_type, $main_item_id, $related_item_type, $related_item_id) {

        // itself
        if($main_item_type == $related_item_type && $main_item_id == $related_item_id) {
            return true;
        }

        $query = $this->db->runQuery(
            "SELECT id FROM `".$this->tb_relation."` 
                    WHERE  `main_item_type` = ? AND  `main_item_id` = ? AND  `related_item_type` = ? AND `related_item_id` = ? 
                    LIMIT 1" ,
            [
                's', 'd', 's', 'd'
            ],
            [
                $main_item_type,
                intval($main_item_id),
                $related_item_type,
                intval($related_item_id)
            ]
        );

        if($rs = $this->db->fetchAssoc($query)) {
            return $rs['id'];
        }

        return false;
    }

    //remove a related-item
    public function remove($related_item_type, $related_item_id, $remove_both_way = true) {

        $main_item_query = ( $this->main_item_id ) ? " `main_item_id` = '".intval($this->main_item_id)."' AND  " : "";

        $this->db->query("DELETE FROM `".$this->tb_relation."` WHERE 
                                `main_item_type` = '" . $this->db->escape($this->main_item_type) . "' AND 
                                ". $main_item_query ."
                                `related_item_type` = '".$this->db->escape($related_item_type)."' AND
                                `related_item_id` = '".intval($related_item_id)."'  ");

        if($remove_both_way) {

            $related_item_query = ( $this->main_item_id ) ? " `related_item_id` = '".intval($this->main_item_id)."' AND   " : "";

            $this->db->query("DELETE FROM `".$this->tb_relation."` WHERE 
                                `related_item_type` = '" . $this->db->escape($this->main_item_type) . "' AND 
                                " . $related_item_query . "
                                `main_item_type` = '".$this->db->escape($related_item_type)."' AND
                                `main_item_id` = '".intval($related_item_id)."'  ");
        }

        return [
            'status' => 'success',
            'message' => ''
        ];
    }


    //remove all relate items
    public function truncate() {
        $this->db->runQuery(
            "DELETE FROM `".$this->tb_relation."` WHERE  `main_item_type` = ? AND  `main_item_id` = ?  ",
            [ 's', 's' ],
            [ $this->main_item_type, $this->main_item_id ]
        );
    }


    //count related items
    public function getRelatedItemCount() {

        $query = $this->db->runQuery(
            "
                    SELECT `related_item_type`, COUNT(`related_item_id`) AS total FROM `". $this->tb_relation ."`
                    WHERE `main_item_type` = ? AND  `main_item_id` = ? 
                    GROUP BY `related_item_type`
             ",
            [ 's', 's' ], [ $this->main_item_type, $this->main_item_id ]
        );
        $result = array();
        foreach ( $this->db->fetchAll($query) as $info ) {
            $result[$info['related_item_type']] = $info['total'];
        }

        return $result;
    }


    //extend getRelatedItems to get for list of main_item_ids
    public function getRelatedItemsForList(array $main_item_list_ids, array $related_item_types = []) {

        if(!sizeof($main_item_list_ids)) return [];

        $bind_values = $main_item_list_ids;
        list($parameterized_ids, $bind_types) = create_bind_sql_parameter_from_value_list($main_item_list_ids, 'int');
        $condition =  " AND `main_item_id` IN (". $parameterized_ids .") ";

        $bind_types[] = 's';
        $bind_values[] = $this->main_item_type;
        $condition .=  " AND `main_item_type` = ?  ";

        if(sizeof($related_item_types)) {
            $type_condition = [];
            foreach ($related_item_types as $_type) {
                $type_condition[] = " `related_item_type` = ? ";

                $bind_types[] = 's';
                $bind_values[] = $_type;
            }

            $condition .=  " AND ( ".join(' OR ', $type_condition )." ) ";
        }

        $query = $this->db->runQuery("
                                    SELECT 
                                        `main_item_id` ,
                                        `related_item_type`, 
                                        `related_item_id`, 
                                        `ordering` 
                                    FROM `". $this->tb_relation ."`
                                    WHERE 1 ". $condition ." 
                                    ORDER BY `ordering` DESC, `id` DESC
                                    LIMIT 5000
                             ", $bind_types, $bind_values);

        $result = array();
        foreach ( $this->db->fetchAll($query) as $info ) {
            $result[$info['main_item_id']][$info['related_item_type']][$info['related_item_id']] = [
                "item_id"   => $info['related_item_id'],
                "ordering"  => $info['ordering'],
            ];
        }

        //final result
        $final_result = [];
        foreach ($main_item_list_ids as $_id) {
            $final_result[$_id] = (isset($result[$_id])) ? $result[$_id] : false;
        }

        return $final_result;
    }

    //get all related items and group them by type
    public function getRelatedItems(array $related_item_types = []) {

        $bind_types = ['s', 's'];
        $bind_values = [$this->main_item_type, $this->main_item_id];

        $condition =  "";
        if(sizeof($related_item_types)) {
            $type_condition = [];
            foreach ($related_item_types as $_type) {
                $type_condition[] = " `related_item_type` = ? ";

                $bind_types[] = 's';
                $bind_values[] = $_type;
            }

            $condition .=  " AND ( ".join(' OR ', $type_condition )." ) ";
        }


        $query = $this->db->runQuery("
                                    SELECT 
                                        `related_item_type`, 
                                        `related_item_id`, 
                                        `ordering` 
                                    FROM `". $this->tb_relation ."`
                                    WHERE `main_item_type` = ? AND  `main_item_id` = ?  ". $condition ." 
                                    ORDER BY `ordering` DESC, `id` DESC
                                    LIMIT 5000
                             ", $bind_types, $bind_values );
        $result = array();
        foreach ( $this->db->fetchAll($query) as $info ) {
            $result[$info['related_item_type']][$info['related_item_id']] = [
                "item_id"   => $info['related_item_id'],
                "ordering"  => $info['ordering'],
            ];
        }

        //check if we get only single type, then return all the results
        if(sizeof($related_item_types) == 1) {
            $related_item_type = $related_item_types[0];
            return isset($result[$related_item_type]) ? $result[$related_item_type] : array();
        }

        return $result;
    }


}
