using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
namespace WandEnhancer.Core.Js
{
///
/// Loads injected JavaScript from embedded Patches/*.js files so payloads stay
/// lintable source rather than escaped C# string literals.
///
internal static class PatchPayload
{
private const string ResourcePrefix = "patches/";
private static readonly ConcurrentDictionary Cache =
new ConcurrentDictionary(StringComparer.Ordinal);
private static readonly Regex Placeholder = new Regex(@"\$\{(?\w+)\}");
///
/// Loads a payload, replacing each ${name} placeholder from alternating name/value pairs.
/// Substitution is a single pass, so injected bundle text is never rescanned for placeholders.
/// Unknown placeholders are left intact for the caller's own regex replacement to resolve.
///
public static string Load(string name, params string[] placeholders)
{
if (placeholders.Length % 2 != 0)
{
throw new ArgumentException("Placeholders must be name/value pairs", nameof(placeholders));
}
var values = new Dictionary(StringComparer.Ordinal);
for (int index = 0; index < placeholders.Length; index += 2)
{
values[placeholders[index]] = placeholders[index + 1];
}
return Placeholder.Replace(
Cache.GetOrAdd(name, ReadResource),
match => values.TryGetValue(match.Groups["name"].Value, out var value) ? value : match.Value);
}
private static string ReadResource(string name)
{
string resourceName = $"{ResourcePrefix}{name}.js";
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
if (stream == null)
{
throw new FileNotFoundException($"Embedded patch payload not found: {resourceName}");
}
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd().Trim();
}
}
}
}
}