using System;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;

namespace StellaOps.Scanner.Sbomer.BuildXPlugin.Cas;

/// <summary>
/// Minimal filesystem-backed CAS used when the BuildX generator runs inside CI.
/// </summary>
public sealed class LocalCasClient
{
    private readonly string rootDirectory;
    private readonly string algorithm;

    public LocalCasClient(LocalCasOptions options)
    {
        if (options is null)
        {
            throw new ArgumentNullException(nameof(options));
        }

        algorithm = options.Algorithm.ToLowerInvariant();
        if (!string.Equals(algorithm, "sha256", StringComparison.OrdinalIgnoreCase))
        {
            throw new ArgumentException("Only the sha256 algorithm is supported.", nameof(options));
        }

        rootDirectory = Path.GetFullPath(options.RootDirectory);
    }

    public Task<CasWriteResult> VerifyWriteAsync(CancellationToken cancellationToken)
    {
        ReadOnlyMemory<byte> probe = "stellaops-buildx-probe"u8.ToArray();
        return WriteAsync(probe, cancellationToken);
    }

    public async Task<CasWriteResult> WriteAsync(ReadOnlyMemory<byte> content, CancellationToken cancellationToken)
    {
        var digest = ComputeDigest(content.Span);
        var path = BuildObjectPath(digest);

        Directory.CreateDirectory(Path.GetDirectoryName(path)!);

        await using var stream = new FileStream(
            path,
            FileMode.Create,
            FileAccess.Write,
            FileShare.Read,
            bufferSize: 16 * 1024,
            FileOptions.Asynchronous | FileOptions.SequentialScan);

        await stream.WriteAsync(content, cancellationToken).ConfigureAwait(false);
        await stream.FlushAsync(cancellationToken).ConfigureAwait(false);

        return new CasWriteResult(algorithm, digest, path);
    }

    private string BuildObjectPath(string digest)
    {
        // Layout: <root>/<algorithm>/<first two>/<rest>.bin
        var prefix = digest.Substring(0, 2);
        var suffix = digest[2..];
        return Path.Combine(rootDirectory, algorithm, prefix, $"{suffix}.bin");
    }

    private static string ComputeDigest(ReadOnlySpan<byte> content)
    {
        Span<byte> buffer = stackalloc byte[32];
        SHA256.HashData(content, buffer);
        return Convert.ToHexString(buffer).ToLowerInvariant();
    }
}
