<?php

namespace Hura8\System\Controller;

use Hura8\Interfaces\iEntityLanguageModel;
use Hura8\Interfaces\iEntityModel;
use Hura8\System\Security\DataClean;
use Hura8\System\Security\DataType;
use Hura8\Traits\ClassCacheTrait;


/**
 * @description shared controller for aAdminEntityBaseController and aPublicEntityBaseController
 * DO NOT EXTEND THIS CLASS from other classes
 */
abstract class aEntityBaseController
{
    use ClassCacheTrait;

    /* @var  iEntityModel $iEntityModel */
    protected $iEntityModel;

    /* @var  ?iEntityLanguageModel $iEntityLanguageModel */
    protected $iEntityLanguageModel = null;

    protected $view_language = LANGUAGE;

    public function __construct(
        iEntityModel $iEntityModel,
        ?iEntityLanguageModel $iEntityLanguageModel = null
    ) {

        $this->iEntityModel = $iEntityModel;

        if(!$this->isDefaultLanguage() && $iEntityLanguageModel instanceof iEntityLanguageModel) {
            $this->iEntityLanguageModel = $iEntityLanguageModel;

            // only controller allow to control the language for the model
            $this->iEntityLanguageModel->setLanguage($this->view_language);
        }
    }


    public function getLanguage() {
        return $this->view_language;
    }


    public function isDefaultLanguage() {
        return IS_DEFAULT_LANGUAGE;
    }


    public function getFilterConditions() : array {
        return array_merge(
            $this->baseFilterConditions(),
            $this->extendFilterConditions()
        );
    }


    // this is supposed to be overwritten by the extending class if required
    protected function extendFilterConditions() : array {
        return [];
    }

    // just here to show the reserved keys. That's all
    protected function baseFilterConditions() {
        $base_filter = [
            'q'         => getRequest("q", ''), // keyword search
            'q_options'  => [
                'field_filters' => [],
                'fulltext_fields' => [],
                'limit_result' => 2000
            ], // q_options as in iSearch->find($keyword, array $field_filters = [], array $fulltext_fields = ["keywords"], $limit_result = 2000)
            'featured'  => getRequestInt("featured", 0), // 1|-1
            'status'    => getRequestInt("status", 0), // 1|-1
            'excluded_ids' => null, // [id1, id2, ...]
            'included_ids' => null,// [id1, id2, ...]

            // to special filters for language
            'translated'     => getRequestInt("translated", 0), // 1|-1

            // for sorting
            'sort_by' => getRequest('sort', ''),

            // for pagination, not exactly for filter but put here to reserve the keys
            'numPerPage' => getRequestInt("show", 20),
            'page' => getPageId(),
        ];

        if(getRequest("excluded_ids", '') != '') {
            $base_filter['excluded_ids'] = explode("-", getRequest("excluded_ids", ''));
        }

        if(getRequest("included_ids", '') != '') {
            $base_filter['included_ids'] = explode("-", getRequest("included_ids", ''));
        }

        return $base_filter;
    }


    protected function formatItemInList(array $item_info)
    {
        return $item_info;
    }


    protected function formatItemInfo(array $item_info)
    {
        return $item_info;
    }


    public function getListByIds(array $list_id, array $condition = array()) : array
    {
        $item_list = array_map(function ($item){
            return $this->formatItemInList($item);
        }, $this->iEntityModel->getListByIds($list_id, $condition));

        if($this->iEntityLanguageModel) {
            $item_list_language_info = $this->iEntityLanguageModel->getListByIds($list_id);

            $final_list = [];
            foreach ($item_list as $item) {
                $item_language_info = $item_list_language_info[$item['id']] ?? ["not_translated" => true];
                $final_list[] = array_merge($item, $item_language_info);
            }

            return $final_list;
        }

        return $item_list;
    }


    // for extending controller class to validate and clean data in the $raw_filter_condition (ie. from URL)
    // before sending to model for data querying
    // extending controller must overwrite this
    protected function validateAndCleanFilterCondition(array $raw_filter_condition) : array {

        $clean_values = [];

        foreach ($raw_filter_condition as $key => $value) {
            // default
            if(is_array($value)) {
                $clean_values[$key] = DataClean::makeListOfInputSafe($value, DataType::ID);
            }else{
                $clean_values[$key] = DataClean::makeInputSafe($value, DataType::ID);
            }
        }

        return $clean_values;
    }


    /**
     * @description utility to inspect the actual filters which will be used in getList
     * make sure to edit the ::_buildQueryConditionExtend method on the Model so the Model will parse the filters provided by controller here
     * @param array $raw_filter_condition
     * @return string[]
     */
    public function getActualFilterCondition(array $raw_filter_condition) : array
    {
        return $this->buildFilterQuery($raw_filter_condition);
    }

    /**
     * @description utility to inspect the actual filters which will be used in getList by Model
     * @param array $raw_filter_condition
     * @return string[]
     */
    public function getModelFilterCondition(array $raw_filter_condition) : array
    {
        return $this->iEntityModel->getQueryCondition($this->buildFilterQuery($raw_filter_condition));
    }


    public function getList(array $raw_filter_condition) : array
    {

        $filter_condition = $this->buildFilterQuery($raw_filter_condition);
        //debug_var($filter_condition);

        $item_list = array_map(function ($item){
            return $this->formatItemInList($item);
        }, $this->iEntityModel->getList($filter_condition));


        if($this->iEntityLanguageModel) {

            $item_list_ids = array_map(function ($item){ return $item['id']; }, $item_list);
            $item_list_language_info = $this->iEntityLanguageModel->getListByIds($item_list_ids);

            $final_list = [];
            foreach ($item_list as $item) {
                $item_language_info = $item_list_language_info[$item['id']] ?? ["not_translated" => true];
                $final_list[] = array_merge($item, $item_language_info);
            }

            return $final_list;
        }

        return $item_list;
    }


    public function getTotal(array $raw_filter_condition) : int
    {
        $filter_condition = $this->buildFilterQuery($raw_filter_condition);

        return $this->iEntityModel->getTotal($filter_condition);
    }


    protected function buildFilterQuery(array $raw_filter_condition) : array
    {
        $filter_condition = $this->validateAndCleanFilterCondition($raw_filter_condition);

        // special case to filter out which ids have not been translated
        if(isset($filter_condition['translated']) && $filter_condition['translated'] && $this->iEntityLanguageModel) {
            if($filter_condition['translated'] == 1) {
                $filter_condition['included_ids'] = $this->iEntityLanguageModel->getTranslatedIds();
            }else{
                $filter_condition['excluded_ids'] = $this->iEntityLanguageModel->getTranslatedIds();
            }
        }

        return $filter_condition;
    }


    public function getInfo($id): ?array
    {
        if(!$id) return null;

        return self::getCache("getInfo-".$id."-".$this->view_language, function () use ($id){

            $info = $this->iEntityModel->getInfo($id);

            if($this->iEntityLanguageModel && $info) {
                $item_language_info = $this->iEntityLanguageModel->getInfo($id);
                //debug_var($item_language_info);

                if($item_language_info) {
                    return $this->formatItemInfo(array_merge($info, $item_language_info));
                }else{
                    $info["not_translated"] = true;

                    return $this->formatItemInfo($info);
                }
            }

            return ($info) ? $this->formatItemInfo($info) : null;

        });

    }


}
