using TeleWave.Domain.Media;

namespace TeleWave.Application.Common.Interfaces;

/// <summary>
/// Порт файлового хранилища медиа. Все относительные пути резолвятся строго внутри корня
/// (<c>Storage:RootPath</c>) — защита от path traversal лежит на реализации.
/// </summary>
public interface IMediaStorage
{
    /// <summary>Свободное место на томе хранилища, байт.</summary>
    long GetAvailableFreeSpaceBytes();

    /// <summary>
    /// Абсолютный путь к HLS-плейлисту готового ассета (<c>assets/{id}/index.m3u8</c>) — им ffmpeg
    /// читает уже нарезанное медиа как обычный вход. Null, если каталога ассета нет на диске.
    /// </summary>
    string? ResolvePlaylistPath(Guid assetId);

    /// <summary>
    /// Стримит загружаемый контент во временный файл в <c>uploads/</c> без буферизации в память.
    /// Возвращает непрозрачный токен (имя временного файла) для последующего <see cref="PromoteToOriginalAsync"/>.
    /// </summary>
    Task<string> SaveUploadAsync(
        Stream content,
        string extension,
        CancellationToken cancellationToken
    );

    /// <summary>Удаляет временный файл загрузки (откат при ошибке до регистрации ассета).</summary>
    Task DeleteUploadAsync(string uploadToken, CancellationToken cancellationToken);

    /// <summary>Файл ручного inbox.</summary>
    /// <param name="RelativePath">Путь относительно manual/ — может содержать подкаталоги.</param>
    public readonly record struct ManualInboxFile(string RelativePath, string Name, long SizeBytes);

    /// <summary>
    /// Что лежит в <c>manual/</c>, включая подкаталоги. <paramref name="max"/> ограничивает выдачу:
    /// каталог наполняет человек, и он может оказаться большим.
    /// </summary>
    IReadOnlyList<ManualInboxFile> ListManualInbox(int max);

    /// <summary>
    /// Убирает то, что осталось в <c>manual/</c> от забранного файла: спутники с тем же именем
    /// и другим расширением (субтитры, nfo, обложка), файлы того же эпизода в соседних подпапках
    /// (<c>subs_ru</c>, <c>Subtitles</c>) и опустевшие каталоги вверх до корня <c>manual/</c>.
    /// Другие видеофайлы не трогает — рядом может лежать следующая серия или иная версия.
    /// </summary>
    Task CleanupManualLeftoversAsync(string relativePath, CancellationToken cancellationToken);

    /// <summary>
    /// Переносит исходник в <c>originals/{assetId}{ext}</c>. Каталог-источник определяется
    /// <paramref name="source"/>: <c>uploads/</c>, <c>inbox/</c> либо <c>manual/</c>. Именно
    /// переносит — файл из каталога-источника уходит.
    /// </summary>
    Task PromoteToOriginalAsync(
        MediaSource source,
        string sourceToken,
        Guid assetId,
        string extension,
        CancellationToken cancellationToken
    );

    /// <summary>Удаляет все артефакты ассета: исходник в <c>originals/</c> и каталог сегментов <c>assets/{id}/</c>.</summary>
    Task DeleteAssetArtifactsAsync(
        Guid assetId,
        string extension,
        CancellationToken cancellationToken
    );
}
