Editor/ScfuConfigSchema.cs
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Sandbox;

/// <summary>
/// Editor-side mirror of <c>Scfu.Core.ScfuConfig</c>. Property names match
/// the upstream class so System.Text.Json round-trips losslessly under the
/// shared camelCase naming policy. Each section carries
/// <see cref="JsonExtensionDataAttribute"/> so unknown fields (added by a
/// newer SCFU release the editor schema hasn't been updated for yet) survive
/// a load/save cycle instead of being silently dropped.
/// </summary>
public sealed class ScfuConfigSchema
{
	public static readonly JsonSerializerOptions JsonOptions = new()
	{
		WriteIndented = true,
		PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
		DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
		ReadCommentHandling = JsonCommentHandling.Skip,
		AllowTrailingCommas = true,
		Converters = { new JsonStringEnumConverter() }
	};

	[Property, Title( "Config Version" ), Group( "General" )]
	public int Version { get; set; } = 1;

	[Property, Title( "Seed" ), Description( "Deterministic seed used for name generation and per-class decoder variants." ), Group( "General" )]
	public string Seed { get; set; } = "scfu-default-seed";

	[Property, Title( "Include Globs" ), Description( "File patterns SCFU's workspace scanner considers." ), Group( "General" )]
	public List<string> Include { get; set; } = new()
	{
		"**/*.cs",
		"**/*.razor",
		"**/*.scss",
		"**/*.json",
		"**/*.scene",
		"**/*.prefab",
		"**/*.resource",
		"**/*.sbproj"
	};

	[Property, Title( "Exclude Globs" ), Description( "File patterns excluded from the scan. 'Libraries/**' is force-added by the s&box defaults layer." ), Group( "General" )]
	public List<string> Exclude { get; set; } = new()
	{
		".git/**",
		".scfu/**",
		"**/bin/**",
		"**/obj/**",
		"**/packages/**",
		"**/Generated/**",
		"Libraries/**",
		"**/Libraries/**"
	};

	public ScfuSafetyConfig Safety { get; set; } = new();
	public ScfuCSharpConfig CSharp { get; set; } = new();
	public ScfuRazorConfig Razor { get; set; } = new();
	public ScfuScssConfig Scss { get; set; } = new();
	public ScfuJsonAssetsConfig JsonAssets { get; set; } = new();
	public ScfuFilenameConfig Filenames { get; set; } = new();

	[Hide, JsonExtensionData]
	public Dictionary<string, JsonElement> Extra { get; set; }
}

public sealed class ScfuSafetyConfig
{
	[Property, Title( "Require Clean Git" ), Description( "Refuse to fuck unless the git working tree is clean." )]
	public bool RequireCleanGit { get; set; } = false;

	[Property, Title( "Snapshot Before Fuck" ), Description( "Take an unfuckable snapshot of the project before mutation." )]
	public bool SnapshotBeforeFuck { get; set; } = true;

	[Property, Title( "Refuse If Already Fucked" ), Description( "Abort if the project is already in the Fucked state." )]
	public bool RefuseIfAlreadyFucked { get; set; } = true;

	[Hide, JsonExtensionData]
	public Dictionary<string, JsonElement> Extra { get; set; }
}

public sealed class ScfuCSharpConfig
{
	[Property, Title( "Rename Private Members" ), Group( "Renaming" )]
	public bool RenamePrivateMembers { get; set; } = true;

	[Property, Title( "Rename Locals" ), Group( "Renaming" )]
	public bool RenameLocals { get; set; } = true;

	[Property, Title( "Rename Parameters" ), Group( "Renaming" )]
	public bool RenameParameters { get; set; } = true;

	[Property, Title( "Rename Public Types" ), Description( "Rename top-level user types (classes/structs/records/interfaces/enums/delegates). Skips razor wrapper classes and types with preservation attributes." ), Group( "Renaming" )]
	public bool RenamePublicTypes { get; set; } = true;

	[Property, Title( "Preserve Lifecycle Methods" ), Group( "Renaming" )]
	public bool PreserveLifecycleMethods { get; set; } = true;

	[Property, Title( "Mangle Serialized Members" ), Description( "OFF by default - mangling [Property] members can break razor markup whose receivers don't resolve cleanly through the snippet binding." ), Group( "Renaming" )]
	public bool MangleSerializedMembers { get; set; } = false;

	[Property, Title( "Strip Comments" ), Group( "Cleanup" )]
	public bool StripComments { get; set; } = true;

	[Property, Title( "Strip Logs" ), Description( "Remove statement-level log calls matching LogCallPatterns." ), Group( "Cleanup" )]
	public bool StripLogs { get; set; } = true;

	[Property, Title( "Log Call Patterns" ), Description( "Receiver.Method patterns matched as log calls. Matches when the receiver ends with the prefix." ), Group( "Cleanup" )]
	public List<string> LogCallPatterns { get; set; } = new()
	{
		"Log.Info", "Log.Warning", "Log.Warn", "Log.Error", "Log.Trace",
		"Console.WriteLine", "Console.Write",
		"Debug.WriteLine", "Debug.Log", "Debug.Write"
	};

	[Property, Title( "Minify Whitespace" ), Group( "Cleanup" )]
	public bool MinifyWhitespace { get; set; } = true;

	[Property, Title( "Encode String Literals" ), Group( "Literal Encoding" )]
	public bool EncodeStringLiterals { get; set; } = true;

	[Property, Title( "Per-Class String Decoders" ), Description( "Inject a unique decoder method per class instead of routing all literals through one shared decoder." ), Group( "Literal Encoding" )]
	public bool PerClassStringDecoders { get; set; } = true;

	[Property, Title( "Encode Numeric Literals" ), Description( "Replace eligible int/uint/long/ulong literals with arithmetic identities. Constant-context literals are skipped." ), Group( "Literal Encoding" )]
	public bool EncodeNumericLiterals { get; set; } = true;

	[Property, Title( "Indirect Method Calls" ), Description( "Hoist static-method calls through per-type Action/Func fields." ), Group( "Control-Flow Obfuscation" )]
	public bool IndirectMethodCalls { get; set; } = true;

	[Property, Title( "Skip Framework Calls" ), Description( "Don't indirect calls into System.*/Microsoft.* and engine math/util classes." ), Group( "Control-Flow Obfuscation" )]
	public bool SkipIndirectingFrameworkCalls { get; set; } = true;

	[Property, Title( "Flatten Control Flow" ), Description( "Rewrite eligible void methods as state-machine switch loops." ), Group( "Control-Flow Obfuscation" )]
	public bool FlattenControlFlow { get; set; } = true;

	[Property, Title( "Use Opaque Predicates" ), Description( "Gate real bodies behind always-true predicates with a dead branch into decoy systems." ), Group( "Control-Flow Obfuscation" )]
	public bool UseOpaquePredicates { get; set; } = true;

	[Property, Title( "Split Methods" ), Description( "Outline eligible methods into many 1–2 statement fragment calls." ), Group( "Control-Flow Obfuscation" )]
	public bool SplitMethods { get; set; } = true;

	[Property, Title( "Merge Methods" ), Description( "Fuse same-bucket private methods within a type into one dispatcher." ), Group( "Control-Flow Obfuscation" )]
	public bool MergeMethods { get; set; } = true;

	[Property, Title( "Goto Spaghetti" ), Description( "Re-link statement-only methods with goto/label to scramble source order." ), Group( "Control-Flow Obfuscation" )]
	public bool GotoSpaghetti { get; set; } = true;

	[Property, Title( "Value-Dependent Opaque Predicates" ), Description( "Reference a precomputed RuntimeEntropy.State value so a naive simplifier can't fold the predicate." ), Group( "Control-Flow Obfuscation" )]
	public bool ValueDependentOpaquePredicates { get; set; } = true;

	[Property, Title( "Harmless Dead Branches" ), Description( "Prefer harmless arithmetic over fake decoy-system calls in dead branches." ), Group( "Control-Flow Obfuscation" )]
	public bool HarmlessDeadBranches { get; set; } = true;

	[Property, Title( "Wrap In Bogus Try/Catch" ), Description( "Wrap bodies in try/catch with an always-false 'when' filter. Iterator methods are skipped." ), Group( "Control-Flow Obfuscation" )]
	public bool WrapMethodsInBogusTryCatch { get; set; } = true;

	[Property, Title( "Inject Decoy Systems" ), Description( "Inject decoy classes used by opaque predicates / dead code. Required for those features to compile." ), Group( "Decoys & Attributes" )]
	public bool InjectDecoySystems { get; set; } = true;

	[Property, Title( "Inject Dead Code" ), Description( "Scatter always-false opaque-predicate blocks containing decoy calls between real statements." ), Group( "Decoys & Attributes" )]
	public bool InjectDeadCode { get; set; } = true;

	[Property, Title( "Shuffle Fields" ), Description( "Permute field declarations and interleave decoy fields. Skips [StructLayout]/[FieldOffset] types." ), Group( "Decoys & Attributes" )]
	public bool ShuffleFields { get; set; } = true;

	[Property, Title( "Inject Fake Attributes" ), Description( "Sprinkle CompilerGenerated/DebuggerHidden/DebuggerStepThrough/StackTraceHidden attributes onto ~35% of methods." ), Group( "Decoys & Attributes" )]
	public bool InjectFakeAttributes { get; set; } = true;

	[Property, Title( "Mangle Display Names" ), Description( "Rewrite [Title]/[Description]/[Group]/[Category]/[Icon] string arguments to misleading values." ), Group( "Decoys & Attributes" )]
	public bool MangleDisplayNames { get; set; } = true;

	[Property, Title( "Randomize Internal Names" ), Description( "Derive SCFU's internal symbol prefixes from Seed so a deobfuscator can't regex out artefacts across builds." ), Group( "Symbols" )]
	public bool RandomizeInternalNames { get; set; } = true;

	[Property, Title( "Mangle Decoy Names" ), Description( "Give injected decoy classes rename-pass-style identifiers (_t_xxxxxx) instead of bland utility names. Requires Randomize Internal Names." ), Group( "Symbols" )]
	public bool MangleDecoyNames { get; set; } = true;

	[Property, Title( "Global Symbol Prefix" ), Description( "Optional user-chosen prefix prepended to every SCFU-injected name. Useful to tag SCFU's output in crash logs." ), Group( "Symbols" )]
	public string GlobalSymbolPrefix { get; set; } = "";

	[Property, Title( "Preserve Attributes" ), Description( "Attribute names whose presence prevents rename. The S&box defaults layer pre-populates this if empty." ), Group( "Preservation" )]
	public List<string> PreserveAttributes { get; set; } = new();

	[Property, Title( "Lifecycle Methods" ), Description( "Method names preserved even when private. The S&box defaults layer pre-populates this if empty." ), Group( "Preservation" )]
	public List<string> LifecycleMethods { get; set; } = new();

	[Hide, JsonExtensionData]
	public Dictionary<string, JsonElement> Extra { get; set; }
}

public sealed class ScfuRazorConfig
{
	[Property, Title( "Rename Razor Files" )]
	public bool RenameRazorFiles { get; set; } = true;

	[Property, Title( "Rename Component Tags" )]
	public bool RenameComponentTags { get; set; } = true;

	[Property, Title( "Rewrite C# Type References" )]
	public bool RewriteCSharpTypeReferences { get; set; } = true;

	[Property, Title( "Rewrite SCSS Companion Selectors" )]
	public bool RewriteScssCompanionSelectors { get; set; } = true;

	[Hide, JsonExtensionData]
	public Dictionary<string, JsonElement> Extra { get; set; }
}

public sealed class ScfuScssConfig
{
	[Property, Title( "Rename SCSS Files" )]
	public bool RenameScssFiles { get; set; } = true;

	[Property, Title( "Rewrite Imports" )]
	public bool RewriteImports { get; set; } = true;

	[Property, Title( "Rewrite StyleSheet Attributes" )]
	public bool RewriteStyleSheetAttributes { get; set; } = true;

	[Property, Title( "Rename CSS Classes" ), Description( "Mangle CSS classes that appear in both razor class=\"…\" and an .scss .classname AND aren't referenced as a C# string literal." )]
	public bool RenameCssClasses { get; set; } = true;

	[Property, Title( "Rename CSS IDs" ), Description( "OFF by default - IDs are more likely to be referenced dynamically." )]
	public bool RenameCssIds { get; set; } = false;

	[Hide, JsonExtensionData]
	public Dictionary<string, JsonElement> Extra { get; set; }
}

public sealed class ScfuJsonAssetsConfig
{
	[Property, Title( "Rewrite Type References" ), Description( "Rewrite type-name strings in .scene/.prefab/.resource JSON when their C# type was renamed." )]
	public bool RewriteTypeReferences { get; set; } = true;

	[Property, Title( "Rewrite Guessed References" ), Description( "Risky - rewrites strings the pipeline only guessed are references. OFF by default." )]
	public bool RewriteGuessedReferences { get; set; } = false;

	[Hide, JsonExtensionData]
	public Dictionary<string, JsonElement> Extra { get; set; }
}

public sealed class ScfuFilenameConfig
{
	[Property, Title( "Rename C# Files" ), Description( "Rename .cs files to match the renamed top-level type." )]
	public bool RenameCSharpFiles { get; set; } = true;

	[Hide, JsonExtensionData]
	public Dictionary<string, JsonElement> Extra { get; set; }
}