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

namespace AsbCloudWebApi.Tests.ServicesTests
{
    public class WellFinalDocumentsServiceTest
    {
        private const int validInsertedFileId = 555;
        private const int idWellFinalDocCategory = 10_000;
        private const string editPublisherPermission = "WellFinalDocuments.editPublisher";
        private readonly WellFinalDocumentsService service;
        private readonly Mock<IUserRepository> userRepositoryMock;
        private readonly Mock<IWellService> wellServiceMock;
        private readonly Mock<IFileCategoryService> fileCategoryService;
        private readonly NotificationService notificationService;
        private readonly Mock<ICrudRepository<NotificationCategoryDto>> notificationCategoryRepositoryMock;
        private readonly Mock<INotificationTransportService> notificationTransportServiceMock;

        private static readonly UserExtendedDto[] users = new[]{
            new UserExtendedDto {
                Id = 1,
                IdCompany = 1,
                Surname = "Tester 1",
                Name = "Peppa",
                Email = "test@test.com"
            },
            new UserExtendedDto {
                Id = 3,
                IdCompany = 1,
                Surname = "Tester 3",
                Name = "Jourge",
                Email = "test1@test1.com"
            }
        };

        private static readonly WellFinalDocumentDto[] wellFinalDocumentDto = new[]
        {
            new WellFinalDocumentDto {
                IdCategory= idWellFinalDocCategory,
                PermissionToUpload = true,
                Publishers = new List<UserDto> {
                    new UserDto {
                        Id = 1
                    }
                }
            }
        };

        private static readonly WellCaseDto wellCaseDto = new WellCaseDto {
            IdWell = 1,
            PermissionToSetPubliher = true,
            WellFinalDocuments = wellFinalDocumentDto
        };

        private static readonly WellFinalDocumentDBDto wellFinalDocumentDBDto = new WellFinalDocumentDBDto {
            IdCategory = idWellFinalDocCategory,
            IdUser = 1,
            IdWell = 1
        };

        private readonly Mock<IFileRepository> fileRepositoryMock;
        private readonly Mock<IFileStorageRepository> fileStorageRepositoryMock;
        private readonly FileService fileService;
        private readonly Mock<IWellFinalDocumentsRepository> wellFinalDocumentsRepository;

        public WellFinalDocumentsServiceTest()
        {
            wellFinalDocumentsRepository = new Mock<IWellFinalDocumentsRepository>();
            wellFinalDocumentsRepository.Setup(r => r.GetByWellIdAsync(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
                .ReturnsAsync(wellCaseDto);

            wellFinalDocumentsRepository.Setup(r => r.GetCategoryAsync(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
                .ReturnsAsync(wellFinalDocumentDBDto);

            fileRepositoryMock = new Mock<IFileRepository>();
            fileRepositoryMock.Setup(r => r.InsertAsync(It.IsAny<FileInfoDto>(), It.IsAny<CancellationToken>()))
                .ReturnsAsync(validInsertedFileId);
            fileRepositoryMock.Setup(r => r.GetOrDefaultAsync(validInsertedFileId, It.IsAny<CancellationToken>()))
                .ReturnsAsync(new FileInfoDto {Id = validInsertedFileId});
            
            fileStorageRepositoryMock = new Mock<IFileStorageRepository>();
            fileService = new FileService(fileRepositoryMock.Object, fileStorageRepositoryMock.Object);

            userRepositoryMock = new Mock<IUserRepository>();
            userRepositoryMock.Setup(x => x.GetAllAsync(It.IsAny<CancellationToken>()))
                .ReturnsAsync(users);
            userRepositoryMock.Setup(x => x.GetOrDefault(It.IsAny<int>()))
                .Returns<int>(id => GetOrDefaultUserById(id));

            userRepositoryMock.Setup(x => x.GetOrDefaultAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
                .ReturnsAsync((int id, CancellationToken token) =>  GetOrDefaultUserById(id));

            UserExtendedDto? GetOrDefaultUserById(int id)
                => users
                .Where(u => u.Id == id)
                .Select(u => new UserExtendedDto { 
                    Id = u.Id,
                    IdCompany = u.IdCompany,
                    Email = u.Email,
                    Name = u.Name,
                    Patronymic = u.Patronymic,
                    Surname = u.Surname,
                    IdState = u.IdState,
                    Login = u.Login,
                    Position = u.Position
                })
                .FirstOrDefault();

            userRepositoryMock.Setup(x => x.HasPermission(users[0].Id, editPublisherPermission))
                .Returns(true);

            wellServiceMock = new Mock<IWellService>();
            wellServiceMock.Setup(s => s.GetOrDefaultAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
                .ReturnsAsync((int id, CancellationToken _) => new WellDto { 
                    Id = id, 
                    Caption = "well 1",
                    Cluster = "cluster 1",
                    Deposit = "deposit 1" });
            var configuration = new Microsoft.Extensions.Configuration.ConfigurationBuilder().Build();

            notificationCategoryRepositoryMock = new Mock<ICrudRepository<NotificationCategoryDto>>();

            notificationCategoryRepositoryMock.Setup(r => r.GetOrDefaultAsync(It.IsAny<int>(),
                    It.IsAny<CancellationToken>()))
                .ReturnsAsync(new NotificationCategoryDto
                {
                    Id = 20000,
                    Name = "Системные уведомления"
                });

            notificationTransportServiceMock = new Mock<INotificationTransportService>();

            notificationTransportServiceMock.SetupGet(x => x.IdTransportType)
                .Returns(1);
            
            notificationService = new NotificationService(notificationCategoryRepositoryMock.Object,
                new Mock<INotificationRepository>().Object,
                new [] { notificationTransportServiceMock.Object });

            fileCategoryService = new Mock<IFileCategoryService>();
            fileCategoryService.Setup(s => s.GetOrDefaultAsync(idWellFinalDocCategory, It.IsAny<CancellationToken>()))
                .ReturnsAsync((int id, CancellationToken _) => new FileCategoryDto
                {
                    Id = idWellFinalDocCategory,
                    Name = "Проект на бурение транспортного и горизонтального участков скважины"
                });

            service = new WellFinalDocumentsService(
                fileService: fileService,
                userRepository: userRepositoryMock.Object,
                wellService: wellServiceMock.Object,
                configuration: configuration,
                notificationService: notificationService,
                fileCategoryService: fileCategoryService.Object, 
                wellFinalDocumentsRepository: wellFinalDocumentsRepository.Object);
        }

        [Fact]
        public async Task GetHistoryFileByIdCategory_return_empty_hitory()
        {
            var data = await service.GetFilesHistoryByIdCategoryAsync(1, 13 * idWellFinalDocCategory, CancellationToken.None);
            Assert.NotNull(data);
            Assert.Empty(data.Files);
        }

        [Fact]
        public async Task SaveCategoryFile_throws_wrong_user()
        {
            var content = new byte[] {0xAA, 0xBB};
            var stream = new MemoryStream(content);
            var data = await service.SaveCategoryFileAsync(1, idWellFinalDocCategory, users[0].Id, stream, "test.txt", CancellationToken.None);
            Assert.Equal(555, data);
        }

        [Fact]
        public async Task SaveCategoryFile_returns_file_id()
        {
            var content = new byte[] { 0xAA, 0xBB };
            var stream = new MemoryStream(content);
            var token = CancellationToken.None;
            var idFile = await service.SaveCategoryFileAsync(1, idWellFinalDocCategory, users[0].Id, stream, "test.txt", CancellationToken.None);
            Assert.Equal(validInsertedFileId, idFile);
            fileRepositoryMock.Verify(m => m.InsertAsync(It.IsAny<FileInfoDto>(), token));
            fileStorageRepositoryMock.Verify(m=>m.SaveFileAsync(It.IsAny<string>(), stream, token));
        }

        [Fact]
        public async Task ReNotifyPublishersAsync_deny_to_non_editors()
        {
            var data = await service.ReNotifyPublishersAsync(1, users[1].Id, idWellFinalDocCategory, CancellationToken.None);
            Assert.Equal(1, data);
        }

        [Fact]
        public async Task ReNotifyPublishersAsync_deny_to_non_wrong_category()
        {
            var data = await service.ReNotifyPublishersAsync(1, users[0].Id, idWellFinalDocCategory, CancellationToken.None);
            Assert.Equal(1, data);
        }
    }
}