using System.Security.AccessControl;
using System.Security.Principal;
using ClaudeDo.Installer.Checks;
using ClaudeDo.Installer.Core;

namespace ClaudeDo.Installer.Tests.Checks;

public sealed class WriteAccessCheckTests : IDisposable
{
    private readonly List<string> _cleanupDirs = new();

    private string TempDir()
    {
        var dir = Path.Combine(Path.GetTempPath(), $"cdwriteaccess_{Guid.NewGuid():N}");
        Directory.CreateDirectory(dir);
        _cleanupDirs.Add(dir);
        return dir;
    }

    public void Dispose()
    {
        foreach (var dir in _cleanupDirs)
        {
            try { Directory.Delete(dir, recursive: true); } catch { }
        }
    }

    [Fact]
    public async Task Writable_directory_reports_ok()
    {
        var dir = TempDir();
        var check = new WriteAccessCheck();

        var result = await check.RunAsync(new InstallContext { InstallDirectory = dir }, CancellationToken.None);

        Assert.Equal(CheckStatus.Ok, result.Status);
    }

    [Fact]
    public async Task NonExistent_path_under_writable_parent_reports_ok()
    {
        var parent = TempDir();
        var target = Path.Combine(parent, "does", "not", "exist");
        var check = new WriteAccessCheck();

        var result = await check.RunAsync(new InstallContext { InstallDirectory = target }, CancellationToken.None);

        Assert.Equal(CheckStatus.Ok, result.Status);
    }

    [Fact]
    public async Task Unwritable_directory_reports_failed_with_no_leftover_file()
    {
        var dir = TempDir();
        var dirInfo = new DirectoryInfo(dir);
        var identity = WindowsIdentity.GetCurrent().User!;
        var denyRule = new FileSystemAccessRule(identity, FileSystemRights.CreateFiles | FileSystemRights.WriteData, AccessControlType.Deny);

        var acl = dirInfo.GetAccessControl();
        acl.AddAccessRule(denyRule);
        dirInfo.SetAccessControl(acl);

        try
        {
            var check = new WriteAccessCheck();

            var result = await check.RunAsync(new InstallContext { InstallDirectory = dir }, CancellationToken.None);

            Assert.Equal(CheckStatus.Failed, result.Status);
            Assert.Equal(CheckSeverity.Error, result.Severity);
            Assert.Contains(dir, result.Message);
            Assert.Empty(Directory.GetFiles(dir));
        }
        finally
        {
            acl.RemoveAccessRule(denyRule);
            dirInfo.SetAccessControl(acl);
        }
    }
}
