using Dalamud.Interface.ImGuiFileDialog;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using HellionChat.Infrastructure.Hosting;
using HellionChat.Infrastructure.Logging;
using HellionChat.Ipc;
using HellionChat.Themes;
using HellionChat.Ui;
using HellionChat.Util;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace HellionChat;

// Builds the generic-host DI container that drives v1.5.0+. The factory is
// invoked synchronously from Plugin.ctor (after the schema gate clears) so the
// container exists before PluginLifecycle.LoadAsync runs. See plan §1 for the
// deliberate divergence from Lightless' deferred Func-delegate pattern.
internal static class PluginHostFactory
{
    public static IHost Build(Plugin plugin, PluginHostDependencies dependencies)
    {
        return new HostBuilder()
            .UseContentRoot(dependencies.PluginInterface.ConfigDirectory.FullName)
            .ConfigureLogging(logging =>
            {
                logging.ClearProviders();
                logging.AddDalamudLogging(dependencies.PluginLog);
                logging.SetMinimumLevel(LogLevel.Trace);
            })
            .ConfigureServices(services => ConfigureServices(services, plugin, dependencies))
            .Build();
    }

    private static void ConfigureServices(
        IServiceCollection services,
        Plugin plugin,
        PluginHostDependencies dependencies
    )
    {
        // Block A — Dalamud services (21 [PluginService] singletons).
        services.AddSingleton(dependencies);
        services.AddSingleton(dependencies.PluginInterface);
        services.AddSingleton(dependencies.PluginLog);
        services.AddSingleton(dependencies.ChatGui);
        services.AddSingleton(dependencies.ClientState);
        services.AddSingleton(dependencies.CommandManager);
        services.AddSingleton(dependencies.Condition);
        services.AddSingleton(dependencies.DataManager);
        services.AddSingleton(dependencies.Framework);
        services.AddSingleton(dependencies.GameGui);
        services.AddSingleton(dependencies.KeyState);
        services.AddSingleton(dependencies.ObjectTable);
        services.AddSingleton(dependencies.PartyList);
        services.AddSingleton(dependencies.TargetManager);
        services.AddSingleton(dependencies.TextureProvider);
        services.AddSingleton(dependencies.GameInteropProvider);
        services.AddSingleton(dependencies.GameConfig);
        services.AddSingleton(dependencies.Notification);
        services.AddSingleton(dependencies.AddonLifecycle);
        services.AddSingleton(dependencies.PlayerState);
        services.AddSingleton(dependencies.Evaluator);
        services.AddSingleton(dependencies.SelfTestRegistry);

        // Self-references: Plugin and its WindowSystem already exist.
        services.AddSingleton(plugin);
        services.AddSingleton(plugin.WindowSystem);
        services.AddSingleton<PluginLifecycle>();

        // Block B — HellionChat singletons. Factory lambdas because most
        // classes are internal-sealed and the default activator only sees
        // public ctors.
        services.AddSingleton<IPlatformUtil>(_ => new DalamudPlatformUtil());
        services.AddSingleton<IPluginLogProxy>(sp => new DalamudPluginLogProxy(
            sp.GetRequiredService<IPluginLog>()
        ));
        services.AddSingleton<FileDialogManager>(_ => new FileDialogManager());
        services.AddSingleton(sp => new Commands(sp.GetRequiredService<ILogger<Commands>>()));
        services.AddSingleton(sp => new FontManager(
            sp.GetRequiredService<IDalamudPluginInterface>()
        ));
        services.AddSingleton(_ => new StatusBar());
        services.AddSingleton(sp => new IpcManager(sp.GetRequiredService<ILogger<IpcManager>>()));
        services.AddSingleton(sp => new ExtraChat(sp.GetRequiredService<ILogger<ExtraChat>>()));

        services.AddSingleton(sp => new ThemeRegistry(
            Path.Combine(
                sp.GetRequiredService<IDalamudPluginInterface>().ConfigDirectory.FullName,
                "themes"
            ),
            sp.GetRequiredService<ILogger<ThemeRegistry>>()
        ));

        services.AddSingleton(sp => new GameFunctions.GameFunctions(
            sp.GetRequiredService<Plugin>(),
            sp.GetRequiredService<ILogger<GameFunctions.GameFunctions>>(),
            sp.GetRequiredService<ILoggerFactory>()
        ));
        services.AddSingleton(sp => new TypingIpc(
            sp.GetRequiredService<Plugin>(),
            sp.GetRequiredService<ILogger<TypingIpc>>()
        ));

        services.AddSingleton(sp => new Integrations.HonorificService(
            sp.GetRequiredService<IDalamudPluginInterface>(),
            sp.GetRequiredService<ILogger<Integrations.HonorificService>>(),
            sp.GetRequiredService<IFramework>()
        ));
        services.AddSingleton(sp => new Integrations.FailedTellNotifier(
            sp.GetRequiredService<ILogger<Integrations.FailedTellNotifier>>()
        ));
        services.AddSingleton(sp => new Integrations.CustomAudioPlayer(
            sp.GetRequiredService<ILogger<Integrations.CustomAudioPlayer>>()
        ));

        services.AddSingleton(sp => new MessageManager(
            sp.GetRequiredService<Plugin>(),
            sp.GetRequiredService<ILogger<MessageManager>>(),
            sp.GetRequiredService<ILoggerFactory>()
        ));

        // MessageStore is allocated inside MessageManager.ctor; a separate
        // container singleton would double-construct the SQLite handle.
        services.AddSingleton(sp =>
        {
            var pluginRef = sp.GetRequiredService<Plugin>();
            var manager = sp.GetRequiredService<MessageManager>();
            return new AutoTellTabsService(
                pluginRef,
                manager,
                manager.Store,
                sp.GetRequiredService<ILogger<AutoTellTabsService>>()
            );
        });

        // Block C — Windows. WindowSystem.AddWindow is called from
        // PluginLifecycle.LoadAsync on the framework thread.
        services.AddSingleton(sp => new ChatLogWindow(
            sp.GetRequiredService<Plugin>(),
            sp.GetRequiredService<ILogger<ChatLogWindow>>(),
            sp.GetRequiredService<ILoggerFactory>()
        ));
        services.AddSingleton(sp => new SettingsWindow(
            sp.GetRequiredService<Plugin>(),
            sp.GetRequiredService<ILoggerFactory>()
        ));
        services.AddSingleton(sp => new DbViewer(
            sp.GetRequiredService<Plugin>(),
            sp.GetRequiredService<ILogger<DbViewer>>()
        ));
        services.AddSingleton(sp => new InputPreview(sp.GetRequiredService<ChatLogWindow>()));
        services.AddSingleton(sp => new CommandHelpWindow(sp.GetRequiredService<ChatLogWindow>()));
        services.AddSingleton(sp => new SeStringDebugger(sp.GetRequiredService<Plugin>()));
        services.AddSingleton(sp => new DebuggerWindow(sp.GetRequiredService<Plugin>()));
        services.AddSingleton(sp => new FirstRunWizard(sp.GetRequiredService<Plugin>()));

        // Hosted-service adapters: thin wrappers around the existing init
        // methods so the service class bodies stay unchanged. FontManager
        // does not need one — its ctor runs the init inline inside a single
        // SuppressAutoRebuild block on eager resolve.
        services.AddHostedService(sp => new ThemeRegistryInitHostedService(
            sp.GetRequiredService<ThemeRegistry>()
        ));
        services.AddHostedService(sp => new IpcManagerInitHostedService(
            sp.GetRequiredService<IpcManager>()
        ));
        services.AddHostedService(sp => new TypingIpcInitHostedService(
            sp.GetRequiredService<TypingIpc>()
        ));
        services.AddHostedService(sp => new ExtraChatInitHostedService(
            sp.GetRequiredService<ExtraChat>()
        ));
        services.AddHostedService(sp => new MessageManagerInitHostedService(
            sp.GetRequiredService<IDalamudPluginInterface>(),
            sp.GetRequiredService<MessageManager>()
        ));
        services.AddHostedService(sp => new AutoTellTabsServiceInitHostedService(
            sp.GetRequiredService<AutoTellTabsService>()
        ));
        services.AddHostedService(
            sp => new Infrastructure.Hosting.FailedTellNotifierInitHostedService(
                sp.GetRequiredService<Integrations.FailedTellNotifier>()
            )
        );
    }
}

internal sealed record PluginHostDependencies(
    IDalamudPluginInterface PluginInterface,
    IPluginLog PluginLog,
    IChatGui ChatGui,
    IClientState ClientState,
    ICommandManager CommandManager,
    ICondition Condition,
    IDataManager DataManager,
    IFramework Framework,
    IGameGui GameGui,
    IKeyState KeyState,
    IObjectTable ObjectTable,
    IPartyList PartyList,
    ITargetManager TargetManager,
    ITextureProvider TextureProvider,
    IGameInteropProvider GameInteropProvider,
    IGameConfig GameConfig,
    INotificationManager Notification,
    IAddonLifecycle AddonLifecycle,
    IPlayerState PlayerState,
    ISeStringEvaluator Evaluator,
    ISelfTestRegistry SelfTestRegistry
);
