<?php

namespace App\Models;

use App\SearchTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Session;

class Central extends Model implements Exportable
{
    use SearchTrait;

    public $importUpdateKeys = null;
    public static $extraModelCondition = [
        'stores' => [
            ['column' => 'status', 'operation' => '!=', 'value' => 'trash']
        ]
    ];

    public function __construct()
    {
        parent::__construct();
    }

    public function getModelCountColumn()
    {
        return $this->table . '.' . $this->primaryKey;
    }

    public function buildExportQuery($params)
    {
        return $this->buildQuery($params);
    }

    public function getExportHeaders(): array
    {
        $gridInfo = $this->makeInfo($this->module)['config']['grid'];

        $headersData = [];
        $duplicateCheckArr = [];
        foreach ($gridInfo as $gridColumn) {
            if ($gridColumn['download'] == '1' && !in_array($gridColumn['field'], $duplicateCheckArr)) {
                $duplicateCheckArr[] = $gridColumn['field'];
                $headersData[$gridColumn['label']] = $gridColumn['alias'] . '.' . $gridColumn['field'];
            }
        }

        return $headersData;
    }

    public static function buildQuery($args)
    {
        $model = new static;
        $qry = $model::query();

        extract(array_merge(array(
            'page' => '1',
            'limit' => '50',
            'sort' => "{$model->table}.{$model->primaryKey}",
            'order' => 'DESC',
            'search' => null,
            'params' => null,
            'flimit' => '',
            'fstart' => '',
            'global' => 1
        ), $args));

        if ($params || $search)
            self::buildWhere($qry, $params, $search, $global);

        return $qry;
    }

    public static function getRows($args, $gid = 0)
    {
        $model = new static;
        $qry = $model::query();

        extract(array_merge(array(
            'page' => '1',
            'limit' => '50',
            'sort' => "{$model->table}.{$model->primaryKey}",
            'order' => 'DESC',
            'search' => null,
            'params' => null,
            'flimit' => '',
            'fstart' => '',
            'global' => 1
        ), $args));

        if ($params || $search)
            self::buildWhere($qry, $params, $search, $global);
        $total = $qry->count();
        $qry = $qry->take($limit)->offset($limit * ($page - 1));
        if (isset($sort) && $sort)
            $qry->orderBy($sort, $order);

        $result = $qry->get()->toArray();

        return array('rows' => $result, 'total' => $total);
    }

    public static function mapSearchOperation($operate)
    {
        $val = '';
        switch ($operate) {
            case 'equal':
                $val = '=';
                break;
            case 'not_equal':
                $val = '<>';
                break;
            case 'bigger_equal':
                $val = '>=';
                break;
            case 'smaller_equal':
                $val = '<=';
                break;
            case 'smaller':
                $val = '<';
                break;
            case 'bigger':
                $val = '>';
                break;
            case 'not_null':
                $val = 'not_null';
                break;

            case 'is_null':
                $val = 'is_null';
                break;

            case 'like':
                $val = 'like';
                break;

            case 'between':
                $val = 'between';
                break;

            default:
                $val = '=';
                break;
        }
        return $val;
    }

    public static function buildWhere(&$qry, $params, $search, $global, $searchFieldsInfo = null)
    {
        $sessionGid = Session::get('gid');
        if ($search)
            $qry = $qry->searchFields($search);

        $arr = [];
        $allowsearch = $searchFieldsInfo ?? self::getSearchFields(true);

        foreach ($allowsearch as $as) $arr[$as['field']] = $as;

        if ($params) {
            foreach ($params as $t) {
                $keys = explode(":", $t);
                $tableName = $qry->getModel()->getTable();
                $columnName = "{$tableName}.{$keys[0]}";

                if (count($keys) > 2 && in_array($keys[0], array_keys($arr))) :
                    switch ($arr[$keys[0]]['type']) {
                        case 'lang':
                            $operate = self::mapSearchOperation($keys[1]);
                            if ($operate == '=')
                                $qry->where($columnName, 'like', '%' . $keys[2] . '%');
                            break;
                        case 'select':
                        case 'radio':
                            if ($arr[$keys[0]]['option']['is_json'] == 1)
                                $qry->whereRaw("JSON_CONTAINS({$columnName}, '\"{$keys[2]}\"')");
                            else
                                $qry->where($columnName, $keys[2]);
                            break;
                        case 'text_date';
                        case 'text_datetime';
                        case 'update_timestamp';
                            $operate = self::mapSearchOperation($keys[1]);
                            switch ($operate) {
                                case 'between':
                                    $qry->whereDate($columnName, '>', $keys[2])->whereDate($columnName, '<', $keys[3]);
                                    break;
                                case 'is_null':
                                    $qry->whereNull($columnName);
                                    break;
                                default:
                                    $qry->whereDate($columnName, $operate, $keys[2]);
                                    break;
                            }
                            break;
                        case 'select_relation':
                            $qry->join($arr[$keys[0]]['option']['lookup_table'], function ($join) use ($arr, $keys, $columnName) {
                                $joinConditions = explode('=', $arr[$keys[0]]['option']['lookup_query']);
                                $join->on(trim($joinConditions[0]), trim($joinConditions[1]));
                                if ($arr[$keys[0]]['option']['is_json'] == 1)
                                    $join->whereRaw("{$arr[$keys[0]]['option']['lookup_key']} = '{$keys[2]}'");
                                else
                                    $join->where($columnName, $keys[2]);
                            });
                            break;
                        case 'radio_relation':

                            $columnName = "{$arr[$keys[0]]['option']['lookup_table']}.{$keys[0]}";

                            $qry->join($arr[$keys[0]]['option']['lookup_table'], function ($join) use ($arr, $keys, $columnName,$tableName,$search) {
                                $joinConditions = explode('=', $arr[$keys[0]]['option']['lookup_query']);


                                $join->on( $arr[$keys[0]]['option']['lookup_table'] .'.'.trim($arr[$keys[0]]['option']['lookup_key']), $tableName.'.'.trim($arr[$keys[0]]['option']['joinon_key']));
                                if ($arr[$keys[0]]['option']['is_json'] == 1)
                                    $join->whereRaw("{$arr[$keys[0]]['option']['lookup_key']} = '{$keys[2]}'");
                                else
                                    $join->where($columnName, $keys[2]);
                            });
                            break;
                        case 'json':
                            $jsonConditionKey = $arr[$keys[0]]['json_condition_key'] ?? $keys[0];
                            $operate = self::mapSearchOperation($keys[1]);
                            $jsonKey = explode('->>', $jsonConditionKey);
                            switch ($operate) {
                                case 'between':
                                    $qry->whereRaw("{$jsonConditionKey} > '{$keys[2]}' AND {$jsonConditionKey} < '{$keys[3]}' AND json_extract(`{$jsonKey[0]}`, {$jsonKey[1]}) IS NOT NULL");
                                    break;
                                default:
                                    $qry->whereRaw("{$jsonConditionKey} = '{$keys[2]}'");
                                    break;
                            }
                            break;
                        default:
                            $operate = self::mapSearchOperation($keys[1]);

                            if ($operate == 'like')
                                $qry->where($columnName, 'like', '%' . $keys[2] . '%');
                            else if ($operate == 'is_null')
                                $qry->whereNull($columnName);
                            else if ($operate == 'not_null')
                                $qry->whereNotNull($columnName);
                            else if ($operate == 'between')
                                $qry->whereBetween($columnName, [$keys[2], $keys[3]]);
                            else
                                $qry->where($columnName, $operate, $keys[2]);
                            break;
                    }
                endif;
            }
        }
        if ($global == 0 && isset($arr['entry_by']))
            $qry->where('entry_by', $sessionGid);

    }


    public static function getRow($id)
    {

        $model = new static;
        return $model::find($id)->toArray();
    }

    public static function prevNext($id)
    {

        $table = with(new static)->table;
        $key = with(new static)->primaryKey;

        $prev = '';
        $next = '';

        $Qnext = \DB::select(
            self::querySelect() .
            self::queryWhere() .
            " AND " . $table . "." . $key . " > '{$id}'  " .
            self::queryGroup() . ' LIMIT 1'
        );


        if (count($Qnext) >= 1) $next = $Qnext[0]->{$key};

        $Qprev = \DB::select(
            self::querySelect() .
            self::queryWhere() .
            " AND " . $table . "." . $key . " < '{$id}'" .
            self::queryGroup() . " ORDER BY " . $table . "." . $key . " DESC LIMIT 1"
        );
        if (count($Qprev) >= 1) $prev = $Qprev[0]->{$key};

        return array('prev' => $prev, 'next' => $next);
    }

    public function insertRow($data, $id)
    {

        foreach ($data as $key => $val) {
            if ($this->table == 'front_menu') continue;
            if (is_array($val))
                $data[$key] = json_encode($val, JSON_UNESCAPED_UNICODE);
            else if (strlen($val) < 1)
                $data[$key] = null;
        }

        $model = new static;
        $key = $this->importUpdateKeys ?: $this->primaryKey;
        $keyPair = [];
        $returnFieldKey = $model->primaryKey;
        if (is_array($key)) {
            foreach ($key as $fieldName) {
                $keyPair[$fieldName] = $data[$fieldName];
            }
        } else {
            $keyPair = array($key => $data[$key]);
            unset($data[$key]);
        }

        if ($model->timestamps) {
            if (isset($data['createdOn'])) $data['createdOn'] = date("Y-m-d H:i:s");
            if (isset($data['updatedOn'])) $data['updatedOn'] = date("Y-m-d H:i:s");
        }

        $obj = $model::updateOrCreate($keyPair, $data);

        return $id ?: $obj->{$returnFieldKey};
    }

    static function makeInfo($id)
    {
        $row = config("modules.{$id}");
        $data = array();
        if (empty($row))
            return $data;
        $r = (object)$row;
        $langs = (json_decode($r->module_lang, true));
        $data['id'] = $r->module_id;
        $data['title'] = \SiteHelpers::infoLang($r->module_title, $langs, 'title');
        $data['note'] = \SiteHelpers::infoLang($r->module_note, $langs, 'note');
        $data['table'] = $r->module_db;
        $data['key'] = $r->module_db_key;
        $data['frontend_slug'] = $r->module_frontend_slug;
        $data['type'] = $r->module_type;
        $data['config'] = $r->module_config;

        $field = array();
        foreach ($data['config']['grid'] as $fs) {
            $field[] = $fs['field'];
        }

        $data['field'] = $field;

        $data['setting'] = array(
            'gridtype' => (isset($data['config']['setting']['gridtype']) ? $data['config']['setting']['gridtype'] : 'native'),
            'orderby' => (isset($data['config']['setting']['orderby']) ? $data['config']['setting']['orderby'] : $r->module_db_key),
            'ordertype' => (isset($data['config']['setting']['ordertype']) ? $data['config']['setting']['ordertype'] : 'asc'),
            'perpage' => (isset($data['config']['setting']['perpage']) ? $data['config']['setting']['perpage'] : '50'),
            'frozen' => (isset($data['config']['setting']['frozen']) ? $data['config']['setting']['frozen'] : 'false'),
            'form-method' => (isset($data['config']['setting']['form-method']) ? $data['config']['setting']['form-method'] : 'native'),
            'view-method' => (isset($data['config']['setting']['view-method']) ? $data['config']['setting']['view-method'] : 'native'),
            'inline' => (isset($data['config']['setting']['inline']) ? $data['config']['setting']['inline'] : 'false'),

        );
        return $data;
    }

    static function getComboselectOld($params, $limit = null, $parent = null, $values = null, $where = null)
    {

        if ($values) {
            $str_values = preg_replace('/[\d,]+/', '', $values);
            if (strlen($str_values) > 0)
                $values = "'{$values}'";
        }

        $limit = explode(':', $limit);
        $parent = explode(':', $parent);
        if ($parent[0] == 'network_id') {
            $route_name = $parent[1];
        } else {
            $route_name = \Request::route()->getName();
            $route_name = str_replace('.show', '', $route_name);
        }

        if (count($limit) >= 3) {
            if ($values)
                $order_by = " ORDER BY FIELD({$params[1]},{$values}) ASC";
            else
                $order_by = ' ';

            $table = $params[0];
            $condition = $limit[0] . " `" . $limit[1] . "` " . $limit[2] . " '" . $limit[3] . "' ";
            if ($where)
                $condition .= ' AND ' . urldecode($where) . '  ';
            if (count($parent) >= 2) {
                $row = \DB::table($table)->where($parent[0], $parent[1])->get();
                $row = \DB::select("SELECT * FROM " . $table . " " . $condition . " AND " . $parent[0] . " = '" . $parent[1] . "'" . $order_by);
            } else {
                $row = \DB::select("SELECT * FROM " . $table . " " . $condition . $order_by);
            }
        } else {

            $table = $params[0];

            if ($where) {

                if (count($parent) >= 2) {
                    if ($values)
                        $row = \DB::table($table)->where($parent[0], $route_name)->whereRaw($where)->orderByRaw("FIELD({$params[1]},{$values}) ASC")->get();
                    else
                        $row = \DB::table($table)->where($parent[0], $route_name)->whereRaw($where)->get();
                } else {
                    if ($values)
                        $row = \DB::table($table)->whereRaw($where)->orderByRaw("FIELD({$params[1]},{$values}) ASC")->get();
                    else
                        $row = \DB::table($table)->whereRaw($where)->get();
                }
            } else {
                if (count($parent) >= 2) {
                    if ($values)
                        $row = \DB::table($table)->where($parent[0], $route_name)->orderByRaw("FIELD({$params[1]},{$values}) ASC")->get();
                    else
                        $row = \DB::table($table)->where($parent[0], $route_name)->get();
                } else {
                    if ($values)
                        $row = \DB::table($table)->orderByRaw("FIELD({$params[1]},{$values}) ASC")->get();
                    else
                        $row = \DB::table($table)->get();
                }
            }
        }

        return $row;
    }

    static function getComboselect($params = [], $limit = null, $parent = null, $values = null, $where = null)
    {
        // dump($parent); dump($values); dump($where);
        $results = [];
        if (count($params) > 2) {

            $table = $params[0];
            $id_col = $params[1];
            $selColms = array_merge([$id_col], explode('|', $params[2]));
            $qry = \DB::table($table)->select($selColms);
            if ($where)
                $qry = $qry->whereRaw($where);
            $results = $qry->get();
        }

        return $results;
    }

    static function getAjaxselect($params = [], $limit = null, $parent = null, $values = null, $where = null, $search = null)
    {
        $results = [];
        if (count($params) > 2) {

            $table = $params[0];
            $id_col = $params[1];
            $selColms = array_merge([$id_col], explode('|', $params[2]));
            $qry = \DB::table($table)->select($selColms);
            if ($where)
                $qry = $qry->whereRaw($where);
            if ($search) {
                foreach (explode('|', $params[2]) as $srCol)
                    $qry = $qry->orWhereRaw($srCol . ' LIKE ' . "'%$search%'");
            }
            if ($values) {
                if (!strpos($values, '|')) {
                    $values_order_str = "'" . implode("','", explode(',', $values)) . "'";
                    $qry = $qry->orderByRaw("FIELD({$id_col},{$values_order_str})");
                }
                $values = explode(',', str_replace(array('"', ' '), '', $values));
                $qry = $qry->whereIn($id_col, $values);

                $paginate = (count($values) > 25) ? count($values) : 25;
            } else
                $paginate = 25;

            if (isset(self::$extraModelCondition[$table])) {
                foreach (self::$extraModelCondition[$table] as $condition) {
                    $qry->where($condition['column'], $condition['operation'], $condition['value']);
                }
            }

            $results = $qry->paginate($paginate);
        }

        return $results;
    }


    public static function getColoumnInfo($result)
    {
        $pdo = \DB::getPdo();
        $res = $pdo->query($result);
        $i = 0;
        $coll = array();
        while ($i < $res->columnCount()) {
            $info = $res->getColumnMeta($i);
            $coll[] = $info;
            $i++;
        }
        return $coll;
    }


    function isAccess($id, $task, $gid)
    {

        $row = \DB::table('tb_groups_access')->where('module_id', $id)->where('group_id', $gid)->get();

        if (count($row) >= 1) {
            $row = $row[0];
            if ($row->access_data != '') {
                $data = json_decode($row->access_data, true);
                return $data[$task];
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    }

    function validAccess($id, $gid = 0)
    {

        $row = \DB::table('tb_groups_access')->where('module_id', $id)->where('group_id', $gid)->get();

        if (count($row) >= 1) {
            $row = $row[0];
            if ($row->access_data != '') {
                $data = json_decode($row->access_data, true);
            } else {
                $data = array();
            }
            return $data;
        } else {
            return false;
        }
    }

    static function getColumnTable($table)
    {
        $columns = array();
        foreach (\DB::select("SHOW COLUMNS FROM $table") as $column) {
            //print_r($column);
            $columns[$column->Field] = '';
        }


        return $columns;
    }

    static function getTableList($db)
    {
        $t = array();
        $dbname = 'Tables_in_' . ($db);
        foreach (\DB::select("SHOW TABLES FROM `{$db}`") as $key => $table) {

            $t[$table->$dbname] = $table->$dbname;
        }
        return $t;
    }

    static function getTableField($table)
    {
        $columns = array();
        foreach (\DB::select("SHOW COLUMNS FROM $table") as $column)
            $columns[$column->Field] = $column->Field;
        return $columns;
    }

    public function logs($request, $id)
    {
        $key = with(new static)->primaryKey;
        if ($request->input($key) == '') {
            $note = 'New Data with ID ' . $id . ' Has been Inserted !';
        } else {
            $note = 'Data with ID ' . $id . ' Has been Updated !';
        }
        $data = array(
            'module' => $request->segment(2),
            'task' => $request->segment(3),
            'user_id' => \Session::get('uid'),
            'ipaddress' => $request->getClientIp(),
            'note' => $note
        );
        \DB::table('tb_logs')->insert($data);

        // Below code replaced by CentralObserver

        // $custom_func_name = 'custom_func_' . $request->segment(2);

        // if (method_exists($this, $custom_func_name)) {
        // 	$this->{$custom_func_name}();
        // }
    }


    protected static function boot()
    {
        // you MUST call the parent boot method 
        // in this case the \Illuminate\Database\Eloquent\Model
        parent::boot();

        // note I am using static::observe(...) instead of Config::observe(...)
        // this way the child classes auto-register the observer to their own class
        static::observe('App\Observers\CentralObserver');
    }
}
