<?php
/**
 * FinanceSettings — Slice 8Z-j · Batch 4
 *
 * Key/value config table owned by Finance. Currently holds the two Product Discovery tunables
 * (cap % of PO total, Exec-threshold % of PO total) — designed to grow into a general Finance
 * settings surface (approval thresholds, retention rates, etc.) without adding new columns.
 *
 * Read is cheap (single row per key); model caches within a request so repeat lookups don't hit
 * the DB. Writes bump `edited_by` + `edited_on` for audit.
 */
class FinanceSettings extends Model
{
    private static array $cache = [];

    /** Get a setting's raw string value. Returns $default if not set. */
    public function get(string $key, string $default = ''): string
    {
        if (array_key_exists($key, self::$cache)) return self::$cache[$key];
        $db = new DB();
        $r = $db->mySelect("SELECT setting_value FROM finance_settings WHERE setting_key = :k", [':k' => $key]);
        $val = $r[0]['setting_value'] ?? $default;
        self::$cache[$key] = $val;
        return $val;
    }

    /** Convenience: get a float — useful for percentages / thresholds. */
    public function getFloat(string $key, float $default = 0.0): float
    {
        $v = $this->get($key, (string)$default);
        return is_numeric($v) ? (float)$v : $default;
    }

    /** All settings for the config page — key => [value, description]. */
    public function all(): array
    {
        $db = new DB();
        $rows = $db->mySelect("SELECT setting_key, setting_value, description, edited_by_name, edited_on FROM finance_settings ORDER BY setting_key", []) ?: [];
        $out = [];
        foreach ($rows as $r) $out[$r['setting_key']] = $r;
        return $out;
    }

    /**
     * Update one or more settings. $updates is [key => value]. Only keys already seeded are
     * writable (prevents typos silently creating new keys).
     * Numeric % values are clamped to [0, 100].
     */
    public function updateMany(array $updates, int $userId, string $userName): array
    {
        if (empty($updates)) return ['ok' => false, 'msg' => 'Nothing to save.'];
        $db = new DB();
        $existing = $db->mySelect("SELECT setting_key FROM finance_settings", []) ?: [];
        $known = array_column($existing, 'setting_key');

        $written = 0;
        foreach ($updates as $k => $v) {
            if (!in_array($k, $known, true)) continue; // silently skip unknown keys
            $val = trim((string)$v);
            // % settings clamp
            if (str_ends_with($k, '_pct')) {
                if (!is_numeric($val)) return ['ok' => false, 'msg' => 'Setting "' . $k . '" must be a number.'];
                $n = (float)$val;
                if ($n < 0 || $n > 100)   return ['ok' => false, 'msg' => 'Setting "' . $k . '" must be between 0 and 100.'];
                $val = (string)$n;
            }
            $db->insertUpdate(
                "UPDATE finance_settings SET setting_value = :v, edited_by = :u, edited_by_name = :un, edited_on = NOW() WHERE setting_key = :k",
                [':v' => $val, ':u' => $userId, ':un' => $userName, ':k' => $k]
            );
            self::$cache[$k] = $val; // refresh in-request cache
            $written++;
        }
        // Cross-key sanity: exec threshold cannot exceed cap.
        $cap  = $this->getFloat('discovery_cap_pct', 5);
        $thr  = $this->getFloat('discovery_exec_threshold_pct', 3);
        if ($thr > $cap) return ['ok' => false, 'msg' => 'Exec threshold (' . $thr . '%) cannot exceed the hard cap (' . $cap . '%). Adjust the cap first.'];

        return ['ok' => true, 'written' => $written];
    }
}
