<?php

use Illuminate\Database\Eloquent\Model as Eloquent;

/**
 * Setting Class
 *
 * Used to control the settings within the database and file
 * copies.
 *
 * @package MTC\Core
 * @author Craig McCreath <craig@mtcmedia.co.uk>
 */
class Setting extends Eloquent
{
    // Path in the site directory to the auto-generated settings file
    const FILE = '/settings/settings.db.php';

    // List of fillable attributes for Eloquent
    protected $fillable = [
        'key',
        'environment',
        'type',
        'value',
        'module',
        'description',
        'created_at',
    ];

    /**
     * Value Accessor ensures value is casted to the correct type
     * @return mixed
     * @author Aleksey Lavrinenko <aleksey.lavrinenko@mtcmedia.co.uk>
     */
    public function getValueAttribute()
    {
        $value = $this->attributes['value'];
        if (! empty($this->attributes['type'])) {
            settype($value, $this->attributes['type']);
        }
        return $value;
    }

    /**
     * Return a list of all the settings, grouped by module.
     *
     * @param string $environment Optionally restrict by environment.
     * @return array
     */
    static public function allByModule($environment = '')
    {
        $results = [];

        // Get default settings
        foreach (self::where('ENVIRONMENT', '=', '')->get() as $item) {

            if ($item->module == '') {
                $item->module = 'CORE';
            }

            // Check if this environment doesn't have an overrided setting
            if (!empty($environment)) {
                $env_item = self::where('key', '=', $item->key)
                    ->where('environment', '=', $environment)
                    ->first();

                if ($env_item !== null) {
                    $item->value_original = $item->value;
                    $item->value = $env_item->value;
                }
            }

            $results[$item->module][] = $item;
        }

        return $results;
    }

    /**
     * Get the settings for this site and define them, either by
     * file (if exists) or via the database
     *
     * @access public
     * @static
     * @return void
     */
    public static function loadAndDefine()
    {
        // Get the settings from a file, or load from db
        // Loading from file is a teeeny bit faster
        if (is_file(self::getFilePath())) {
            $data = require self::getFilePath();
            // Check that the db doesn't contain more rows. If it
            // does, it's a good sign something has been inserted in the
            // database and the file hasn't been updated yet.
            if (config('app.env') !== 'production' && count($data) < self::count()) {
                unset($data);
                // Should export here so it'll run from file next time
                // with the updated settings
                self::export();
            }
        }

        // Load from database if otherwise unavailable
        if (!isset($data) || !is_array($data)) {
            $data = self::orderBy('environment', 'desc')->get();
            self::export();
        }

        $environments = ['', config('app.env')];

        foreach ($data as $row) {
            // Ensure not already defined, and that the key
            // is allowed in this environment
            if (!(!defined($row['key']) && in_array($row['environment'], $environments))) { continue; }

define($row['key'], $row['value']);
        }
    }

    /**
     * Get the full path to the settings file for this site.
     *
     * @access public
     * @static
     * @return string
     */
    public static function getFilePath()
    {
        return SITE_PATH . DEFAULT_THEME_PATH . self::FILE;
    }

    /**
     * Export the current database settings into a file.
     *
     * @access public
     * @static
     * @return void
     */
    public static function export()
    {
        // Only run the export on non-live environments to ensure no changes
        // can occur from live.
        if (config('app.env') == 'production') {
            return;
        }

        $file = self::getFilePath();

        // Get all the settings, convert into PHP syntax
        // By ordering by environment desc we will get custom environments
        // first, then default attributse
        $data = self::orderBy('environment', 'desc')
            ->get()
            ->toArray();

        $data = self::varExport($data, true);

        // Add a nice message at the top telling people not to touch this.
        $header = "<?php\n" .
            "/**\n" .
            " * Database Settings File\n" .
            " *\n" .
            " * Exported: " . date('c') . "\n" .
            " *           " . $_SERVER['HTTP_HOST'] . "\n" .
            " *\n" .
            " * This file is auto-generated when settings are saved\n" .
            " * within the admin area. Any edits done here will be\n" .
            " * migrated to the settings table automatically.\n" .
            " */\n\n" .
            " return ";

        file_put_contents($file, $header . $data . ';');

        // Save the cache version so it doesn't try to overwrite
        // the settings on load automatically.
        copy($file, $file . '.cache');
    }

    /**
     * Determine if the settings table is out of date compared to the
     * settings db file. If so, update.
     *
     * @access public
     * @static
     * @return void
     */
    public static function import()
    {
        // Get the current settings file, which may have been updated
        $file = self::getFilePath();

        //check the main file exists
        if (!file_exists($file)) {
            return;
        }

        // Compare hashes between the settings file and the cached version
        if (!file_exists($file . '.cache') || (md5_file($file) !== md5_file($file . '.cache'))) {
            // Get the array of data
            $content = include $file;

            if (is_array($content)) {
                // Remove old settings, paste in the new ones!
                self::truncate();
                self::insert($content);

                // Copy the file so we know it's been completed
                copy($file, $file . '.cache');
            }
        }
    }

    /**
     * Var export but with square brackets for
     *
     * @param $var
     * @param bool $return
     * @param string $indent
     * @return string
     */
    public static function varExport($var, bool $return = false, string $indent = ""): string
    {
        switch (gettype($var)) {
            case "string":
                return '"' . addcslashes($var, "\\\$\"\r\n\t\v\f") . '"';
            case "array":
                $indexed = array_keys($var) === range(0, count($var) - 1);
                $r = [];
                foreach ($var as $key => $value) {
                    $r[] = "{$indent}    "
                        . ($indexed ? "" : self::varExport($key, $return) . " => ")
                        . self::varExport($value, $return, "{$indent}    ");
                }
                return "[\n" . implode(",\n", $r) . "\n" . $indent . "]";
            case "boolean":
                return $var ? "true" : "false";
            default:
                return var_export($var, $return);
        }
    }

}
