using System.Collections;
using System.IO.Abstractions.TestingHelpers;
using Kavita.API.Database;
using Kavita.API.Repositories;
using Kavita.API.Services;
using Kavita.Database.Tests;
using Kavita.Models.Builders;
using Kavita.Models.DTOs.Reader;
using Kavita.Models.Entities.Enums;
using Kavita.Models.Entities.User;
using Kavita.Services.Builders;
using Kavita.Services.Scanner;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit.Abstractions;

namespace Kavita.Services.Tests;

public class BookmarkServiceTests(ITestOutputHelper outputHelper): AbstractDbTest(outputHelper)
{
    private BookmarkService Create(IDirectoryService ds, IUnitOfWork unitOfWork)
    {
        return new BookmarkService(Substitute.For<ILogger<BookmarkService>>(), unitOfWork, ds,
Substitute.For<IMediaConversionService>());
    }

    #region BookmarkPage

    [Fact]
    public async Task BookmarkPage_ShouldCopyTheFileAndUpdateDB()
    {
        var (unitOfWork, context, _) = await CreateDatabase();

        var filesystem = CreateFileSystem();
        var file = $"{CacheDirectory}1/0001.jpg";
        filesystem.AddFile(file, new MockFileData("123"));

        var series = new SeriesBuilder("Test")
            .WithFormat(MangaFormat.Epub)
            .WithVolume(new VolumeBuilder(Parser.LooseLeafVolume)
                .WithChapter(new ChapterBuilder("1")
                    .Build())
                .Build())
            .Build();
        series.Library = new LibraryBuilder("Test LIb").Build();
        context.Series.Add(series);


        context.AppUser.Add(new AppUser()
        {
            UserName = "Joe"
        });

        await context.SaveChangesAsync();


        var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
        var bookmarkService = Create(ds, unitOfWork);
        var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);

        var result = await bookmarkService.BookmarkPage(user, new BookmarkDto()
        {
            ChapterId = 1,
            Page = 1,
            SeriesId = 1,
            VolumeId = 1
        }, file);


        Assert.True(result);
        Assert.Single((IEnumerable)ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories));
        Assert.NotNull(await unitOfWork.UserRepository.GetBookmarkAsync(1));
    }

    [Fact]
    public async Task BookmarkPage_ShouldDeleteFileOnUnbookmark()
    {
        var (unitOfWork, context, _) = await CreateDatabase();

        var filesystem = CreateFileSystem();
        filesystem.AddFile($"{CacheDirectory}1/0001.jpg", new MockFileData("123"));
        filesystem.AddFile($"{BookmarkDirectory}1/1/0001.jpg", new MockFileData("123"));

        var series = new SeriesBuilder("Test")
            .WithFormat(MangaFormat.Epub)
            .WithVolume(new VolumeBuilder("1")
                .WithMinNumber(1)
                .WithChapter(new ChapterBuilder(Parser.DefaultChapter)
                    .Build())
                .Build())
            .Build();
        series.Library = new LibraryBuilder("Test LIb").Build();

        context.Series.Add(series);
        context.AppUser.Add(new AppUser()
        {
            UserName = "Joe"
        });

        await context.SaveChangesAsync();

        // Now add the bookmark after we have valid IDs
        var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);
        user.Bookmarks.Add(new AppUserBookmark()
        {
            Page = 1,
            ChapterId = 1,
            FileName = $"1/1/0001.jpg",
            SeriesId = 1,
            VolumeId = 1,
            AppUserId = 1
        });

        await context.SaveChangesAsync();

        var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
        var bookmarkService = Create(ds, unitOfWork);

        // Reload user to get the bookmark
        user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);

        var result = await bookmarkService.RemoveBookmarkPage(user, new BookmarkDto()
        {
            ChapterId = 1,
            Page = 1,
            SeriesId = 1,
            VolumeId = 1
        });

        Assert.True(result);
        Assert.Empty(ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories));
        Assert.Null(await unitOfWork.UserRepository.GetBookmarkAsync(1));
    }

    #endregion

    #region DeleteBookmarkFiles

        [Fact]
        public async Task DeleteBookmarkFiles_ShouldDeleteOnlyPassedFiles()
    {
        var (unitOfWork, context, _) = await CreateDatabase();

        var filesystem = CreateFileSystem();
        filesystem.AddFile($"{CacheDirectory}1/0001.jpg", new MockFileData("123"));
        filesystem.AddFile($"{BookmarkDirectory}1/1/1/0001.jpg", new MockFileData("123"));
        filesystem.AddFile($"{BookmarkDirectory}1/2/1/0002.jpg", new MockFileData("123"));
        filesystem.AddFile($"{BookmarkDirectory}1/2/1/0001.jpg", new MockFileData("123"));

        var series = new SeriesBuilder("Test")
            .WithFormat(MangaFormat.Epub)
            .WithVolume(new VolumeBuilder("1")
                .WithMinNumber(1)
                .WithChapter(new ChapterBuilder("1")
                    .Build())
                .Build())
            .Build();
        series.Library = new LibraryBuilder("Test LIb").Build();

        context.Series.Add(series);
        context.AppUser.Add(new AppUser()
        {
            UserName = "Joe"
        });

        await context.SaveChangesAsync();

        // Add bookmarks after entities are saved
        var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);
        user.Bookmarks.Add(new AppUserBookmark()
        {
            Page = 1,
            ChapterId = 1,
            FileName = $"1/1/1/0001.jpg",
            SeriesId = 1,
            VolumeId = 1,
            AppUserId = 1
        });
        user.Bookmarks.Add(new AppUserBookmark()
        {
            Page = 2,
            ChapterId = 1,
            FileName = $"1/2/1/0002.jpg",
            SeriesId = 1,
            VolumeId = 1,
            AppUserId = 1
        });
        user.Bookmarks.Add(new AppUserBookmark()
        {
            Page = 3,
            ChapterId = 1,
            FileName = $"1/2/1/0001.jpg",
            SeriesId = 1,
            VolumeId = 1,
            AppUserId = 1
        });

        await context.SaveChangesAsync();

        var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
        var bookmarkService = Create(ds, unitOfWork);

        await bookmarkService.DeleteBookmarkFiles([
            new AppUserBookmark
            {
                Page = 1,
                ChapterId = 1,
                FileName = $"1/1/1/0001.jpg",
                SeriesId = 1,
                VolumeId = 1
            }
        ]);

        Assert.Equal(2, ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories).Count());
        Assert.False(ds.FileSystem.FileInfo.New(Path.Join(BookmarkDirectory, "1/1/1/0001.jpg")).Exists);
    }
    #endregion

    #region GetBookmarkFilesById

    [Fact]
    public async Task GetBookmarkFilesById_ShouldMatchActualFiles()
    {
        var filesystem = CreateFileSystem();
        filesystem.AddFile($"{CacheDirectory}1/0001.jpg", new MockFileData("123"));

        var (unitOfWork, context, _) = await CreateDatabase();

        var series = new SeriesBuilder("Test")
            .WithFormat(MangaFormat.Epub)
            .WithVolume(new VolumeBuilder("1")
                .WithMinNumber(1)
                .WithChapter(new ChapterBuilder("1")
                    .Build())
                .Build())
            .Build();
        series.Library = new LibraryBuilder("Test LIb").Build();

        context.Series.Add(series);


        context.AppUser.Add(new AppUser()
        {
            UserName = "Joe"
        });

        await context.SaveChangesAsync();


        var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
        var bookmarkService = Create(ds, unitOfWork);
        var user = await unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Bookmarks);

        await bookmarkService.BookmarkPage(user, new BookmarkDto()
        {
            ChapterId = 1,
            Page = 1,
            SeriesId = 1,
            VolumeId = 1
        }, $"{CacheDirectory}1/0001.jpg");

        var files = await bookmarkService.GetBookmarkFilesById(new[] {1});
        var actualFiles = ds.GetFiles(BookmarkDirectory, searchOption: SearchOption.AllDirectories);
        Assert.Equal(files.Select(Parser.NormalizePath).ToList(), actualFiles.Select(Parser.NormalizePath).ToList());
    }


    #endregion

}
