using AutoMapper;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tsi1.BusinessLayer.Dtos;
using Tsi1.BusinessLayer.Helpers;
using Tsi1.BusinessLayer.Interfaces;
using Tsi1.DataLayer;
using Tsi1.DataLayer.Entities;

namespace Tsi1.BusinessLayer.Services
{
    public class ActivityService : IActivityService
    {
        private readonly Tsi1Context _context;
        private readonly IMapper _mapper;

        public ActivityService(Tsi1Context context, IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
        }

        public async Task<ServiceResult<bool>> AttendVirtualClass(int activityId, int userId)
        {
            var result = new ServiceResult<bool>();

            var activity = await _context.Activities.FirstOrDefaultAsync(x => x.Id == activityId);

            if (activity == null)
            {
                result.HasError = true;
                result.AddMessage(string.Format(ErrorMessages.ActivityDoesNotExist, activityId));
            }

            var attendance = await _context.Attendances
                .FirstOrDefaultAsync(x => x.ActivityId == activityId && x.UserId == userId);

            if (attendance == null)
            {
                attendance = new Attendance
                {
                    Activity = activity,
                    UserId = userId,
                    Date = DateTime.Now,
                };
            }
            
            return result;
        }

        public async Task<ServiceResult<int>> ActivityValidation(int activityId, int userId)
        {
            var result = new ServiceResult<int>();

            var activity = await _context.Activities.AsNoTracking().FirstOrDefaultAsync(x => x.Id == activityId);

            if (activity == null)
            {
                result.HasError = true;
                result.AddMessage(string.Format(ErrorMessages.ActivityDoesNotExist, activityId));
                return result;
            }

            var validation = await this.UserTypeValidation(activity.CourseId, userId);

            if (validation.HasError)
            {
                result.HasError = true;
                result.AddMessage(validation.Message);
            }

            return result;
        }

        private async Task<ServiceResult<User>> UserTypeValidation(int courseId, int userId)
        {
            var result = new ServiceResult<User>();

            var user = await _context.Users.AsNoTracking()
                .Include(x => x.UserType)
                .FirstOrDefaultAsync(x => x.Id == userId);

            if (user == null)
            {
                result.HasError = true;
                result.AddMessage(string.Format(ErrorMessages.UserDoesNotExist, userId));
                return result;
            }

            if (user.UserType.Name == UserTypes.Professor)
            {
                var isProfessorCourse = await _context.ProfessorCourses
                    .AsNoTracking()
                    .AnyAsync(x => x.ProfessorId == user.ProfessorId && x.CourseId == courseId);

                if (!isProfessorCourse)
                {
                    result.HasError = true;
                    result.AddMessage(string.Format(ErrorMessages.ProfessorCourseDoesNotExists, user.Id, courseId));

                }
            }

            if (user.UserType.Name == UserTypes.Student)
            {
                var isStudentCourse = await _context.StudentCourses
                    .AsNoTracking()
                    .AnyAsync(x => x.StudentId == user.StudentId && x.CourseId == courseId);

                if (!isStudentCourse)
                {
                    result.HasError = true;
                    result.AddMessage(string.Format(ErrorMessages.StudentCourseDoesNotExists, user.Id, courseId));

                }
            }

            result.Data = user;
            return result;
        }

        private async Task<ServiceResult<bool>> CourseValidation(int courseId)
        {
            var result = new ServiceResult<bool>();

            var course = await _context.Courses.AsNoTracking().FirstOrDefaultAsync(x => x.Id == courseId);

            if (course == null)
            {
                result.HasError = true;
                result.AddMessage(string.Format(ErrorMessages.CourseDoesNotExist, courseId));
            }

            return result;
        }

        public async Task<ServiceResult<List<ActivityDto>>> GetAll(int courseId)
        {
            var result = new ServiceResult<List<ActivityDto>>();

            var validation = await this.CourseValidation(courseId);

            if (validation.HasError)
            {
                result.HasError = true;
                result.AddMessage(validation.Message);
                return result;
            }

            var activities = await _context.Activities
                .AsNoTracking()
                .Where(x => x.CourseId == courseId)
                .OrderBy(x => x.Date)
                .ToListAsync();

            result.Data = _mapper.Map<List<ActivityDto>>(activities);
            return result;
        }

        public async Task<ServiceResult<int>> Create(ActivityCreateDto newActivity, int userId)
        {
            var result = new ServiceResult<int>();

            var courseValidation = await this.CourseValidation(newActivity.CourseId);

            if (courseValidation.HasError)
            {
                result.HasError = true;
                result.AddMessage(courseValidation.Message);
                return result;
            }

            var userTypeValidation = await this.UserTypeValidation(newActivity.CourseId, userId);

            if (userTypeValidation.HasError)
            {
                result.HasError = true;
                result.AddMessage(userTypeValidation.Message);
                return result;
            }

            if (newActivity.IsVideoConference && newActivity.Evaluation != null)
            {
                result.HasError = true;
                result.AddMessage(ErrorMessages.VideoConferenceIsNotEvaluation);
                return result;
            }

            var activity = _mapper.Map<Activity>(newActivity);
            _context.Activities.Add(activity);

            await _context.SaveChangesAsync();

            result.Data = activity.Id;
            return result;
        }

        public async Task<ServiceResult<int>> Modify(int activityId, ActivityModifyDto activityDto, int userId)
        {
            var result = new ServiceResult<int>();

            var activity = await _context.Activities.FirstOrDefaultAsync(x => x.Id == activityId);

            if (activity == null)
            {
                result.HasError = true;
                result.AddMessage(string.Format(ErrorMessages.ActivityDoesNotExist, activityId));
                return result;
            }

            var userTypeValidation = await this.UserTypeValidation(activity.CourseId, userId);

            if (userTypeValidation.HasError)
            {
                result.HasError = true;
                result.AddMessage(userTypeValidation.Message);
                return result;
            }

            if (activityDto.IsVideoConference && activityDto.Evaluation != null)
            {
                result.HasError = true;
                result.AddMessage(ErrorMessages.VideoConferenceIsNotEvaluation);
                return result;
            }

            _mapper.Map(activityDto, activity);

            await _context.SaveChangesAsync();

            result.Data = activity.Id;
            return result;
        }

        public async Task<ServiceResult<int>> Delete(int activityId, int userId)
        {
            var result = new ServiceResult<int>();

            var activity = await _context.Activities.FirstOrDefaultAsync(x => x.Id == activityId);

            if (activity == null)
            {
                result.HasError = true;
                result.AddMessage(string.Format(ErrorMessages.ActivityDoesNotExist, activityId));
                return result;
            }

            var userTypeValidation = await this.UserTypeValidation(activity.CourseId, userId);

            if (userTypeValidation.HasError)
            {
                result.HasError = true;
                result.AddMessage(userTypeValidation.Message);
                return result;
            }

            _context.Activities.Remove(activity);

            await _context.SaveChangesAsync();

            result.Data = activity.Id;
            return result;
        }

    }
}