using Amazon.S3.Model;
using InnovEnergy.Lib.S3Utils.Data;
using InnovEnergy.Lib.Utils;
using S3Bucket = InnovEnergy.Lib.S3Utils.Data.S3Bucket;

namespace InnovEnergy.Lib.S3Utils;

public static class S3
{
    public static S3RegionCredentials Region(this S3Credentials credentials, String region) => new()
    {
        Secret = credentials.Secret,
        Key    = credentials.Key,
        Region = region,
    };
    
    public static S3Bucket Bucket(this S3RegionCredentials region, String bucket) => new()
    {
        Region = region.Region,
        Secret = region.Secret,
        Key    = region.Key,
        Bucket = bucket,
    };
    
    public static S3Path Path(this S3Bucket bucket, String path) => new()
    {
        Bucket = bucket.Bucket,
        Region = bucket.Region,
        Secret = bucket.Secret,
        Key    = bucket.Key,
        Path   = path
    };

    public static IAsyncEnumerable<S3Path> ListObjects(this S3Bucket bucketOrPrefixPath)
    {
        var path = bucketOrPrefixPath as S3Path ?? bucketOrPrefixPath.Path(null!);

        var request = new ListObjectsV2Request
        {
            BucketName = path.Bucket,
            Prefix     = path.Path
        };

        return bucketOrPrefixPath
              .Client
              .Paginators
              .ListObjectsV2(request)
              .Responses
              .SelectMany(r => r.S3Objects)
              .Select(o => path with { Path = o.Key });
    }
    
    public static async Task<String> GetObject(this S3Path path)
    {
        var request = new GetObjectRequest
        {
            BucketName = path.Bucket,
            Key        = path.Path
        };

        using var response = await path.Client.GetObjectAsync(request);
        await using var responseStream = response.ResponseStream;
        using var reader = new StreamReader(responseStream);

        return await reader.ReadToEndAsync();
    }
    
    public static async IAsyncEnumerable<String> GetObjectLineByLine(this S3Path path)
    {
        var request = new GetObjectRequest
        {
            BucketName = path.Bucket,
            Key        = path.Path
        };

        using var response = await path.Client.GetObjectAsync(request);
        await using var responseStream = response.ResponseStream;
        using var reader = new StreamReader(responseStream);

        while (true)
        {
            var line = await reader.ReadLineAsync();
            
            if (line is not null)
                yield return line;
            else
                yield break;
        }
    }
}