// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices.JavaScript;
using Microsoft.Extensions.HotReload;
using Microsoft.JSInterop;

namespace Microsoft.AspNetCore.Components.WebAssembly.HotReload;

/// <summary>
/// Contains methods called by interop. Intended for framework use only, not supported for use in application
/// code.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static partial class WebAssemblyHotReload
{
    private const string BlazorHotReloadModuleName = "blazor-hotreload";

    private static HotReloadAgent? _hotReloadAgent;
    private static readonly UpdateDelta[] _updateDeltas = new[]
    {
        new UpdateDelta(),
    };

    internal static async Task InitializeAsync()
    {
        if (Environment.GetEnvironmentVariable("__ASPNETCORE_BROWSER_TOOLS") == "true" &&
            OperatingSystem.IsBrowser())
        {
            // Attempt to read previously applied hot reload deltas if the ASP.NET Core browser tools are available (indicated by the presence of the Environment variable).
            // The agent is injected in to the hosted app and can serve this script that can provide results from local-storage.
            // See https://github.com/dotnet/aspnetcore/issues/37357#issuecomment-941237000
            await JSHost.ImportAsync(BlazorHotReloadModuleName, "/_framework/blazor-hotreload.js");
            await ReceiveHotReloadAsync();
        }
    }

    /// <summary>
    /// For framework use only.
    /// </summary>
    [JSInvokable(nameof(ApplyHotReloadDelta))]
    public static void ApplyHotReloadDelta(string moduleIdString, byte[] metadataDelta, byte[] ilDelta, byte[] pdbBytes)
    {
        // Analyzer has a bug where it doesn't handle ConditionalAttribute: https://github.com/dotnet/roslyn/issues/63464
#pragma warning disable IDE0200 // Remove unnecessary lambda expression
        Interlocked.CompareExchange(ref _hotReloadAgent, new HotReloadAgent(m => Debug.WriteLine(m)), null);
#pragma warning restore IDE0200 // Remove unnecessary lambda expression

        var moduleId = Guid.Parse(moduleIdString, CultureInfo.InvariantCulture);

        _updateDeltas[0].ModuleId = moduleId;
        _updateDeltas[0].MetadataDelta = metadataDelta;
        _updateDeltas[0].ILDelta = ilDelta;
        _updateDeltas[0].PdbBytes = pdbBytes;

        _hotReloadAgent.ApplyDeltas(_updateDeltas);
    }

    /// <summary>
    /// For framework use only.
    /// </summary>
    [JSInvokable(nameof(GetApplyUpdateCapabilities))]
    public static string GetApplyUpdateCapabilities()
    {
        var method = typeof(System.Reflection.Metadata.MetadataUpdater).GetMethod("GetCapabilities", BindingFlags.NonPublic | BindingFlags.Static, Type.EmptyTypes);
        if (method is null)
        {
            return string.Empty;
        }
        return (string)method.Invoke(obj: null, parameters: null)!;
    }

    [JSImport("receiveHotReloadAsync", BlazorHotReloadModuleName)]
    private static partial Task ReceiveHotReloadAsync();
}
