<?php
// admin/ui_settings.php
// Shared app_settings helpers (supports legacy and current schemas)

if (!function_exists('ua_app_settings_schema')) {
  function ua_app_settings_schema(mysqli $conn): string {
    static $schema = null;
    if ($schema !== null) return $schema;
    try {
      $rs = @$conn->query("SHOW COLUMNS FROM app_settings LIKE 'setting_key'");
      if ($rs && $rs->num_rows > 0) { $schema = 'setting_key'; return $schema; }
    } catch (Throwable $e) {}
    $schema = 'key';
    return $schema;
  }
}

if (!function_exists('ua_settings_schema_marker_file')) {
  function ua_settings_schema_marker_file(mysqli $conn): string {
    $dir = __DIR__ . '/cache/runtime';
    if (!is_dir($dir)) { @mkdir($dir, 0777, true); }
    $host = (string)($conn->host_info ?? '');
    $db = '';
    try {
      $r = @$conn->query('SELECT DATABASE() AS db_name');
      if ($r && ($row = $r->fetch_assoc())) $db = (string)($row['db_name'] ?? '');
    } catch (Throwable $e) {}
    $fingerprint = @filemtime(__FILE__) ?: time();
    return $dir . '/app_settings_schema_' . sha1($host . '|' . $db . '|' . $fingerprint) . '.json';
  }
}

if (!function_exists('ua_settings_schema_ready')) {
  function ua_settings_schema_ready(mysqli $conn): bool {
    if ((string)($_GET['ua_settings_migrate'] ?? '') === '1') return false;
    if ((string)($_ENV['UA_FORCE_SCHEMA_MIGRATE'] ?? '') === '1') return false;
    $file = ua_settings_schema_marker_file($conn);
    if (!is_file($file)) return false;
    $data = json_decode((string)@file_get_contents($file), true);
    $ts = is_array($data) ? (int)($data['ts'] ?? 0) : 0;
    return !empty($data['ok']) && $ts > 0 && (time() - $ts) <= 86400;
  }
}

if (!function_exists('ua_settings_schema_mark_ready')) {
  function ua_settings_schema_mark_ready(mysqli $conn): void {
    $file = ua_settings_schema_marker_file($conn);
    $tmp = $file . '.tmp';
    $data = ['ok' => true, 'ts' => time(), 'source' => 'ua_ensure_settings_table'];
    if (@file_put_contents($tmp, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), LOCK_EX) !== false) {
      @rename($tmp, $file);
    }
  }
}

if (!function_exists('ua_ensure_settings_table')) {
  function ua_ensure_settings_table(mysqli $conn): void {
    if (ua_settings_schema_ready($conn)) return;

    // Create using the newer schema.
    // NOTE: some older DB dumps use (key/value) without updated_at.
    // This function upgrades the existing table safely so UI settings can be saved.
    $sql = "CREATE TABLE IF NOT EXISTS app_settings (
      `id` INT AUTO_INCREMENT PRIMARY KEY,
      `setting_key` VARCHAR(100) NOT NULL,
      `setting_value` LONGTEXT NULL,
      `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      UNIQUE KEY `uniq_setting_key` (`setting_key`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
    @$conn->query($sql);

    // Helper: check column exists
    $col = function(string $c) use ($conn): bool {
      try {
        $c2 = $conn->real_escape_string($c);
        $r = @$conn->query("SHOW COLUMNS FROM app_settings LIKE '$c2'");
        return ($r && $r->num_rows > 0);
      } catch (Throwable $e) { return false; }
    };

    // Ensure updated_at exists (legacy tables may miss it)
    if (!$col('updated_at')) {
      @$conn->query("ALTER TABLE app_settings ADD COLUMN updated_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP");
    }

    $hasSettingKey = $col('setting_key');
    $hasSettingVal = $col('setting_value');
    $hasKey        = $col('key');
    $hasVal        = $col('value');

    // If legacy (key/value) exists, add (setting_key/setting_value) and migrate once.
    if ((!$hasSettingKey || !$hasSettingVal) && $hasKey && $hasVal) {
      if (!$hasSettingKey)  @$conn->query("ALTER TABLE app_settings ADD COLUMN setting_key VARCHAR(100) NULL");
      if (!$hasSettingVal)  @$conn->query("ALTER TABLE app_settings ADD COLUMN setting_value LONGTEXT NULL");
      // Copy only where new columns are empty (safe for repeated runs)
      @$conn->query("UPDATE app_settings SET setting_key=`key` WHERE (setting_key IS NULL OR setting_key='') AND `key` IS NOT NULL AND `key`<>''");
      @$conn->query("UPDATE app_settings SET setting_value=`value` WHERE (setting_value IS NULL OR setting_value='') AND `value` IS NOT NULL");
    }

    // If newer schema exists but legacy columns are missing, add them too (compat)
    // so other parts of the system that read key/value won't break.
    if ($hasSettingKey && $hasSettingVal && (!$hasKey || !$hasVal)) {
      if (!$hasKey) @$conn->query("ALTER TABLE app_settings ADD COLUMN `key` VARCHAR(100) NULL");
      if (!$hasVal) @$conn->query("ALTER TABLE app_settings ADD COLUMN `value` LONGTEXT NULL");
      @$conn->query("UPDATE app_settings SET `key`=setting_key WHERE (`key` IS NULL OR `key`='') AND setting_key IS NOT NULL AND setting_key<>''");
      @$conn->query("UPDATE app_settings SET `value`=setting_value WHERE (`value` IS NULL OR `value`='') AND setting_value IS NOT NULL");
    }

    // Ensure there is a unique index for setting_key when the column exists.
    if ($col('setting_key')) {
      try {
        $r = @$conn->query("SHOW INDEX FROM app_settings WHERE Key_name='uniq_setting_key'");
        if (!($r && $r->num_rows > 0)) {
          @$conn->query("ALTER TABLE app_settings ADD UNIQUE KEY `uniq_setting_key` (`setting_key`)");
        }
      } catch (Throwable $e) {}
    }

    ua_settings_schema_mark_ready($conn);
  }
}

if (!function_exists('ua_get_setting')) {
  function ua_get_setting(mysqli $conn, string $key, $default = null) {
    $schema = ua_app_settings_schema($conn);
    $keyEsc = $conn->real_escape_string($key);
    if ($schema === 'setting_key') {
      $r = @$conn->query("SELECT `setting_value` AS v FROM app_settings WHERE `setting_key`='$keyEsc' LIMIT 1");
    } else {
      $r = @$conn->query("SELECT `value` AS v FROM app_settings WHERE `key`='$keyEsc' LIMIT 1");
    }
    if ($r && ($row = $r->fetch_assoc())) return $row['v'];
    return $default;
  }
}

if (!function_exists('ua_set_setting')) {
  function ua_set_setting(mysqli $conn, string $key, string $value): void {
    $schema = ua_app_settings_schema($conn);
    if ($schema === 'setting_key') {
      $stmt = @$conn->prepare("INSERT INTO app_settings (`setting_key`,`setting_value`) VALUES (?,?)
        ON DUPLICATE KEY UPDATE `setting_value`=VALUES(`setting_value`), updated_at=NOW()");
    } else {
      // Legacy schema may not have updated_at, so update it only if present.
      $hasUpdatedAt = false;
      try {
        $r = @$conn->query("SHOW COLUMNS FROM app_settings LIKE 'updated_at'");
        $hasUpdatedAt = ($r && $r->num_rows > 0);
      } catch (Throwable $e) {}
      if ($hasUpdatedAt) {
        $stmt = @$conn->prepare("INSERT INTO app_settings (`key`,`value`) VALUES (?,?)
          ON DUPLICATE KEY UPDATE `value`=VALUES(`value`), updated_at=NOW()");
      } else {
        $stmt = @$conn->prepare("INSERT INTO app_settings (`key`,`value`) VALUES (?,?)
          ON DUPLICATE KEY UPDATE `value`=VALUES(`value`)");
      }
    }
    if ($stmt) {
      $stmt->bind_param('ss', $key, $value);
      $saved = $stmt->execute();
      $stmt->close();
      if ($saved) {
        // A successful write must invalidate the shared read cache; otherwise
        // another page can keep the previous theme for up to two minutes.
        foreach ((array)@glob(__DIR__ . '/cache/runtime/app_settings_*.json') as $cacheFile) {
          if (is_file($cacheFile)) @unlink($cacheFile);
        }
      }
    }
  }
}

/*
 * UI preferences belong to the physical installation, not to the shared
 * database. Branch and agent installations can intentionally use the same
 * app_settings table, so their keys must be isolated by the stable instance
 * identity. The legacy unscoped values remain a one-time migration source.
 */
if (!function_exists('ua_ui_setting_storage_key')) {
  function ua_ui_setting_storage_key(string $key): string {
    if (!function_exists('ua_install_instance_key')) {
      require_once __DIR__ . '/app_runtime.php';
    }
    $instance = function_exists('ua_install_instance_key') ? ua_install_instance_key() : php_uname('n');
    $type = function_exists('ua_install_type') ? ua_install_type() : 'server';
    $scope = substr(hash('sha256', $type . '|' . $instance), 0, 24);
    return substr('ui_scope_' . $scope . '_' . $key, 0, 100);
  }
}

if (!function_exists('ua_get_ui_setting')) {
  function ua_get_ui_setting(mysqli $conn, string $key, $default = null) {
    $scopedKey = ua_ui_setting_storage_key($key);
    $missing = "\0UA_UI_SETTING_MISSING\0";
    $value = ua_get_setting($conn, $scopedKey, $missing);
    if ($value !== $missing) return $value;

    $value = ua_get_setting($conn, $key, $default);
    if (is_scalar($value) || $value === null) {
      ua_set_setting($conn, $scopedKey, (string)($value ?? ''));
    }
    return $value;
  }
}

if (!function_exists('ua_set_ui_setting')) {
  function ua_set_ui_setting(mysqli $conn, string $key, string $value): void {
    ua_set_setting($conn, ua_ui_setting_storage_key($key), $value);
  }
}

if (!function_exists('ua_hex_to_rgb')) {
  function ua_hex_to_rgb(string $hex): ?array {
    $hex = trim($hex);
    if ($hex === '') return null;
    if ($hex[0] === '#') $hex = substr($hex, 1);
    if (strlen($hex) === 3) {
      $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
    }
    if (!preg_match('/^[0-9a-fA-F]{6}$/', $hex)) return null;
    return [
      hexdec(substr($hex, 0, 2)),
      hexdec(substr($hex, 2, 2)),
      hexdec(substr($hex, 4, 2)),
    ];
  }
}

if (!function_exists('ua_rgb_to_hex')) {
  function ua_rgb_to_hex(int $r, int $g, int $b): string {
    $r = max(0, min(255, $r));
    $g = max(0, min(255, $g));
    $b = max(0, min(255, $b));
    return sprintf('#%02x%02x%02x', $r, $g, $b);
  }
}

if (!function_exists('ua_mix')) {
  // Mix two RGB colors: out = a*(1-t) + b*t
  function ua_mix(array $a, array $b, float $t): array {
    $t = max(0.0, min(1.0, $t));
    return [
      (int)round($a[0] * (1 - $t) + $b[0] * $t),
      (int)round($a[1] * (1 - $t) + $b[1] * $t),
      (int)round($a[2] * (1 - $t) + $b[2] * $t),
    ];
  }
}

if (!function_exists('ua_accent_pair')) {
  // Build accent + accent-2 as a lighter variant for gradients.
  function ua_accent_pair(string $hex): array {
    $rgb = ua_hex_to_rgb($hex);
    if (!$rgb) return ['#f5b000', '#ffcc33'];
    $white = [255, 255, 255];
    $a2 = ua_mix($rgb, $white, 0.25); // 25% towards white
    return [ua_rgb_to_hex($rgb[0], $rgb[1], $rgb[2]), ua_rgb_to_hex($a2[0], $a2[1], $a2[2])];
  }
}


if (!function_exists('ua_runtime_cache_dir')) {
  function ua_runtime_cache_dir(): string {
    $dir = __DIR__ . '/cache/runtime';
    if (!is_dir($dir)) { @mkdir($dir, 0777, true); }
    return $dir;
  }
}

if (!function_exists('ua_runtime_cache_path')) {
  function ua_runtime_cache_path(string $key): string {
    $safe = preg_replace('/[^a-zA-Z0-9_\-\.]/', '_', $key);
    return ua_runtime_cache_dir() . '/' . $safe . '.json';
  }
}

if (!function_exists('ua_runtime_cache_read')) {
  function ua_runtime_cache_read(string $key, int $ttl): ?array {
    $path = ua_runtime_cache_path($key);
    if (!is_file($path)) return null;
    $raw = @file_get_contents($path);
    if ($raw === false || $raw === '') return null;
    $data = json_decode($raw, true);
    if (!is_array($data)) return null;
    $ts = (int)($data['ts'] ?? 0);
    if ($ttl > 0 && $ts > 0 && (time() - $ts) > $ttl) return null;
    return $data;
  }
}

if (!function_exists('ua_runtime_cache_write')) {
  function ua_runtime_cache_write(string $key, array $data): void {
    $data['ts'] = time();
    @file_put_contents(ua_runtime_cache_path($key), json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
  }
}

if (!function_exists('ua_get_settings_map')) {
  function ua_get_settings_map(mysqli $conn, array $keys, int $ttl = 120): array {
    $keys = array_values(array_unique(array_filter(array_map('strval', $keys), static function($v){ return $v !== ''; })));
    if (empty($keys)) return [];

    $schema = ua_app_settings_schema($conn);
    $cacheKey = 'app_settings_' . md5($schema . '|' . implode('|', $keys));
    $cached = ua_runtime_cache_read($cacheKey, $ttl);
    if (is_array($cached) && isset($cached['values']) && is_array($cached['values'])) {
      return $cached['values'];
    }

    $values = [];
    $escaped = [];
    foreach ($keys as $k) {
      $escaped[] = "'" . $conn->real_escape_string($k) . "'";
    }

    if ($schema === 'setting_key') {
      $sql = "SELECT setting_key AS k, setting_value AS v FROM app_settings WHERE setting_key IN (" . implode(',', $escaped) . ")";
    } else {
      $sql = "SELECT `key` AS k, `value` AS v FROM app_settings WHERE `key` IN (" . implode(',', $escaped) . ")";
    }

    $rs = @$conn->query($sql);
    if ($rs) {
      while ($row = $rs->fetch_assoc()) {
        $k = (string)($row['k'] ?? '');
        if ($k !== '') $values[$k] = $row['v'];
      }
      $rs->free();
    }

    ua_runtime_cache_write($cacheKey, ['values' => $values]);
    return $values;
  }
}

if (!function_exists('ua_get_ui_settings_map')) {
  function ua_get_ui_settings_map(mysqli $conn, array $keys, int $ttl = 120): array {
    $keys = array_values(array_unique(array_filter(array_map('strval', $keys), static function($value){
      return $value !== '';
    })));
    if (!$keys) return [];

    $storageKeys = [];
    foreach ($keys as $key) $storageKeys[$key] = ua_ui_setting_storage_key($key);
    $stored = ua_get_settings_map($conn, array_values($storageKeys), $ttl);

    $values = [];
    $missing = [];
    foreach ($storageKeys as $key => $storageKey) {
      if (array_key_exists($storageKey, $stored)) $values[$key] = $stored[$storageKey];
      else $missing[] = $key;
    }

    if ($missing) {
      $legacy = ua_get_settings_map($conn, $missing, $ttl);
      foreach ($missing as $key) {
        if (!array_key_exists($key, $legacy)) continue;
        $values[$key] = $legacy[$key];
        ua_set_ui_setting($conn, $key, (string)$legacy[$key]);
      }
    }
    return $values;
  }
}
