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

namespace PowderCoating.Application.Services;

/// <summary>
/// Manages equipment manual documents stored in Azure Blob Storage.
/// Manuals are stored in the <c>manuals</c> container under the path
/// <c>{companyId}/equipment-manuals/{equipmentId}/{sanitizedFilename}{ext}</c>.
/// <para>
/// The 50 MB limit (5× larger than other upload types) is intentional —
/// equipment OEM manuals are often large PDF scans.  Only document formats
/// are accepted; image uploads are rejected to keep this container clean.
/// </para>
/// </summary>
public class EquipmentManualService : IEquipmentManualService
{
    private readonly IAzureBlobStorageService _blobService;
    private readonly StorageSettings _settings;

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

    /// <summary>
    /// Document formats permitted for equipment manuals.
    /// Images and spreadsheets are deliberately excluded to keep
    /// the container purpose-specific.
    /// </summary>
    private static readonly string[] AllowedExtensions = [".pdf", ".doc", ".docx", ".txt"];

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

    /// <summary>
    /// Validates, sanitizes, and uploads an equipment manual to Azure Blob Storage.
    /// The original filename (minus invalid characters) is preserved in the blob
    /// name so operators can recognise the document from the path alone.
    /// </summary>
    /// <param name="file">The uploaded file from the HTTP request.</param>
    /// <param name="companyId">The tenant company's database ID (for path scoping).</param>
    /// <param name="equipmentId">The equipment record's database ID.</param>
    /// <returns>
    /// A tuple with a success flag, the stored blob path (on success), and a
    /// human-readable error message (on failure).
    /// </returns>
    public async Task<(bool Success, string FilePath, string ErrorMessage)> SaveEquipmentManualAsync(IFormFile file, int companyId, int equipmentId)
    {
        var (isValid, extension, error) = BlobFileHelper.ValidateUpload(file, AllowedExtensions, MaxFileSize);
        if (!isValid)
            return (false, string.Empty, error);

        // Sanitize filename — replace OS-invalid characters with underscores to
        // prevent path traversal and blob naming errors in Azure.
        var fileName = BlobFileHelper.SanitizeFileName(Path.GetFileNameWithoutExtension(file.FileName));

        var blobName = $"{companyId}/equipment-manuals/{equipmentId}/{fileName}{extension}";
        var contentType = BlobFileHelper.GetContentType(extension);

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

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

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

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

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

    /// <summary>
    /// Downloads the raw bytes of an equipment manual so the controller can
    /// stream it to the browser with the appropriate content-disposition header.
    /// </summary>
    /// <param name="filePath">Blob-relative path of the manual.</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)> GetEquipmentManualAsync(string filePath)
    {
        if (string.IsNullOrEmpty(filePath))
            return (false, Array.Empty<byte>(), string.Empty, "File path is empty");

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

    /// <summary>
    /// Checks whether a manual blob exists at the given path without downloading it.
    /// Used by the Equipment Details view to determine whether a "Download Manual"
    /// button should be rendered.
    /// </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> EquipmentManualExistsAsync(string filePath)
    {
        if (string.IsNullOrEmpty(filePath))
            return false;

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

}
