using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using PowderCoating.Application.Configuration;
using PowderCoating.Application.Interfaces;

namespace PowderCoating.Application.Services;

/// <summary>
/// Manages company logo files stored in Azure Blob Storage.
/// Logos are stored in the <c>companylogos</c> container using the path
/// <c>{companyId}/company-logo{ext}</c> (e.g. <c>7/company-logo.png</c>).
/// The service enforces a 10 MB size limit and restricts uploads to common
/// web-safe image formats.  It also handles the "replace" case by deleting
/// blobs of every other allowed extension before writing the new one, so a
/// company can never end up with multiple active logos at different paths.
/// </summary>
public class CompanyLogoService : ICompanyLogoService
{
    private readonly IAzureBlobStorageService _blobService;
    private readonly StorageSettings _settings;

    /// <summary>Maximum logo file size accepted on upload (10 MB).</summary>
    private const long MaxFileSize = 10 * 1024 * 1024; // 10 MB

    /// <summary>
    /// Exhaustive list of image extensions permitted for company logos.
    /// SVG is included because some companies upload vector-format brand assets.
    /// </summary>
    private static readonly string[] AllowedExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"];

    /// <summary>
    /// Initialises the service with the blob storage provider and storage
    /// configuration (container names, etc.).
    /// </summary>
    public CompanyLogoService(
        IAzureBlobStorageService blobService,
        IOptions<StorageSettings> settings)
    {
        _blobService = blobService;
        _settings = settings.Value;
    }

    /// <summary>
    /// Returns the deterministic blob path for a company logo given its file
    /// extension.  Using a fixed name (rather than a GUID) allows the path to
    /// be derived from <c>companyId</c> alone — useful for PDF embedding and
    /// sidebar rendering without an extra DB lookup.
    /// </summary>
    /// <param name="companyId">The tenant company's database ID.</param>
    /// <param name="extension">File extension including the leading dot (e.g. <c>.png</c>).</param>
    /// <returns>Blob-relative path such as <c>7/company-logo.png</c>.</returns>
    public string GetCompanyLogoPath(int companyId, string extension)
    {
        return $"{companyId}/company-logo{extension}";
    }

    /// <summary>
    /// Validates and uploads a new company logo, replacing any existing logo
    /// regardless of its extension.  Old blobs at alternative extensions are
    /// deleted first so the container never holds stale copies.
    /// </summary>
    /// <param name="file">The uploaded file from the HTTP request.</param>
    /// <param name="companyId">The tenant company's database ID.</param>
    /// <returns>
    /// A tuple indicating success, the stored blob path (on success), and a
    /// human-readable error message (on failure).
    /// </returns>
    public async Task<(bool Success, string FilePath, string ErrorMessage)> SaveCompanyLogoAsync(IFormFile file, int companyId)
    {
        var (isValid, extension, error) = BlobFileHelper.ValidateUpload(file, AllowedExtensions, MaxFileSize);
        if (!isValid)
            return (false, string.Empty, error);

        // Delete old logo (any extension) before saving new one
        await DeleteOldLogosAsync(companyId, extension);

        var blobName = GetCompanyLogoPath(companyId, extension);
        var contentType = BlobFileHelper.GetContentType(extension);

        using var stream = file.OpenReadStream();
        var result = await _blobService.UploadAsync(_settings.Containers.CompanyLogos, blobName, stream, contentType);

        if (!result.Success)
            return (false, string.Empty, result.ErrorMessage);

        return (true, blobName, string.Empty);
    }

    /// <summary>
    /// Deletes the company logo blob at the given path from Azure Blob Storage.
    /// </summary>
    /// <param name="filePath">Blob-relative path previously returned by <see cref="SaveCompanyLogoAsync"/>.</param>
    /// <returns>Success flag and an error message on failure.</returns>
    public async Task<(bool Success, string ErrorMessage)> DeleteCompanyLogoAsync(string filePath)
    {
        if (string.IsNullOrEmpty(filePath))
            return (false, "File path is empty");

        return await _blobService.DeleteAsync(_settings.Containers.CompanyLogos, filePath);
    }

    /// <summary>
    /// Downloads the raw bytes of the company logo for embedding in PDFs or
    /// serving through the <c>/CompanyLogo</c> controller endpoint.
    /// </summary>
    /// <param name="filePath">Blob-relative path of the logo.</param>
    /// <returns>
    /// A tuple with a success flag, the raw file bytes, the MIME content type,
    /// and an error message on failure.
    /// </returns>
    public async Task<(bool Success, byte[] FileContent, string ContentType, string ErrorMessage)> GetCompanyLogoAsync(string filePath)
    {
        if (string.IsNullOrEmpty(filePath))
            return (false, Array.Empty<byte>(), string.Empty, "File path is empty");

        return await _blobService.DownloadAsync(_settings.Containers.CompanyLogos, filePath);
    }

    /// <summary>
    /// Checks whether a logo blob exists at the given path without downloading it.
    /// Used by the sidebar to decide whether to show the tenant logo or fall back
    /// to the default PCL logo.
    /// </summary>
    /// <param name="filePath">Blob-relative path to check.</param>
    /// <returns><c>true</c> if the blob exists; otherwise <c>false</c>.</returns>
    public async Task<bool> CompanyLogoExistsAsync(string filePath)
    {
        if (string.IsNullOrEmpty(filePath))
            return false;

        return await _blobService.ExistsAsync(_settings.Containers.CompanyLogos, filePath);
    }

    /// <summary>
    /// Deletes any pre-existing logo blobs at extensions other than
    /// <paramref name="currentExtension"/> so that uploading a PNG logo
    /// automatically removes a previously stored JPG logo (and vice versa).
    /// Errors are intentionally swallowed — a missing old blob is not a failure.
    /// </summary>
    /// <param name="companyId">The tenant company's database ID.</param>
    /// <param name="currentExtension">
    /// The extension of the incoming upload; blobs with this extension are
    /// skipped because they will be overwritten by the caller.
    /// </param>
    private async Task DeleteOldLogosAsync(int companyId, string currentExtension)
    {
        foreach (var ext in AllowedExtensions)
        {
            if (ext == currentExtension) continue;
            var oldBlobName = GetCompanyLogoPath(companyId, ext);
            await _blobService.DeleteAsync(_settings.Containers.CompanyLogos, oldBlobName);
        }
    }

}
