<?php

namespace App\Models;

use App\Models\Model;
use Hypervel\Support\Facades\Cache;

class SystemSetting extends Model
{
    protected array $fillable = [
        'key',
        'value',
        'type',
        'options',
        'group',
        'label',
        'description',
        'hashkey',
        'created_by',
        'updated_by',
        'is_active',
    ];

    /**
     * Cache key for settings.
     */
    protected static string $cacheKey = 'app_system_settings';

    /**
     * Get a setting value by key.
     *
     * @param string $key
     * @param mixed $default
     * @return mixed
     */
    public static function getValue(string $key, $default = null)
    {
        $settings = static::getAllFromCache();
        
        /** @var self|null $setting */
        $setting = $settings->where('key', $key)->first();
        
        if ($setting === null) {
            return $default;
        }
        
        return static::parseValue($setting->value, $setting->type);
    }

    /**
     * Set a setting value.
     *
     * @param string $key
     * @param mixed $value
     * @return static
     */
    public static function setValue(string $key, $value, $group = 'general', $type = null)
    {
        /** @var self|null $setting */
        $setting = static::where('key', $key)->first();
        
        // Auto-detect JSON if it's an array or a JSON string
        if ($type === null) {
            if (is_bool($value)) {
                $type = 'boolean';
            } elseif (is_array($value)) {
                $type = 'json';
                $value = json_encode($value);
            } elseif (is_string($value) && (strpos($value, '[') === 0 || strpos($value, '{') === 0)) {
                $decoded = json_decode($value);
                if (json_last_error() === JSON_ERROR_NONE) {
                    $type = 'json';
                }
            }
        } elseif ($type === 'json' && is_array($value)) {
            $value = json_encode($value);
        }

        if ($setting) {
            $data = ['value' => is_bool($value) ? ($value ? 'true' : 'false') : (string) $value];
            if ($type !== null) $data['type'] = $type;
            $setting->update($data);
        } else {
            $setting = static::create([
                'key' => $key,
                'value' => is_bool($value) ? ($value ? 'true' : 'false') : (string) $value,
                'group' => $group,
                'type' => $type ?? 'text'
            ]);
        }
        
        static::clearCache();
        
        return $setting;
    }

    /**
     * Get all settings in a group.
     *
     * @param string $group
     * @return \Hypervel\Support\Collection
     */
    public static function getGroup(string $group)
    {
        return static::getAllFromCache()->where('group', $group);
    }

    /**
     * Clear the settings cache.
     */
    public static function clearCache()
    {
        if (class_exists(\Hypervel\Support\Facades\Cache::class)) {
            \Hypervel\Support\Facades\Cache::forget(static::$cacheKey);
        }
    }

    /**
     * Get all settings from cache or database.
     *
     * @return \Hypervel\Support\Collection|\Hyperf\Database\Model\Collection
     */
    protected static function getAllFromCache()
    {
        if (class_exists(\Hypervel\Support\Facades\Cache::class)) {
            return \Hypervel\Support\Facades\Cache::rememberForever(static::$cacheKey, function () {
                return static::all();
            });
        }
        return static::all();
    }

    /**
     * Parse value based on type.
     *
     * @param mixed $value
     * @param string|null $type
     * @return mixed
     */
    protected static function parseValue($value, ?string $type)
    {
        switch ($type) {
            case 'boolean':
                return filter_var($value, FILTER_VALIDATE_BOOLEAN);
            case 'number':
                return is_numeric($value) ? (float) $value : $value;
            case 'json':
                return is_string($value) ? json_decode($value, true) : $value;
            default:
                return $value;
        }
    }

    /**
     * Accessor for parsed value.
     */
    public function getParsedValueAttribute()
    {
        return static::parseValue($this->value, $this->type);
    }
}
