using OF_DL.Models.Config;
using OF_DL.Models.Downloads;
using OF_DL.Services;

namespace OF_DL.Tests.Services;

public class DownloadServiceTests
{
    [Fact]
    public async Task ProcessMediaDownload_RenamesServerFileAndUpdatesDb_WhenNotDownloadedButServerFileExists()
    {
        using TempFolder temp = new();
        string folder = NormalizeFolder(Path.Combine(temp.Path, "creator"));
        string path = "/Posts/Free";
        string url = "https://example.com/image.jpg";
        string serverFilename = "server";
        string resolvedFilename = "custom";
        string serverFilePath = $"{folder}{path}/{serverFilename}.jpg";
        Directory.CreateDirectory(Path.GetDirectoryName(serverFilePath) ?? throw new InvalidOperationException());
        await File.WriteAllTextAsync(serverFilePath, "abc");

        MediaTrackingDbService dbService = new() { CheckDownloadedResult = false };
        FakeConfigService configService = new(new Config { ShowScrapeSize = false });
        DownloadService service = CreateService(configService, dbService);
        ProgressRecorder progress = new();

        bool isNew = await service.ProcessMediaDownload(folder, 1, "Posts", url, path, serverFilename,
            resolvedFilename, ".jpg", progress);

        string renamedPath = $"{folder}{path}/{resolvedFilename}.jpg";
        Assert.False(isNew);
        Assert.False(File.Exists(serverFilePath));
        Assert.True(File.Exists(renamedPath));
        Assert.NotNull(dbService.LastUpdateMedia);
        Assert.Equal($"{folder}{path}", dbService.LastUpdateMedia.Value.directory);
        Assert.Equal("custom.jpg", dbService.LastUpdateMedia.Value.filename);
        Assert.Equal(new FileInfo(renamedPath).Length, dbService.LastUpdateMedia.Value.size);
        Assert.Equal(1, progress.Total);
    }

    [Fact]
    public async Task ProcessMediaDownload_RenamesExistingFile_WhenDownloadedAndCustomFormatEnabled()
    {
        using TempFolder temp = new();
        string folder = NormalizeFolder(Path.Combine(temp.Path, "creator"));
        string path = "/Posts/Free";
        string url = "https://example.com/image.jpg";
        string serverFilename = "server";
        string resolvedFilename = "custom";
        string serverFilePath = $"{folder}{path}/{serverFilename}.jpg";
        Directory.CreateDirectory(Path.GetDirectoryName(serverFilePath) ?? throw new InvalidOperationException());
        await File.WriteAllTextAsync(serverFilePath, "abc");

        MediaTrackingDbService dbService = new() { CheckDownloadedResult = true, StoredFileSize = 123 };
        FakeConfigService configService =
            new(new Config { ShowScrapeSize = false, RenameExistingFilesWhenCustomFormatIsSelected = true });
        DownloadService service = CreateService(configService, dbService);
        ProgressRecorder progress = new();

        bool isNew = await service.ProcessMediaDownload(folder, 1, "Posts", url, path, serverFilename,
            resolvedFilename, ".jpg", progress);

        string renamedPath = $"{folder}{path}/{resolvedFilename}.jpg";
        Assert.False(isNew);
        Assert.False(File.Exists(serverFilePath));
        Assert.True(File.Exists(renamedPath));
        Assert.NotNull(dbService.LastUpdateMedia);
        Assert.Equal("custom.jpg", dbService.LastUpdateMedia.Value.filename);
        Assert.Equal(123, dbService.LastUpdateMedia.Value.size);
        Assert.Equal(1, progress.Total);
    }

    [Fact]
    public async Task GetDecryptionInfo_UsesOfdlWhenCdmMissing()
    {
        StaticApiService apiService = new();
        DownloadService service =
            CreateService(new FakeConfigService(new Config()), new MediaTrackingDbService(), apiService);

        (string decryptionKey, DateTime lastModified)? result = await service.GetDecryptionInfo(
            "https://example.com/file.mpd", "policy", "signature", "kvp", "1", "2", "post",
            true, false);

        Assert.NotNull(result);
        Assert.Equal("ofdl-key", result.Value.decryptionKey);
        Assert.Equal(apiService.LastModifiedToReturn, result.Value.lastModified);
        Assert.True(apiService.OfdlCalled);
        Assert.False(apiService.CdmCalled);
    }

    [Fact]
    public async Task GetDecryptionInfo_UsesCdmWhenAvailable()
    {
        StaticApiService apiService = new();
        DownloadService service =
            CreateService(new FakeConfigService(new Config()), new MediaTrackingDbService(), apiService);

        (string decryptionKey, DateTime lastModified)? result = await service.GetDecryptionInfo(
            "https://example.com/file.mpd", "policy", "signature", "kvp", "1", "2", "post",
            false, false);

        Assert.NotNull(result);
        Assert.Equal("cdm-key", result.Value.decryptionKey);
        Assert.Equal(apiService.LastModifiedToReturn, result.Value.lastModified);
        Assert.True(apiService.CdmCalled);
        Assert.False(apiService.OfdlCalled);
    }

    [Fact]
    public async Task DownloadHighlights_ReturnsZeroWhenNoMedia()
    {
        StaticApiService apiService = new() { MediaToReturn = new Dictionary<long, string>() };
        DownloadService service =
            CreateService(new FakeConfigService(new Config()), new MediaTrackingDbService(), apiService);

        DownloadResult result = await service.DownloadHighlights("user", 1, "/tmp/creator", new HashSet<long>(),
            new ProgressRecorder());

        Assert.Equal(0, result.TotalCount);
        Assert.Equal(0, result.NewDownloads);
        Assert.Equal(0, result.ExistingDownloads);
        Assert.Equal("Highlights", result.MediaType);
        Assert.True(result.Success);
    }

    [Fact]
    public async Task DownloadHighlights_CountsExistingWhenAlreadyDownloaded()
    {
        using TempFolder temp = new();
        string folder = NormalizeFolder(Path.Combine(temp.Path, "creator"));
        StaticApiService apiService = new()
        {
            MediaToReturn = new Dictionary<long, string>
            {
                { 1, "https://example.com/one.jpg" }, { 2, "https://example.com/two.jpg" }
            }
        };
        MediaTrackingDbService dbService = new() { CheckDownloadedResult = true };
        FakeConfigService configService = new(new Config { ShowScrapeSize = false });
        ProgressRecorder progress = new();
        DownloadService service = CreateService(configService, dbService, apiService);

        DownloadResult result = await service.DownloadHighlights("user", 1, folder, new HashSet<long>(), progress);

        Assert.Equal(2, result.TotalCount);
        Assert.Equal(0, result.NewDownloads);
        Assert.Equal(2, result.ExistingDownloads);
        Assert.Equal("Highlights", result.MediaType);
        Assert.True(result.Success);
        Assert.Equal(2, progress.Total);
    }

    private static DownloadService CreateService(FakeConfigService configService, MediaTrackingDbService dbService,
        StaticApiService? apiService = null) =>
        new(new FakeAuthService(), configService, dbService, new FakeFileNameService(),
            apiService ?? new StaticApiService());

    private static string NormalizeFolder(string folder) => folder.Replace("\\", "/");
}

