using NexusInstaller.Models;

namespace NexusInstaller.Services;

internal enum IdentityBootstrapPhase
{
    CreateFolders,
    ValidateConfig,
    StartApi,
    WaitHealth,
    Migrations,
    LoadOrganizations,
    LoadRoles,
    DeviceName,
    Enrollment,
}

internal sealed class IdentityBootstrapStatus
{
    public required IdentityBootstrapPhase Phase { get; init; }
    public required IdentityReadinessState State { get; init; }
    public string? Detail { get; init; }
}

internal sealed class IdentityBootstrapResult
{
    public bool Success { get; init; }
    public string? Error { get; init; }
    public IReadOnlyList<OrganizationOption> Organizations { get; init; } = [];
    public IReadOnlyList<RoleOption> Roles { get; init; } = [];
    public string? DeviceId { get; init; }
}

internal sealed class NexusIdentityBootstrapService : IDisposable
{
    private readonly NexusIdentityApiClient _api = new();

    public async Task<IdentityBootstrapResult> RunAsync(
        string installRoot,
        OrganizationOption organization,
        string userEmail,
        string deviceName,
        string roleName,
        string shellVersion,
        string agentVersion,
        IProgress<IdentityBootstrapStatus>? progress = null,
        CancellationToken cancellationToken = default)
    {
        void Report(IdentityBootstrapPhase phase, IdentityReadinessState state, string? detail = null) =>
            progress?.Report(new IdentityBootstrapStatus { Phase = phase, State = state, Detail = detail });

        try
        {
            Report(IdentityBootstrapPhase.CreateFolders, IdentityReadinessState.Running);
            NexusIdentityDataPaths.EnsureDataDirectories(installRoot);
            var dbPath = NexusIdentityDataPaths.ResolveDatabasePath(installRoot);
            Report(IdentityBootstrapPhase.CreateFolders, IdentityReadinessState.Ready, dbPath);

            Report(IdentityBootstrapPhase.ValidateConfig, IdentityReadinessState.Running);
            using (var launcher = new NexusLaunchService(installRoot))
            {
                launcher.ValidateInstall();
            }

            Report(IdentityBootstrapPhase.ValidateConfig, IdentityReadinessState.Ready);

            Report(IdentityBootstrapPhase.StartApi, IdentityReadinessState.Running);
            using (var launcher = new NexusLaunchService(installRoot))
            {
                launcher.StopExisting();
                var startLog = new Progress<string>(msg =>
                    Report(IdentityBootstrapPhase.StartApi, IdentityReadinessState.Running, msg));
                await launcher.StartIdentityAsync(startLog, cancellationToken);
            }

            Report(IdentityBootstrapPhase.WaitHealth, IdentityReadinessState.Running);
            if (!await _api.IsHealthyAsync(ct: cancellationToken))
            {
                Report(IdentityBootstrapPhase.WaitHealth, IdentityReadinessState.Failed, "Health check fejlede");
                return Fail("Identity API svarede ikke på /health efter opstart.");
            }

            Report(IdentityBootstrapPhase.WaitHealth, IdentityReadinessState.Ready);
            Report(IdentityBootstrapPhase.Migrations, IdentityReadinessState.Ready, "Database initialiseret");

            Report(IdentityBootstrapPhase.LoadOrganizations, IdentityReadinessState.Running);
            var orgs = await _api.GetOrganizationsAsync(ct: cancellationToken);
            if (orgs.Count == 0)
            {
                orgs = OrganizationCatalog.All;
            }

            Report(IdentityBootstrapPhase.LoadOrganizations, IdentityReadinessState.Ready, $"{orgs.Count} org(s)");

            Report(IdentityBootstrapPhase.LoadRoles, IdentityReadinessState.Running);
            var roles = await _api.GetRolesAsync(ct: cancellationToken);
            if (roles.Count == 0)
            {
                roles = RoleCatalog.All;
            }

            Report(IdentityBootstrapPhase.LoadRoles, IdentityReadinessState.Ready, $"{roles.Count} rolle(r)");

            var resolvedDeviceName = string.IsNullOrWhiteSpace(deviceName)
                ? ResolveDefaultDeviceName()
                : deviceName.Trim();
            Report(IdentityBootstrapPhase.DeviceName, IdentityReadinessState.Ready, resolvedDeviceName);

            Report(IdentityBootstrapPhase.Enrollment, IdentityReadinessState.Running);
            var deviceId = NexusIdentitySeedService.Seed(
                organization,
                userEmail,
                resolvedDeviceName,
                roleName,
                shellVersion,
                agentVersion);
            Report(IdentityBootstrapPhase.Enrollment, IdentityReadinessState.Ready, deviceId);

            return new IdentityBootstrapResult
            {
                Success = true,
                Organizations = orgs,
                Roles = roles,
                DeviceId = deviceId,
            };
        }
        catch (Exception ex)
        {
            return Fail(ex.Message);
        }
    }

    public static string ResolveDefaultDeviceName()
    {
        var machine = Environment.MachineName;
        return string.IsNullOrWhiteSpace(machine) ? "Nexus-PC" : machine;
    }

    private static IdentityBootstrapResult Fail(string error) =>
        new() { Success = false, Error = error };

    public void Dispose() => _api.Dispose();
}
