using Ikar.Server.Data.Entities;

namespace Ikar.Server.Infrastructure.Storage;

public sealed class AttachmentStorageService(IWebHostEnvironment environment)
{
    private readonly string _rootPath = Path.Combine(environment.ContentRootPath, "Data", "Attachments");

    public async Task<MessageAttachment> SaveAsync(
        Guid messageId,
        IFormFile file,
        CancellationToken cancellationToken,
        int sortOrder = 0)
    {
        if (file.Length <= 0)
        {
            throw new InvalidOperationException("Attachment is empty.");
        }

        var (attachmentId, physicalPath) = CreateTargetPath(file.FileName);

        await using (var source = file.OpenReadStream())
        await using (var target = File.Create(physicalPath))
        {
            await source.CopyToAsync(target, cancellationToken);
        }

        return new MessageAttachment
        {
            Id = attachmentId,
            MessageId = messageId,
            OriginalFileName = string.IsNullOrWhiteSpace(file.FileName) ? "attachment" : Path.GetFileName(file.FileName),
            ContentType = string.IsNullOrWhiteSpace(file.ContentType) ? "application/octet-stream" : file.ContentType,
            FileSizeBytes = file.Length,
            StoragePath = physicalPath,
            SortOrder = sortOrder,
            UploadedAt = DateTimeOffset.UtcNow
        };
    }

    public async Task<StoryAttachment> SaveStoryAsync(
        Guid storyId,
        IFormFile file,
        CancellationToken cancellationToken,
        int sortOrder = 0)
    {
        if (file.Length <= 0)
        {
            throw new InvalidOperationException("Attachment is empty.");
        }

        var (attachmentId, physicalPath) = CreateTargetPath(file.FileName);

        await using (var source = file.OpenReadStream())
        await using (var target = File.Create(physicalPath))
        {
            await source.CopyToAsync(target, cancellationToken);
        }

        return new StoryAttachment
        {
            Id = attachmentId,
            StoryId = storyId,
            OriginalFileName = string.IsNullOrWhiteSpace(file.FileName) ? "story-attachment" : Path.GetFileName(file.FileName),
            ContentType = string.IsNullOrWhiteSpace(file.ContentType) ? "application/octet-stream" : file.ContentType,
            FileSizeBytes = file.Length,
            StoragePath = physicalPath,
            SortOrder = sortOrder,
            UploadedAt = DateTimeOffset.UtcNow
        };
    }

    public async Task<MessageAttachment> CloneAsync(
        Guid messageId,
        MessageAttachment sourceAttachment,
        CancellationToken cancellationToken,
        int? sortOrder = null)
    {
        if (string.IsNullOrWhiteSpace(sourceAttachment.StoragePath) || !File.Exists(sourceAttachment.StoragePath))
        {
            throw new FileNotFoundException("Source attachment file was not found.", sourceAttachment.StoragePath);
        }

        var (attachmentId, physicalPath) = CreateTargetPath(sourceAttachment.OriginalFileName);

        await using (var source = File.OpenRead(sourceAttachment.StoragePath))
        await using (var target = File.Create(physicalPath))
        {
            await source.CopyToAsync(target, cancellationToken);
        }

        return new MessageAttachment
        {
            Id = attachmentId,
            MessageId = messageId,
            OriginalFileName = sourceAttachment.OriginalFileName,
            ContentType = sourceAttachment.ContentType,
            FileSizeBytes = sourceAttachment.FileSizeBytes,
            StoragePath = physicalPath,
            SortOrder = sortOrder ?? sourceAttachment.SortOrder,
            UploadedAt = DateTimeOffset.UtcNow
        };
    }

    public void DeleteIfExists(string? physicalPath)
    {
        if (!string.IsNullOrWhiteSpace(physicalPath) && File.Exists(physicalPath))
        {
            File.Delete(physicalPath);
        }
    }

    private (Guid AttachmentId, string PhysicalPath) CreateTargetPath(string? originalFileName)
    {
        Directory.CreateDirectory(_rootPath);

        var attachmentId = Guid.NewGuid();
        var extension = Path.GetExtension(originalFileName);
        var year = DateTime.UtcNow.ToString("yyyy");
        var month = DateTime.UtcNow.ToString("MM");
        var targetDirectory = Path.Combine(_rootPath, year, month);
        Directory.CreateDirectory(targetDirectory);

        var storedFileName = $"{attachmentId:N}{extension}";
        var physicalPath = Path.Combine(targetDirectory, storedFileName);
        return (attachmentId, physicalPath);
    }
}
