<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Schema;

/**
 * Setting aplikasi (key/value). Menimpa config() saat runtime via SettingServiceProvider.
 *
 * Field is_secret menyimpan nilai ter-enkripsi (Crypt) di kolom value JSON.
 *
 * @property string $key
 * @property mixed $value
 * @property bool $is_secret
 */
class Setting extends Model
{
    /** Key cache daftar seluruh setting (key => decrypted value). */
    public const CACHE_KEY = 'settings:all';

    protected $fillable = ['key', 'value', 'is_secret'];

    protected function casts(): array
    {
        return [
            'is_secret' => 'boolean',
        ];
    }

    /**
     * Baca nilai: decrypt bila is_secret. Kolom value disimpan JSON.
     */
    public function getValueAttribute($raw): mixed
    {
        if ($raw === null) {
            return null;
        }

        $decoded = json_decode($raw, true);

        if ($this->is_secret) {
            try {
                return $decoded !== null ? Crypt::decryptString($decoded) : null;
            } catch (\Throwable) {
                return null;
            }
        }

        return $decoded;
    }

    /**
     * Simpan nilai: encrypt bila is_secret, lalu JSON-encode.
     */
    public function setValueAttribute($value): void
    {
        $stored = $this->is_secret && $value !== null && $value !== ''
            ? Crypt::encryptString((string) $value)
            : $value;

        $this->attributes['value'] = $stored !== null ? json_encode($stored) : null;
    }

    /**
     * Ambil satu setting (decrypted). Fallback default bila belum ada.
     */
    public static function get(string $key, mixed $default = null): mixed
    {
        return static::allSettings()[$key] ?? $default;
    }

    /**
     * Simpan satu setting. Hapus cache agar langsung aktif.
     *
     * @param  mixed  $value  Bila null pada field secret, nilai tidak diubah.
     */
    public static function set(string $key, mixed $value, bool $secret = false): void
    {
        $setting = static::firstOrNew(['key' => $key]);
        $setting->is_secret = $secret;

        // Secret kosong = keep existing (jangan timpa null).
        if ($secret && ($value === null || $value === '') && $setting->exists) {
            return;
        }

        $setting->value = $value;
        $setting->save();

        static::flushCache();
    }

    /**
     * Seluruh setting sebagai map key => decrypted value.
     * Return [] bila tabel belum ada (fresh DB saat migrate).
     */
    public static function allSettings(): array
    {
        if (! Schema::hasTable('settings')) {
            return [];
        }

        return Cache::remember(static::CACHE_KEY, now()->addHour(), function () {
            return static::query()->get(['key', 'value', 'is_secret'])
                ->mapWithKeys(fn (self $s) => [$s->key => $s->value])
                ->all();
        });
    }

    public static function flushCache(): void
    {
        Cache::forget(static::CACHE_KEY);
    }
}
