<?php

namespace App\Models;

use App\Modules\Student\Model\Student;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;

class Crud extends Model
{
    const CREATED_AT = 'created_at';
    const UPDATED_AT = 'updated_at';
    const DELETED_AT = 'deleted_at';

    protected static function boot()
    {
        parent::boot();
        static::saving(function ($data) {
            //Lets clear all cache based on updated model.
            //$data->clearCache();
        });
    }

    public function clearCache()
    {
        $this->_clearFindCache();
    }

    public function clearCacheByTags($tags)
    {
        return Cache::tags($tags)->flush();
    }

    protected function _clearFindCache()
    {
        $tags = $this->getCacheTags();
        return Cache::tags($tags)->flush();
    }

    public function getCacheKey($suffix = "", $prefix = "", $options = [])
    {
        $cacheKey = preg_replace("/\\\/", "_", get_class($this));
        if ($prefix) {
            $cacheKey = $prefix . "_" . $cacheKey;
        }
        if ($suffix) {
            $cacheKey .= "_" . $suffix;
        }
        if (isset($this->account_id)) {
            $cacheKey = $cacheKey . "_" . $this->account_id;
        }
        if (!empty($options)) {
            $cacheKey = $cacheKey . "_" . md5(serialize($options));
        }
        return $cacheKey;
    }

    public function delete()
    {
        $delete = parent::delete();
        if ($delete) {
            $cacheKey = self::getCacheKey("all");
            self::clearCache($cacheKey);
        }
        return $delete;
    }

    public function find($options = [])
    {
        $columns   = isset($options["cols"]) ? $options["cols"] : $this->getFetchCols();
        $cacheKey  = $this->getCacheKey("all", null, $options);
        $cacheTags = $this->getCacheTags();
        if (0) // if (Cache::tags($cacheTags)->has($cacheKey))
        {
            $data = Cache::tags($cacheTags)->get($cacheKey);
        } else {


            $query = $this->_getQuery($options);

            if (isset($options['with']) && count($options['with'])) {
                $withs = $options['with'];
                foreach ($withs as $key => $with) {
                    $query->with($with);
                }
            }

            if (isset($options['groupBy'])) {
                // Get default order
                $query->groupBy($options['groupBy']);
            }

            if (isset($options['orderBy'])) {
                // Get default order
                $order = isset($options['order']) ? $options['order'] : 'ASC';
                $query->orderBy($options['orderBy'], $order);
            }
            if (isset($options['whereNot'])) {
                if (is_array($options['whereNot'])) {
                    foreach ($options['whereNot'] as $key => $value) {
                        $query->where($key, '!=', $value);
                    }
                }
            }

            if (isset($options['whereIn'])) {
                if (is_array($options['whereIn'])) {
                    foreach ($options['whereIn'] as $key => $value) {
                        $query->whereIn($key, $value);
                    }
                }
            }

            if (isset($options['whereNotIn'])) {
                if (is_array($options['whereNotIn'])) {
                    foreach ($options['whereNotIn'] as $key => $value) {
                        $query->whereNotIn($key, $value);
                    }
                }
            }

            if (isset($options['whereNotNull'])) {
                if (is_array($options['whereNotNull'])) {
                    foreach ($options['whereNotNull'] as $value) {
                        $query->whereNotNull($value);
                    }
                } else {
                    $query->whereNotNull($options['whereNotNull']);
                }
            }

            if (isset($options['whereNull'])) {
                if (is_array($options['whereNull'])) {
                    foreach ($options['whereNull'] as $key => $value) {
                        $query->whereNull($key, $value);
                    }
                } else {
                    $query->whereNull($options['whereNull']);
                }
            }

            if (isset($options['where'])) {
                if (is_array($options['where'])) {
                    foreach ($options['where'] as $key => $value) {
                        $sign = "=";
                        if (strpos($key, " ") !== false) {
                            $parts = explode(" ", $key);
                            $sign  = $parts[1];
                            $key   = $parts[0];
                        }
                        $query->where($key, $sign, $value);
                    }

                } else {
                    $query->where($options['where']);
                }
            }

            if (isset($options['withtrashed'])) {
                $query->withTrashed();
            }

            if (isset($options['match']) && count($options['match'])) {
                $matches = $options['match'];
                $query->where(function ($query) use ($matches, $options) {
                    foreach ($matches as $key => $value) {
                        $query->orWhere($key, 'like', "%" . $value . "%");
                    }

                    if (isset($options['matchRaw']) && is_array($options['matchRaw'])) {

                        $matcheRaws = $options['matchRaw'];
                        foreach ($matcheRaws as $key => $value) {
                            $query->orWhereRaw("$key like  '%" . $value . "%'");
                        }
                    }
                });
            }
            if (isset($options['limit'])) {
                $query->limit($options['limit']);
            }
            if (isset($options['offset'])) {
                $query->offset($options['offset']);
            }
            if (isset($options['object']) && $options['object'] == true) {
                $data = $query;
            }elseif (isset($options['firstOrFail']) && $options['firstOrFail'] == true) {
                $data = $query->firstOrFail($columns);
            } elseif (isset($options['getFirst']) && $options['getFirst'] == true) {
                $data = $query->first($columns);
            } elseif (isset($options['count']) && $options['count'] == true) {
                if (isset($options['groupBy'])) {
                    $data = $query->get()->count();
                } else {
                    $data = $query->count();
                }

            } else {
                $data = $query->get($columns);
            }
            if (isset($options["debug"]) && $options["debug"] == true) {
                echo($query->toSql());
            }

            Cache::tags($cacheTags)->put($cacheKey, $data, 36000);
        }
        return $data;
    }

    public function returnAction($route, $model, $actions)
    {
        $matchActions = '';
        if (in_array('show', $actions)) {
            $matchActions .= View('layouts.actions.view')
                ->with('model', $model)
                ->with('route', $route . '.show');
        }
        if (in_array('edit', $actions)) {
            $matchActions .= View('layouts.actions.edit')
                ->with('model', $model)
                ->with('route', $route . '.edit');
        }
        if (in_array('destroy', $actions)) {
            $matchActions .= View('layouts.actions.delete')
                ->with('model', $model)
                ->with('route', $route . '.destroy');
        }

        return $matchActions;
    }

    public function returnAjaxAction($route, $model, $actions)
    {
        $matchActions = '';
        if (in_array('show', $actions)) {
            $matchActions .= View('layouts.actions.view')
                ->with('model', $model)
                ->with('ajax', true)
                ->with('route', $route . '.show');
        }
        if (in_array('edit', $actions)) {
            $matchActions .= View('layouts.actions.edit')
                ->with('model', $model)
                ->with('ajax', true)
                ->with('route', $route . '.edit');
        }
        if (in_array('destroy', $actions)) {
            $matchActions .= View('layouts.actions.delete')
                ->with('model', $model)
                ->with('ajax', true)
                ->with('route', $route . '.destroy');
        }

        return $matchActions;
    }

    protected function _getQuery($options = [])
    {
        return parent::query();
    }

    protected function _getCacheTags()
    {
        return ["model"];
    }

    public function getCacheTags($suffix = null)
    {
        $cacheTags = $this->_getCacheTags();

        if (is_null($suffix) && Config::has("account.id")) {
            $suffix = Config::get("account.id");
        }
        if ($suffix) {
            array_walk($cacheTags, [$this, "cacheTagsAlter"], $suffix);
        }
        return $cacheTags;
    }

    public function cacheTagsAlter(&$tag, $key, $suffix)
    {
        $tag .= "_" . $suffix;
    }

    public function findById($id, $options = [])
    {
        return $this->find(["where" => [$this->getTable() . ".id" => $id], "getFirst" => true] + $options);
    }

    public function getFetchCols()
    {
        return ["*"];
    }

    public function createdBy()
    {
        return $this->belongsTo('App\User', 'created_by');
    }

}
