using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using Moq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace AsbCloudWebApi.Tests.ServicesTests
{
    public class FileServiceTest
    {
        private FileService fileService;

        private static UserDto Author = new UserDto {
            Id = 1,
            IdCompany = 1
        };

        private static List<FileMarkDto> FileMarks = new List<FileMarkDto> {
            new FileMarkDto
            {
                Id = 132,
                IdFile = 1742,
                User = Author,
                Comment = "qqq",
                IdMarkType = 1,
                DateCreated = DateTime.Now,
                IsDeleted = false
            },
            new FileMarkDto
            {
                Id = 133,
                IdFile = 1742,
                User = Author,
                Comment = "qqq3",
                IdMarkType = 1,
                DateCreated = DateTime.Now,
                IsDeleted = false
            }
        };

        private static List<FileInfoDto> Files = new List<FileInfoDto> {
            new FileInfoDto {
                Id = 1742,
                IdAuthor = 1,
                Author = Author,
                IdWell = 90,
                IdCategory = 10040,
                Name = "test.txt",
                Size = 0,
                UploadDate = DateTime.Now,
                FileMarks = FileMarks
            },
            new FileInfoDto
            {
                Id = 1743,
                IdAuthor = 1,
                Author = Author,
                IdWell = 90,
                IdCategory = 10021,
                Name = "test1.txt",
                Size = 0,
                UploadDate = DateTime.Now
            }
        };

        public FileServiceTest()
        {
            var repositoryMock = RepositoryFactory.Make<IFileRepository, FileInfoDto>(Files);

            repositoryMock.Setup(x => x.GetByMarkId(It.IsAny<int>(), It.IsAny<CancellationToken>()))
                .Returns((int idMark, CancellationToken token) => {
                    var data = Files.FirstOrDefault(x => x.FileMarks.Any(m => m.Id == idMark));
                    return Task.FromResult(data);
                });
            repositoryMock.Setup(x => x.GetInfoByIdsAsync(It.IsAny<IEnumerable<int>>(), It.IsAny<CancellationToken>()))
                .Returns((int[] idsFile, CancellationToken token) => {
                    var data = Files.Where(x => idsFile.Contains(x.Id));
                    return Task.FromResult(data);
                });

            repositoryMock.Setup(x => x.DeleteAsync(It.IsAny<IEnumerable<int>>(), It.IsAny<CancellationToken>()))
                .Returns((int[] idsFile, CancellationToken token) => {
                    var dtos = Files.Where(x => idsFile.Contains(x.Id)).ToArray();
                    Files.RemoveAll(x => dtos.Select(d => d.Id).Contains(x.Id));
                    return Task.FromResult(dtos.AsEnumerable());
                });

            repositoryMock.Setup(x => x.MarkFileMarkAsDeletedAsync(It.IsAny<IEnumerable<int>>(), It.IsAny<CancellationToken>()))
                .Returns((int[] idsMarks, CancellationToken token) => {
                    var data = FileMarks.Where(m => idsMarks.Contains(m.Id));

                    foreach (var fileMark in data)
                        fileMark.IsDeleted = true;

                    var result = data.All(x => x.IsDeleted) ? 1 : 0;
                    return Task.FromResult(result);
                });

            repositoryMock.Setup(x => x.MarkAsDeletedAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
                .Returns((int idFile, CancellationToken token) => {
                    var result = Files.Where(x => x.Id == idFile).Any() ? 1 : 0;
                    return Task.FromResult(result);
                });

            repositoryMock.Setup(x => x.GetInfosAsync(It.IsAny<FileRequest>(), It.IsAny<CancellationToken>()))
                .Returns((FileRequest request, CancellationToken token) => {
                    var data = Files.Where(x => x.IdWell == request.IdWell);
                    return Task.FromResult(data);
                });

            repositoryMock.Setup(x => x.CreateFileMarkAsync(It.IsAny<FileMarkDto>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
                .Returns((FileMarkDto dto, int idUser, CancellationToken token) => {
                    dto.Id = FileMarks.Max(x => x.Id) + 1;
                    dto.DateCreated = DateTime.UtcNow;
                    dto.User = null;
                    FileMarks.Add(dto);

                    var result = FileMarks.Any(x => x.Id == dto.Id) ? 1 : 0;
                    return Task.FromResult(result);
                });

            var storageRepositoryMock = new Mock<IFileStorageRepository>();
            storageRepositoryMock.Setup(x => x.GetUrl(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<string>()))
                .Returns((int idWell, int idCategory, int idFile, string dotExtention) => {
                    return Path.Combine("files", idWell.ToString(), idCategory.ToString(), $"{idFile}{dotExtention}");
                });

            fileService = new FileService(repositoryMock.Object, storageRepositoryMock.Object);
        }

        [Fact]
        public async Task GetByMarkId_returns_FileInfo_by_idMark()
        {
            var data = await fileService.GetByMarkId(133, CancellationToken.None);
            Assert.NotNull(data);
        }

        [Fact]
        public async Task GetOrDefaultAsync_returns_FileInfo()
        {
            var data = await fileService.GetOrDefaultAsync(1742, CancellationToken.None);
            Assert.NotNull(data);
        }

        [Fact]
        public async Task GetInfoByIdsAsync_returns_FileInfo()
        {
            var data = await fileService.GetInfoByIdsAsync(new int[] { 1742, 1743 }, CancellationToken.None);
            Assert.NotNull(data);
        }

        [Fact]
        public async Task SaveAsync_returns_FileInfo()
        {
            using var stream = new MemoryStream(Array.Empty<byte>());
            var data = await fileService.SaveAsync(90, 1, 10040, "test.txt", stream, CancellationToken.None);
            Assert.NotNull(data);
        }

        [Fact]
        public async Task DeleteAsync()
        {
            var result = await fileService.DeleteAsync(new int[] { 1743 }, CancellationToken.None);
            Assert.True(result > 0);
        }

        [Fact]
        public async Task MarkFileMarkAsDeletedAsync()
        {
            var result = await fileService.MarkFileMarkAsDeletedAsync(new int[] { 132, 133 }, CancellationToken.None);
            Assert.True(result > 0);
        }

        [Fact]
        public async Task MarkAsDeletedAsync()
        {
            var result = await fileService.MarkAsDeletedAsync(1742, CancellationToken.None);
            Assert.True(result > 0);
        }

        [Fact]
        public async Task CreateFileMarkAsync()
        {
            var dto = new FileMarkDto { 
                Comment = "test",
                IdFile = 1742,
                IdMarkType = 1
            };
            var result = await fileService.CreateFileMarkAsync(dto, 1, CancellationToken.None);
            Assert.True(result > 0);
        }
    }
}