using System.IO;
using ClaudeDo.Installer.Core;
using Microsoft.Win32;

namespace ClaudeDo.Installer.Steps;

/// <summary>
/// Registers ClaudeDo under <c>HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ClaudeDo</c>
/// so it shows up in Windows "Apps &amp; Features" / "Programs and Features".
/// Also copies the running installer into the install directory so there is an exe
/// for UninstallString to reference after the temp-extracted single-file bundle is gone.
/// </summary>
public sealed class WriteUninstallRegistryStep : IInstallStep
{
    internal const string UninstallKeyPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ClaudeDo";

    public string Name => "Register in Add/Remove Programs";

    public async Task<StepResult> ExecuteAsync(InstallContext ctx, IProgress<string> progress, CancellationToken ct)
    {
        var uninstallDir = Path.Combine(ctx.InstallDirectory, "uninstaller");
        Directory.CreateDirectory(uninstallDir);
        var targetExe = Path.Combine(uninstallDir, "ClaudeDo.Installer.exe");

        // Copy the running installer so Apps & Features has a stable exe to launch —
        // the single-file temp extract is gone once this process exits.
        var sourceExe = Environment.ProcessPath
            ?? throw new InvalidOperationException("Cannot resolve running installer path.");
        // When relaunched from the installed copy (e.g. the Apps & Features "Rerun
        // Installer" entry points at uninstaller/ClaudeDo.Installer.exe), source == target
        // and the copy would throw. Skip it; the binary is already in place.
        var alreadyInPlace = string.Equals(
            Path.GetFullPath(sourceExe), Path.GetFullPath(targetExe), StringComparison.OrdinalIgnoreCase);
        if (!alreadyInPlace)
        {
            try
            {
                progress.Report("Copying uninstaller binary...");
                File.Copy(sourceExe, targetExe, overwrite: true);
            }
            catch (Exception ex)
            {
                return StepResult.Fail($"Failed to copy uninstaller exe: {ex.Message}");
            }
        }

        progress.Report("Writing Add/Remove Programs entry...");
        try
        {
            using var key = Registry.LocalMachine.CreateSubKey(UninstallKeyPath, writable: true);
            if (key is null)
                return StepResult.Fail("Could not open uninstall registry key (permission denied?).");

            key.SetValue("DisplayName", "ClaudeDo", RegistryValueKind.String);
            key.SetValue("DisplayVersion", ctx.InstallerVersion ?? "0.0.0", RegistryValueKind.String);
            key.SetValue("Publisher", "Mika Kuns", RegistryValueKind.String);
            key.SetValue("InstallLocation", ctx.InstallDirectory, RegistryValueKind.String);
            key.SetValue("UninstallString", $"\"{targetExe}\"", RegistryValueKind.String);
            key.SetValue("DisplayIcon", targetExe, RegistryValueKind.String);
            key.SetValue("NoModify", 1, RegistryValueKind.DWord);
            key.SetValue("NoRepair", 1, RegistryValueKind.DWord);

            // Best-effort install size (KB) — scan install dir.
            try
            {
                var sizeKb = (int)(DirectorySizeBytes(ctx.InstallDirectory) / 1024);
                key.SetValue("EstimatedSize", sizeKb, RegistryValueKind.DWord);
            }
            catch { /* best-effort only */ }
        }
        catch (Exception ex)
        {
            return StepResult.Fail($"Failed to write uninstall registry: {ex.Message}");
        }

        await Task.CompletedTask;
        return StepResult.Ok();
    }

    private static long DirectorySizeBytes(string path)
    {
        long total = 0;
        foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
        {
            try { total += new FileInfo(file).Length; } catch { /* ignore */ }
        }
        return total;
    }
}
