Editor/Bridge/SecboxConfig.cs
using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Sandbox.SecBox.Bridge;

// User-global secbox configuration. Persisted to
// %LOCALAPPDATA%/secbox/config.json. Hand-editable; menu items also write
// here. Distinct from the per-project TrustStore (which holds package
// decisions) - this controls bridge / loader behaviour.
public sealed class SecboxConfig
{
	static readonly JsonSerializerOptions JsonOpts = new()
	{
		WriteIndented = true,
		PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
		Converters = { new JsonStringEnumConverter() },
		DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
	};

	// When true, SecboxCoreLoader skips download + SHA-256 verification and
	// loads Secbox.Core from DevPath (or CorePolicy.DevDefaultPath if blank).
	public bool DevMode { get; set; } = false;

	// Optional override path. If null/empty + DevMode is true, the loader
	// uses CorePolicy.DevDefaultPath ("%LOCALAPPDATA%/secbox/core/dev").
	public string DevPath { get; set; }

	// Allow CorePolicy to fetch + cache new Secbox.Core versions on demand.
	// Off = adapter only uses what's already in cache; useful for offline /
	// audit-locked machines.
	public bool AutoUpdate { get; set; } = true;

	// Enable per-exception trace logging via AppDomain.FirstChanceException.
	// Off by default - every caught exception in the entire editor process
	// would land in the log, flooding it. Turn on only when chasing a specific
	// "why did this throw silently" bug.
	public bool VerboseDiagnostics { get; set; } = false;

	// Runtime monitoring (Tier E managed-call enforcement). Force-enabled -
	// installs no drivers, no services; just attaches the in-editor Harmony
	// hook that intercepts library Process.Start. Always on: this getter is a
	// constant, so a "runtimeMonitoringEnabled": false in config.json is ignored
	// (no setter for STJ to write into). To stop the hook for one session use
	// secbox > Runtime Monitoring > Detach Now; it re-attaches on next boot.
	public bool RuntimeMonitoringEnabled => true;

	// Tier E enforcement - when a library-attributed Process.Start is
	// detected, refuse to let it run. The Harmony prefix in ManagedCallSensor
	// returns false; the original Process.Start never executes; library code
	// sees a null result (and usually NREs at the next member access).
	//
	// Force-enabled: active prevention is always on. Like RuntimeMonitoringEnabled
	// this getter is a constant, so a "blockLibraryProcessStart": false in
	// config.json is ignored (no setter for STJ to write into).
	public bool BlockLibraryProcessStart => true;

	// Pop a modal dialog whenever a Critical-severity finding arrives.
	// On by default - the whole point of Critical is "user must see this".
	// Disable for headless / batch use where attended UI isn't practical.
	public bool ShowDetectionDialog { get; set; } = true;

	// Welcome dialogue - when true, suppress the first-install welcome
	// across every project this user opens. Set by the "don't show again"
	// checkbox in the welcome dialogue. Per-install suppression is handled
	// separately by a marker file at <libraryRoot>/.welcome-shown, which
	// vanishes automatically when the user uninstalls the library.
	public bool WelcomeDialogueDismissedGlobally { get; set; } = false;

	public static string FilePath =>
		Path.Combine(
			Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
			"secbox", "config.json");

	public static SecboxConfig Load()
	{
		try
		{
			if (File.Exists(FilePath))
			{
				var json = File.ReadAllText(FilePath);
				return JsonSerializer.Deserialize<SecboxConfig>(json, JsonOpts) ?? new SecboxConfig();
			}
		}
		catch (Exception ex)
		{
			global::Sandbox.Internal.GlobalGameNamespace.Log.Warning(
				$"[secbox] config unreadable, using defaults: {ex.Message}");
		}
		return new SecboxConfig();
	}

	public void Save()
	{
		try
		{
			var dir = Path.GetDirectoryName(FilePath);
			if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
				Directory.CreateDirectory(dir);
			File.WriteAllText(FilePath, JsonSerializer.Serialize(this, JsonOpts));
		}
		catch (Exception ex)
		{
			global::Sandbox.Internal.GlobalGameNamespace.Log.Error(
				$"[secbox] could not save config: {ex.Message}");
		}
	}
}