<?php

declare(strict_types=1);

namespace App\Models;

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

class Setting extends Model
{
    protected $fillable = [
        'key',
        'value',
        'group',
    ];

    /**
     * Keys that must be encrypted at rest.
     *
     * @var array<int, string>
     */
    private const ENCRYPTED_KEYS = [
        'virtfusion_api_token',
        'synergycp_api_token',
        'enhance_api_token',
        'pterodactyl_api_token',
        'enhance_organization_id',
        'discord_payment_webhook_url',
        'discord_provisioning_webhook_url',
        'discord_support_webhook_url',
        'discord_system_webhook_url',
    ];

    /**
     * Get a setting value by key, automatically decrypting if needed.
     */
    public static function get(string $key, mixed $default = null): mixed
    {
        $setting = static::query()->where('key', $key)->first();

        if (! $setting || $setting->value === null) {
            return $default;
        }

        if (in_array($key, self::ENCRYPTED_KEYS, true)) {
            return self::decryptValue($setting->value);
        }

        return $setting->value;
    }

    /**
     * Set a setting value by key, automatically encrypting if needed.
     */
    public static function set(string $key, mixed $value, string $group = 'general'): void
    {
        $storedValue = $value;

        if (in_array($key, self::ENCRYPTED_KEYS, true) && $value !== null && $value !== '') {
            $storedValue = Crypt::encryptString((string) $value);
        }

        static::query()->updateOrCreate(
            ['key' => $key],
            ['value' => $storedValue, 'group' => $group],
        );
    }

    /**
     * Get all settings for a given group as a key-value array.
     * Encrypted values are automatically decrypted.
     *
     * @return array<string, string|null>
     */
    public static function getGroup(string $group): array
    {
        $settings = static::query()
            ->where('group', $group)
            ->pluck('value', 'key')
            ->toArray();

        foreach ($settings as $key => $value) {
            if (in_array($key, self::ENCRYPTED_KEYS, true) && $value !== null) {
                $settings[$key] = self::decryptValue($value);
            }
        }

        return $settings;
    }

    /**
     * Set multiple settings at once for a given group.
     *
     * @param  array<string, string|null>  $settings
     */
    public static function setGroup(string $group, array $settings): void
    {
        foreach ($settings as $key => $value) {
            static::set($key, $value, $group);
        }
    }

    /**
     * Check if a key stores encrypted data.
     */
    public static function isEncryptedKey(string $key): bool
    {
        return in_array($key, self::ENCRYPTED_KEYS, true);
    }

    /**
     * Safely decrypt a value, returning the raw value if decryption fails.
     */
    private static function decryptValue(string $value): string
    {
        try {
            return Crypt::decryptString($value);
        } catch (\Exception) {
            // Value may not be encrypted yet (legacy data)
            return $value;
        }
    }
}
