using Amazon;
using Amazon.Runtime;
using Amazon.S3;
using DodoSSH.Client.Domain;
using DodoSSH.Client.Ssh;

namespace DodoSSH.Client.ObjectStore;

/// <summary>Opens a bucket as a place with files in it.</summary>
/// <remarks>
/// An interface so the file screen can be tested without a bucket, exactly as <c>ISftpSessionFactory</c> is
/// what lets it be tested without a host.
/// </remarks>
public interface IObjectStoreFactory
{
    /// <summary>Builds a client for one bucket.</summary>
    /// <param name="store">The bucket and its credentials, decrypted.</param>
    /// <remarks>
    /// Synchronous and cheap: nothing is contacted here. S3 is request-per-operation, so there is no
    /// connect step to fail — the first thing that can fail is the first listing, which is where the
    /// credentials and the endpoint are actually tested.
    /// </remarks>
    IRemoteFileStore Open(ObjectStoreSecret store);
}

/// <summary>Opens buckets with the AWS SDK.</summary>
public sealed class S3ObjectStoreFactory : IObjectStoreFactory
{
    /// <inheritdoc />
    public IRemoteFileStore Open(ObjectStoreSecret store)
    {
        ArgumentNullException.ThrowIfNull(store);

        if (!store.TryValidate(out var reason))
        {
            throw new ArgumentException(reason, nameof(store));
        }

        var config = new AmazonS3Config
        {
            // On for nearly every self-hosted service and off for AWS. It is a stored setting rather than
            // something inferred from the endpoint, because inferring it wrongly produces a DNS failure that
            // says nothing about buckets — see ObjectStoreSecret.UsePathStyle.
            ForcePathStyle = store.UsePathStyle,
        };

        if (store.Endpoint is { } endpoint)
        {
            config.ServiceURL = endpoint;

            // Still set when there is one, because SigV4 signs the region into every request and several
            // S3-compatible services check it. The ones that do not, ignore it.
            if (store.Region is { } named)
            {
                config.AuthenticationRegion = named;
            }
        }
        else
        {
            // No endpoint means Amazon, and then the region is what resolves the host. Validation has
            // already refused the case where neither is set.
            config.RegionEndpoint = RegionEndpoint.GetBySystemName(store.Region!);
        }

        var credentials = new BasicAWSCredentials(store.AccessKeyId, store.SecretAccessKey);

        return new S3FileStore(new AmazonS3Client(credentials, config), store.Bucket);
    }
}
