using System.Text.Json;
using System.Text.Json.Serialization;

namespace YMhut.Box.Core.Plugins;

public static class PluginHostProtocol
{
    public const string Version = "1";
    public const string Ready = "ready";
    public const string Ping = "ping";
    public const string Pong = "pong";
    public const string GetSnapshot = "getSnapshot";
    public const string Reload = "reload";
    public const string SetPluginEnabled = "setPluginEnabled";
    public const string SetPermission = "setPermission";
    public const string SetSurfaceMounted = "setSurfaceMounted";
    public const string BridgeCall = "bridgeCall";
    public const string SnapshotChanged = "snapshotChanged";
    public const string Shutdown = "shutdown";
    public const string Error = "error";

    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
    {
        WriteIndented = false,
        PropertyNameCaseInsensitive = true,
        Converters = { new JsonStringEnumConverter<PluginPermission>(), new JsonStringEnumConverter<PluginSurfaceKind>() }
    };

    public static string Serialize(PluginHostMessage message)
    {
        return JsonSerializer.Serialize(message, JsonOptions);
    }

    public static PluginHostMessage? Deserialize(string line)
    {
        return JsonSerializer.Deserialize<PluginHostMessage>(line, JsonOptions);
    }
}

public enum PluginHostStatus
{
    Stopped,
    Starting,
    Ready,
    Failed
}

public sealed record PluginHostMessage(
    string Type,
    string RequestId = "",
    string? Version = null,
    string? PluginId = null,
    string? SurfaceId = null,
    PluginPermission? Permission = null,
    bool? Enabled = null,
    bool? Granted = null,
    bool? Mounted = null,
    PluginSnapshot? Snapshot = null,
    PluginBridgeRequest? BridgeRequest = null,
    PluginBridgeResponse? BridgeResponse = null,
    string? Error = null);

public sealed record PluginSnapshot(
    bool PluginsEnabled,
    string PluginsRoot,
    IReadOnlyList<LoadedPluginDto> Plugins,
    IReadOnlyList<PluginToolDto> EnabledTools,
    DateTimeOffset LoadedAt);

public sealed record LoadedPluginDto(
    PluginManifest Manifest,
    string RootPath,
    PluginRuntimeStateDto State,
    IReadOnlyList<string> Errors)
{
    public bool IsValid => Errors.Count == 0;

    public LoadedPlugin ToLoadedPlugin()
    {
        return new LoadedPlugin(
            Manifest,
            RootPath,
            State.ToRuntimeState(),
            Errors);
    }

    public static LoadedPluginDto FromLoadedPlugin(LoadedPlugin plugin)
    {
        return new LoadedPluginDto(
            plugin.Manifest,
            plugin.RootPath,
            PluginRuntimeStateDto.FromRuntimeState(plugin.State),
            plugin.Errors.ToArray());
    }
}

public sealed record PluginRuntimeStateDto(
    string PluginId,
    bool Enabled,
    IReadOnlyList<PluginPermission> GrantedPermissions,
    IReadOnlyList<string> MountedSurfaceIds,
    DateTimeOffset? LastRunAt)
{
    public PluginRuntimeState ToRuntimeState()
    {
        return new PluginRuntimeState(
            PluginId,
            Enabled,
            GrantedPermissions.ToHashSet(),
            MountedSurfaceIds.ToHashSet(StringComparer.OrdinalIgnoreCase),
            LastRunAt);
    }

    public static PluginRuntimeStateDto FromRuntimeState(PluginRuntimeState state)
    {
        return new PluginRuntimeStateDto(
            state.PluginId,
            state.Enabled,
            state.GrantedPermissions.ToArray(),
            state.MountedSurfaceIds.ToArray(),
            state.LastRunAt);
    }
}

public sealed record PluginToolDto(
    string PluginId,
    string SurfaceId,
    string ToolId,
    PluginManifest Manifest,
    PluginSurface Surface,
    string RootPath,
    PluginRuntimeStateDto State)
{
    public PluginToolModule ToToolModule()
    {
        var plugin = new LoadedPlugin(Manifest, RootPath, State.ToRuntimeState(), []);
        return new PluginToolModule(plugin, Surface);
    }

    public static PluginToolDto FromPluginToolModule(PluginToolModule module)
    {
        return new PluginToolDto(
            module.Plugin.Manifest.Id,
            module.Surface.Id,
            module.Id,
            module.Plugin.Manifest,
            module.Surface,
            module.Plugin.RootPath,
            PluginRuntimeStateDto.FromRuntimeState(module.Plugin.State));
    }
}

public sealed record PluginBridgeRequest(
    string PluginId,
    string SurfaceId,
    string Method,
    string PayloadJson);

public sealed record PluginBridgeResponse(
    bool Ok,
    string? ValueJson = null,
    string? Error = null,
    string? UiAction = null);
