using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

namespace Webserver.WebAPI {
	public class OpenApiHelpers {
		private const string masterResourceName = "openapi.master.yaml";
		private const string masterDocName = "openapi.yaml";

		private const string masterDocRefLineBegin = "    - $ref: ";
		private const string masterDocRefLineEnd = "#/paths";

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

		public OpenApiHelpers () {
			loadMainSpec ();
			Web.ServerInitialized += _ => {
				buildMainSpecRefs ();
			};
		}

		private void loadMainSpec () {
			Assembly apiAssembly = GetType ().Assembly;

			string specText = ResourceHelpers.GetManifestResourceText (apiAssembly, masterResourceName, true);
			if (specText == null) {
				Log.Warning ($"[Web] Failed loading main OpenAPI spec from assembly '{Path.GetFileName (apiAssembly.Location)}'");
				return;
			}

			specs.Add (masterDocName, specText);
			Log.Out ($"[Web] Loaded main OpenAPI spec");
		}

		private void buildMainSpecRefs () {
			if (!TryGetOpenApiSpec (null, out string mainSpec)) {
				return;
			}

			StringBuilder sb = new StringBuilder (mainSpec);
			
			foreach ((string apiSpecName, _) in specs) {
				if (apiSpecName.Equals (masterDocName)) {
					continue;
				}

				sb.AppendLine ($"{masterDocRefLineBegin}./{apiSpecName}{masterDocRefLineEnd}");
			}

			specs[masterDocName] = sb.ToString ();
			
			Log.Out ("[Web] OpenAPI preparation done");
		}

		public void LoadOpenApiSpec (AbsWebAPI _api) {
			Assembly apiAssembly = _api.GetType ().Assembly;
			string apiName = _api.Name;
			string apiSpecName = $"{apiName}.openapi.yaml";

			string specText = ResourceHelpers.GetManifestResourceText (apiAssembly, apiSpecName, true);
			if (specText == null) {
				return;
			}

			Log.Out ($"[Web] Loaded OpenAPI spec for '{apiName}'");
			specs.Add (apiSpecName, specText);
		}

		public bool TryGetOpenApiSpec (string _name, out string _specText) {
			if (string.IsNullOrEmpty (_name)) {
				_name = masterDocName;
			}

			return specs.TryGetValue (_name, out _specText);
		}
	}
}