using System.Text;
using Kajabity.Tools.Java;

namespace Phantom.Agent.Minecraft.Java;

sealed class JavaPropertiesFileEditor {
	private static readonly Encoding Encoding = Encoding.GetEncoding("ISO-8859-1");

	private readonly Dictionary<string, string> overriddenProperties = new ();

	public void Set(string key, string value) {
		overriddenProperties[key] = value;
	}

	public async Task EditOrCreate(string filePath) {
		if (File.Exists(filePath)) {
			string tmpFilePath = filePath + ".tmp";
			File.Copy(filePath, tmpFilePath, overwrite: true);
			await EditFromCopyOrCreate(filePath, tmpFilePath);
			File.Move(tmpFilePath, filePath, overwrite: true);
		}
		else {
			await EditFromCopyOrCreate(null, filePath);
		}
	}

	private async Task EditFromCopyOrCreate(string? sourceFilePath, string targetFilePath) {
		var properties = new JavaProperties();

		if (sourceFilePath != null) {
			// TODO replace with custom async parser
			await using var sourceStream = new FileStream(sourceFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
			properties.Load(sourceStream, Encoding);
		}

		foreach (var (key, value) in overriddenProperties) {
			properties[key] = value;
		}

		await using var targetStream = new FileStream(targetFilePath, FileMode.Create, FileAccess.Write, FileShare.Read);
		await using var targetWriter = new StreamWriter(targetStream, Encoding);

		await targetWriter.WriteLineAsync("# Properties");

		foreach (var (key, value) in properties) {
			await WriteProperty(targetWriter, key, value);
		}
	}

	private static async Task WriteProperty(StreamWriter writer, string key, string value) {
		await WritePropertyComponent(writer, key, escapeSpaces: true);
		await writer.WriteAsync('=');
		await WritePropertyComponent(writer, value, escapeSpaces: false);
		await writer.WriteLineAsync();
	}

	private static async Task WritePropertyComponent(TextWriter writer, string component, bool escapeSpaces) {
		for (int index = 0; index < component.Length; index++) {
			var c = component[index];
			switch (c) {
				case '\\':
				case '#':
				case '!':
				case '=':
				case ':':
				case ' ' when escapeSpaces || index == 0:
					await writer.WriteAsync('\\');
					await writer.WriteAsync(c);
					break;
				case var _ when c > 31 && c < 127:
					await writer.WriteAsync(c);
					break;
				case '\t':
					await writer.WriteAsync("\\t");
					break;
				case '\n':
					await writer.WriteAsync("\\n");
					break;
				case '\r':
					await writer.WriteAsync("\\r");
					break;
				case '\f':
					await writer.WriteAsync("\\f");
					break;
				default:
					await writer.WriteAsync("\\u");
					await writer.WriteAsync(((int) c).ToString("X4"));
					break;
			}
		}
	}
}