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

using System.Runtime.InteropServices;

namespace Microsoft.Maui.Platform.Linux.Services;

/// <summary>
/// Factory for creating the appropriate Input Method service.
/// Automatically selects IBus or XIM based on availability.
/// </summary>
public static class InputMethodServiceFactory
{
    private static IInputMethodService? _instance;
    private static readonly object _lock = new();

    /// <summary>
    /// Gets the singleton input method service instance.
    /// </summary>
    public static IInputMethodService Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_lock)
                {
                    _instance ??= CreateService();
                }
            }
            return _instance;
        }
    }

    /// <summary>
    /// Creates the most appropriate input method service for the current environment.
    /// </summary>
    public static IInputMethodService CreateService()
    {
        // Check environment variable for user preference
        var imePreference = Environment.GetEnvironmentVariable("MAUI_INPUT_METHOD");

        if (!string.IsNullOrEmpty(imePreference))
        {
            return imePreference.ToLowerInvariant() switch
            {
                "ibus" => CreateIBusService(),
                "fcitx" or "fcitx5" => CreateFcitx5Service(),
                "xim" => CreateXIMService(),
                "none" => new NullInputMethodService(),
                _ => CreateAutoService()
            };
        }

        return CreateAutoService();
    }

    private static IInputMethodService CreateAutoService()
    {
        // Check GTK_IM_MODULE for hint
        var imModule = Environment.GetEnvironmentVariable("GTK_IM_MODULE")?.ToLowerInvariant();

        // Try Fcitx5 first if it's the configured IM
        if (imModule?.Contains("fcitx") == true && Fcitx5InputMethodService.IsAvailable())
        {
            DiagnosticLog.Debug("InputMethodServiceFactory", "Using Fcitx5");
            return CreateFcitx5Service();
        }

        // Try IBus (most common on modern Linux)
        if (IsIBusAvailable())
        {
            DiagnosticLog.Debug("InputMethodServiceFactory", "Using IBus");
            return CreateIBusService();
        }

        // Try Fcitx5 as fallback
        if (Fcitx5InputMethodService.IsAvailable())
        {
            DiagnosticLog.Debug("InputMethodServiceFactory", "Using Fcitx5");
            return CreateFcitx5Service();
        }

        // Fall back to XIM
        if (IsXIMAvailable())
        {
            DiagnosticLog.Debug("InputMethodServiceFactory", "Using XIM");
            return CreateXIMService();
        }

        // No IME available
        DiagnosticLog.Warn("InputMethodServiceFactory", "No IME available, using null service");
        return new NullInputMethodService();
    }

    private static IInputMethodService CreateIBusService()
    {
        try
        {
            return new IBusInputMethodService();
        }
        catch (Exception ex)
        {
            DiagnosticLog.Error("InputMethodServiceFactory", $"Failed to create IBus service - {ex.Message}");
            return new NullInputMethodService();
        }
    }

    private static IInputMethodService CreateFcitx5Service()
    {
        try
        {
            return new Fcitx5InputMethodService();
        }
        catch (Exception ex)
        {
            DiagnosticLog.Error("InputMethodServiceFactory", $"Failed to create Fcitx5 service - {ex.Message}");
            return new NullInputMethodService();
        }
    }

    private static IInputMethodService CreateXIMService()
    {
        try
        {
            return new X11InputMethodService();
        }
        catch (Exception ex)
        {
            DiagnosticLog.Error("InputMethodServiceFactory", $"Failed to create XIM service - {ex.Message}");
            return new NullInputMethodService();
        }
    }

    private static bool IsIBusAvailable()
    {
        // Check if IBus daemon is running
        var ibusAddress = Environment.GetEnvironmentVariable("IBUS_ADDRESS");
        if (!string.IsNullOrEmpty(ibusAddress))
        {
            return true;
        }

        // Try to load IBus library
        try
        {
            var handle = NativeLibrary.Load("libibus-1.0.so.5");
            NativeLibrary.Free(handle);
            return true;
        }
        catch
        {
            return false;
        }
    }

    private static bool IsXIMAvailable()
    {
        // Check XMODIFIERS environment variable
        var xmodifiers = Environment.GetEnvironmentVariable("XMODIFIERS");
        if (!string.IsNullOrEmpty(xmodifiers) && xmodifiers.Contains("@im="))
        {
            return true;
        }

        // Check if running under X11
        var display = Environment.GetEnvironmentVariable("DISPLAY");
        return !string.IsNullOrEmpty(display);
    }

    /// <summary>
    /// Resets the singleton instance (useful for testing).
    /// </summary>
    public static void Reset()
    {
        lock (_lock)
        {
            _instance?.Shutdown();
            _instance = null;
        }
    }
}
