using System.Diagnostics;
using System.Net.Http;
using System.Text.Json;

namespace NexusInstaller.Services;

internal sealed class NexusLaunchService : IDisposable
{
    public const string IdentityBaseUrl = "http://localhost:5050";

    private readonly string _installRoot;
    private Process? _identityProcess;
    private readonly HttpClient _http = new() { Timeout = TimeSpan.FromSeconds(5) };

    public NexusLaunchService(string installRoot)
    {
        _installRoot = installRoot;
    }

    public string IdentityExe => Path.Combine(_installRoot, "identity", "NexusIdentity.Api.exe");
    public string ShellExe => Path.Combine(_installRoot, "shell", "NexusShell.exe");
    public string LogsDir => Path.Combine(_installRoot, "logs");

    public void StopExisting()
    {
        foreach (var name in new[] { "NexusIdentity.Api", "NexusShell" })
        {
            foreach (var process in Process.GetProcessesByName(name))
            {
                try
                {
                    process.Kill(entireProcessTree: true);
                    process.WaitForExit(3000);
                }
                catch
                {
                    // ignore
                }
            }
        }
    }

    public void ValidateInstall()
    {
        if (!File.Exists(ShellExe))
        {
            throw new FileNotFoundException("Nexus Shell not installed.", ShellExe);
        }

        if (!File.Exists(IdentityExe))
        {
            throw new FileNotFoundException("Nexus Identity not installed.", IdentityExe);
        }
    }

    public async Task StartIdentityAsync(
        IProgress<string>? log = null,
        CancellationToken cancellationToken = default)
    {
        NexusIdentityDataPaths.EnsureDataDirectories(_installRoot);
        Directory.CreateDirectory(LogsDir);

        var stdoutLog = Path.Combine(LogsDir, "identity.log");
        var stderrLog = Path.Combine(LogsDir, "identity.err.log");
        var dbConnection = NexusIdentityDataPaths.BuildConnectionString(_installRoot);

        var psi = new ProcessStartInfo
        {
            FileName = IdentityExe,
            WorkingDirectory = Path.GetDirectoryName(IdentityExe)!,
            Arguments = $"--urls {IdentityBaseUrl}",
            UseShellExecute = false,
            CreateNoWindow = true,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
        };
        psi.Environment["NEXUS_DB_CONNECTION"] = dbConnection;

        log?.Report($"SQLite: {NexusIdentityDataPaths.ResolveDatabasePath(_installRoot)}");

        _identityProcess = Process.Start(psi)
            ?? throw new InvalidOperationException(
                $"Kunne ikke starte Identity API. Tjek at .NET 8 ASP.NET Core Runtime er installeret.\nExe: {IdentityExe}");

        _ = Task.Run(async () =>
        {
            await File.AppendAllTextAsync(stdoutLog, await _identityProcess.StandardOutput.ReadToEndAsync(cancellationToken));
        }, cancellationToken);
        _ = Task.Run(async () =>
        {
            await File.AppendAllTextAsync(stderrLog, await _identityProcess.StandardError.ReadToEndAsync(cancellationToken));
        }, cancellationToken);

        log?.Report("Venter på Identity API (/health)...");

        for (var i = 0; i < 30; i++)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (_identityProcess.HasExited)
            {
                var exitCode = _identityProcess.ExitCode;
                var err = await ReadTailAsync(stderrLog);
                throw new InvalidOperationException(
                    $"Identity API stopped (exit {exitCode}). SQLite: {dbConnection}" +
                    (string.IsNullOrWhiteSpace(err) ? "" : $"\n{err}"));
            }

            try
            {
                var json = await _http.GetStringAsync($"{IdentityBaseUrl}/health", cancellationToken);
                using var doc = JsonDocument.Parse(json);
                if (doc.RootElement.TryGetProperty("status", out var status)
                    && status.GetString() == "healthy")
                {
                    log?.Report("Identity API kører.");
                    return;
                }
            }
            catch
            {
                // retry
            }

            await Task.Delay(1000, cancellationToken);
        }

        var tail = await ReadTailAsync(stderrLog);
        throw new TimeoutException(
            "Identity API svarede ikke på /health inden for 30 sekunder. " +
            "Installér .NET 8 ASP.NET Core Runtime og tjek logs\\identity.err.log." +
            (string.IsNullOrWhiteSpace(tail) ? "" : $"\n{tail}"));
    }

    public void StartShell()
    {
        var psi = new ProcessStartInfo
        {
            FileName = ShellExe,
            WorkingDirectory = Path.GetDirectoryName(ShellExe)!,
            Arguments = "--safe-poc",
            UseShellExecute = true,
        };

        _shellProcess = Process.Start(psi)
            ?? throw new InvalidOperationException("Could not start Nexus Shell.");
    }

    private Process? _shellProcess;

    public void CreateDesktopShortcut()
    {
        var desktop = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
        var shortcutPath = Path.Combine(desktop, "Ninja Nexus.lnk");
        var exePath = Process.GetCurrentProcess().MainModule?.FileName
            ?? Path.Combine(AppContext.BaseDirectory, "NexusSetup.exe");

        CreateShortcut(shortcutPath, exePath, $"--launch --install \"{_installRoot}\"", _installRoot);
    }

    private static async Task<string> ReadTailAsync(string path, int maxChars = 2000)
    {
        if (!File.Exists(path))
        {
            return string.Empty;
        }

        try
        {
            var text = await File.ReadAllTextAsync(path);
            return text.Length <= maxChars ? text : text[^maxChars..];
        }
        catch
        {
            return string.Empty;
        }
    }

    private static void CreateShortcut(string shortcutPath, string targetPath, string arguments, string workingDir)
    {
        var script = $@"
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut('{shortcutPath.Replace("'", "''")}')
$Shortcut.TargetPath = '{targetPath.Replace("'", "''")}'
$Shortcut.Arguments = '{arguments.Replace("'", "''")}'
$Shortcut.WorkingDirectory = '{workingDir.Replace("'", "''")}'
        $Shortcut.Description = 'Ninja Nexus'
$Shortcut.Save()
";
        var psi = new ProcessStartInfo
        {
            FileName = "powershell.exe",
            Arguments = $"-NoProfile -ExecutionPolicy Bypass -Command \"{script.Replace("\"", "\\\"")}\"",
            UseShellExecute = false,
            CreateNoWindow = true,
        };
        Process.Start(psi)?.WaitForExit(15000);
    }

    public void Dispose()
    {
        _http.Dispose();
    }
}
