using Azure;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Azure.Storage.Blobs.Specialized;
using Azure.Storage.Sas;
using Core.Blueprint.Storage.Adapters;
using Core.Blueprint.Storage.Contracts;
using Microsoft.Extensions.Configuration;
namespace Core.Blueprint.Storage.Provider
{
    public sealed class BlobStorageProvider : IBlobStorageProvider
    {
        private readonly BlobServiceClient _blobServiceClient;
        private readonly BlobContainerClient _blobContainerClient;
        private readonly string _containerName;
        private readonly Trie _trie = new Trie();
        public BlobStorageProvider(BlobServiceClient blobServiceClient, IConfiguration configuration)
        {
            _blobServiceClient = blobServiceClient;
            _containerName = configuration.GetSection("BlobStorage:ContainerName").Value ?? "";
            if (string.IsNullOrEmpty(_containerName))
                throw new ArgumentException("Blob container cannot be null or empty.");
            _blobContainerClient = blobServiceClient.GetBlobContainerClient(_containerName);
        }
        /// 
        /// Creates the blob container if it does not exist.
        /// 
        public async Task> CreateIfNotExistsAsync()
        {
            return await _blobContainerClient.CreateIfNotExistsAsync();
        }
        /// 
        /// Deletes the blob container if it exists.
        /// 
        public async Task DeleteIfExistsAsync()
        {
            await _blobContainerClient.DeleteIfExistsAsync();
        }
        /// 
        /// Gets properties of the blob container.
        /// 
        public async Task> GetPropertiesAsync()
        {
            return await _blobContainerClient.GetPropertiesAsync();
        }
        /// 
        /// Sets metadata for the blob container.
        /// 
        public async Task SetMetadataAsync(IDictionary metadata)
        {
            await _blobContainerClient.SetMetadataAsync(metadata);
        }
        /// 
        /// Uploads a blob to the container.
        /// 
        public async Task> UploadBlobAsync(string blobName, Stream content)
        {
            var blobClient = _blobContainerClient.GetBlobClient(blobName);
            return await blobClient.UploadAsync(content, overwrite: true);
        }
        /// 
        /// Downloads a blob from the container.
        /// 
        public async Task> DownloadBlobAsync(string blobName)
        {
            var blobClient = _blobContainerClient.GetBlobClient(blobName);
            return await blobClient.DownloadAsync();
        }
        /// 
        /// Deletes a blob from the container.
        /// 
        public async Task DeleteBlobAsync(string blobName)
        {
            var blobClient = _blobContainerClient.GetBlobClient(blobName);
            return await blobClient.DeleteIfExistsAsync();
        }
        /// 
        /// Lists all blobs in the container with an optional prefix.
        /// 
        public async Task> ListBlobItemAsync(string? prefix = null)
        {
            var blobs = new List();
            await foreach (var blobItem in _blobContainerClient.GetBlobsAsync(prefix: prefix))
            {
                blobs.Add(blobItem);
            }
            return blobs;
        }
        /// 
        /// Retrieves the account information for the associated Blob Service Client.
        /// 
        /// 
        /// A  that can be used to cancel the operation.
        /// 
        /// 
        /// A task that represents the asynchronous operation. The task result contains the
        ///  object, which provides details about the account, such as the SKU
        /// and account kind.
        public async Task GetAccountInfoAsync(CancellationToken cancellation)
        {
            return await _blobServiceClient.GetAccountInfoAsync(cancellation);
        }
        /// 
        /// Gets a blob client for a specific blob.
        /// 
        public BlobClient GetBlobClient(string blobName)
        {
            return _blobContainerClient.GetBlobClient(blobName);
        }
        /// 
        /// Lists blobs hierarchically using a delimiter.
        /// 
        public async Task> ListBlobsByHierarchyAsync(string? prefix = null, string delimiter = "/")
        {
            var blobs = new List();
            await foreach (var blobHierarchyItem in _blobContainerClient.GetBlobsByHierarchyAsync(prefix: prefix, delimiter: delimiter))
            {
                blobs.Add(blobHierarchyItem);
            }
            return blobs;
        }
        /// 
        /// Generates a SAS token for the container with specified permissions.
        /// 
        public Uri GenerateContainerSasUri(BlobContainerSasPermissions permissions, DateTimeOffset expiresOn)
        {
            if (!_blobContainerClient.CanGenerateSasUri)
            {
                throw new InvalidOperationException("Cannot generate SAS URI. Ensure the client is authorized with account key credentials.");
            }
            var sasBuilder = new BlobSasBuilder
            {
                BlobContainerName = _blobContainerClient.Name,
                Resource = "c", // c for container
                ExpiresOn = expiresOn
            };
            sasBuilder.SetPermissions(permissions);
            return _blobContainerClient.GenerateSasUri(sasBuilder);
        }
        /// 
        /// Acquires a lease on the blob container.
        /// 
        public async Task> AcquireLeaseAsync(string? proposedId = null, TimeSpan? duration = null)
        {
            return await _blobContainerClient.GetBlobLeaseClient(proposedId).AcquireAsync(duration ?? TimeSpan.FromSeconds(60));
        }
        /// 
        /// Releases a lease on the blob container.
        /// 
        public async Task ReleaseLeaseAsync(string leaseId)
        {
            await _blobContainerClient.GetBlobLeaseClient(leaseId).ReleaseAsync();
        }
        /// 
        /// Sets access policies for the blob container.
        /// 
        public async Task SetAccessPolicyAsync(PublicAccessType accessType, IEnumerable? identifiers = null)
        {
            await _blobContainerClient.SetAccessPolicyAsync(accessType, identifiers);
        }
        /// 
        /// Lists blobs in the container with an optional prefix.
        /// 
        public async Task> ListBlobsAsync(string? prefix = null)
        {
            var blobs = new List();
            await foreach (BlobItem blob in _blobContainerClient.GetBlobsAsync(prefix: prefix))
            {
                blobs.Add(new BlobFileAdapter
                {
                    Name = blob.Name,
                    Uri = $"{_blobContainerClient.Uri}/{blob.Name}",
                    ContentType = blob.Properties.ContentType,
                    Size = blob.Properties.ContentLength,
                    DateUpload = blob.Properties.LastModified?.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss"),
                    ShortDate = blob.Properties.LastModified?.UtcDateTime.ToString("MM-dd-yyyy"),
                    Status = "Available"
                });
            }
            return blobs;
        }
        /// 
        /// Uploads a blob to the container.
        /// 
        public async Task UploadBlobAsync(BlobAddDto newBlob)
        {
            var blobClient = _blobContainerClient.GetBlobClient(newBlob.FileName);
            using var stream = new MemoryStream(newBlob.FileContent);
            await blobClient.UploadAsync(stream, overwrite: true);
            var properties = await blobClient.GetPropertiesAsync();
            return new BlobFileAdapter
            {
                Name = newBlob.FileName ?? "",
                Uri = blobClient.Uri.ToString(),
                ContentType = properties.Value.ContentType,
                Size = properties.Value.ContentLength,
                DateUpload = properties.Value.LastModified.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss"),
                ShortDate = properties.Value.LastModified.UtcDateTime.ToString("MM-dd-yyyy"),
                Status = "Uploaded"
            };
        }
        /// 
        /// Deletes a blob from the container.
        /// 
        public async Task DeleteBlobsAsync(string fileName)
        {
            var blobClient = _blobContainerClient.GetBlobClient(fileName);
            if (await blobClient.ExistsAsync())
            {
                var properties = await blobClient.GetPropertiesAsync();
                var _response = await blobClient.DeleteIfExistsAsync();
                return new BlobFileAdapter
                {
                    Name = fileName,
                    Uri = blobClient.Uri.ToString(),
                    ContentType = properties.Value.ContentType,
                    Size = properties.Value.ContentLength,
                    DateUpload = properties.Value.LastModified.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss"),
                    ShortDate = properties.Value.LastModified.UtcDateTime.ToString("MM-dd-yyyy"),
                    Status = _response ? "Deleted" : "Failed to delete"
                };
            }
            return null;
        }
        /// 
        /// Downloads a blob's content.
        /// 
        public async Task DownloadBlobsAsync(string blobName)
        {
            var blobClient = _blobContainerClient.GetBlobClient(blobName);
            if (!await blobClient.ExistsAsync())
            {
                throw new FileNotFoundException($"Blob '{blobName}' does not exist in the container '{_containerName}'.");
            }
            return await blobClient.DownloadAsync();
        }
        /// 
        /// Generates a secure download URI for a specified blob in the storage container.
        /// 
        /// The name of the blob for which the download URI is being generated.
        /// 
        /// An instance of  containing the generated URI, blob name, and status.
        /// 
        /// 
        /// The generated URI includes a Shared Access Signature (SAS) token, which allows secure, time-limited access to the blob.
        /// The SAS token grants read-only access to the blob for a duration of 5 minutes starting from the current time.
        /// 
        /// Thrown if  is null or empty.
        /// Thrown if there is an issue communicating with the Azure Blob service.
        public BlobDownloadUriAdapter GenerateBlobDownloadUri(string blobName)
        {
            var delegationKey = _blobServiceClient.GetUserDelegationKey(DateTimeOffset.UtcNow,
                                                                    DateTimeOffset.UtcNow.AddHours(2));
            var blob = _blobContainerClient.GetBlobClient(blobName);
            var sasBuilder = new BlobSasBuilder()
            {
                BlobContainerName = blob.BlobContainerName,
                BlobName = blob.Name,
                Resource = "b",
                StartsOn = DateTimeOffset.UtcNow,
                ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(5),
            };
            sasBuilder.SetPermissions(BlobAccountSasPermissions.Read);
            sasBuilder.Protocol = SasProtocol.Https;
            var blobUriBuilder = new BlobUriBuilder(blob.Uri)
            {
                Sas = sasBuilder.ToSasQueryParameters(delegationKey, _blobServiceClient.AccountName)
            };
            return new BlobDownloadUriAdapter
            {
                Uri = blobUriBuilder.ToUri(),
                Name = blob.Name,
                Status = "Available"
            };
        }
        /// 
        /// Retrieves the hierarchical folder structure.
        /// 
        public async Task> GetFolderHierarchyAsync(string prefix)
        {
            var rootFolder = new BlobStorageFolder { Name = prefix };
            await PopulateFolderAsync(rootFolder, prefix);
            return new List { rootFolder };
        }
        private async Task PopulateFolderAsync(BlobStorageFolder folder, string? prefix)
        {
            await foreach (var blobHierarchy in _blobContainerClient.GetBlobsByHierarchyAsync(prefix: prefix, delimiter: "/"))
            {
                if (blobHierarchy.IsPrefix)
                {
                    var subFolder = new BlobStorageFolder { Name = blobHierarchy.Prefix.TrimEnd('/') };
                    folder.SubFolders.Add(subFolder);
                    await PopulateFolderAsync(subFolder, blobHierarchy.Prefix);
                }
                else
                {
                    folder.Files.Add(new BlobStorageFilesAdapter(Content: blobHierarchy.Prefix,  //Fix
                                                                Name: blobHierarchy.Blob.Name,
                                                                ContentType: "",
                                                                DownloadUrl: $"{_blobContainerClient.Uri}/{blobHierarchy.Blob.Name}"));
                }
            }
        }
        public async Task>> ListNeighborFoldersAsync(string? prefix)
        {
            await ListFoldersInTrieAsync(prefix);
            var groupedFolders = _trie.SearchByPrefix(prefix)
                .OrderBy(folder => folder)
                .GroupBy(folder => folder.Substring(0, 1))
                .ToDictionary(group => group.Key, group => group.ToList());
            return groupedFolders;
        }
        private async Task ListFoldersInTrieAsync(string? prefix)
        {
            await foreach (var blobHierarchy in _blobContainerClient.GetBlobsByHierarchyAsync(prefix: prefix, delimiter: "/"))
            {
                if (blobHierarchy.IsPrefix)
                {
                    var folderName = blobHierarchy.Prefix.TrimEnd('/').Split('/').Last();
                    _trie.Insert(folderName);
                }
            }
        }
    }
}