using System.Security.Cryptography;
using Microsoft.Extensions.Configuration;

namespace Hc900Crawler.Infrastructure.Kb;

public sealed record KbStoredFile(string AbsolutePath, string RelativePath, string Sha256, long Size);

public sealed class KbStorageService
{
    private readonly string _root;

    public KbStorageService(IConfiguration config)
    {
        var configured = config["Kb:StorageRoot"] ?? "../../storage/kb";
        _root = Path.IsPathRooted(configured)
            ? configured
            : Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), configured));
        Directory.CreateDirectory(_root);
    }

    public string Root => _root;

    public async Task<KbStoredFile> SaveAsync(Stream input, string originalFileName, CancellationToken ct = default)
    {
        var now = DateTime.UtcNow;
        var monthDir = Path.Combine(_root, $"{now:yyyy-MM}");
        Directory.CreateDirectory(monthDir);

        var id = Guid.NewGuid();
        var ext = Path.GetExtension(originalFileName);
        if (string.IsNullOrWhiteSpace(ext)) ext = "";
        var fileName = $"{id:N}{ext}";
        var abs = Path.Combine(monthDir, fileName);

        using var fs = new FileStream(abs, FileMode.CreateNew, FileAccess.Write, FileShare.None, 64 * 1024, true);
        using var sha = SHA256.Create();
        var buffer = new byte[64 * 1024];
        long total = 0;
        int read;
        while ((read = await input.ReadAsync(buffer.AsMemory(0, buffer.Length), ct)) > 0)
        {
            sha.TransformBlock(buffer, 0, read, null, 0);
            await fs.WriteAsync(buffer.AsMemory(0, read), ct);
            total += read;
        }
        sha.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
        var hashHex = Convert.ToHexString(sha.Hash!).ToLowerInvariant();

        var rel = Path.Combine($"{now:yyyy-MM}", fileName).Replace('\\', '/');
        return new KbStoredFile(abs, rel, hashHex, total);
    }

    public string Resolve(string relativePath)
    {
        if (Path.IsPathRooted(relativePath)) return relativePath;
        return Path.Combine(_root, relativePath);
    }

    public bool Delete(string relativePath)
    {
        try
        {
            var p = Resolve(relativePath);
            if (File.Exists(p))
            {
                File.Delete(p);
                return true;
            }
        }
        catch { }
        return false;
    }
}
