using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using InternshipSystem.Api.Queries.SearchQuery;
using InternshipSystem.Api.Security;
using InternshipSystem.Core;
using InternshipSystem.Core.Entity.Internship;
using InternshipSystem.Repository;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace InternshipSystem.Api.Controllers
{
    [ApiController]
    [Route("management/internship")]
    public class InternshipManagementController : ControllerBase
    {
        private InternshipDbContext Context { get; }
        
        public InternshipManagementController(InternshipDbContext context)
        {
            Context = context;
        }

        [HttpGet]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        [Authorize(Policy = Policies.IsOverseer)]
        public async Task<ActionResult<IReadOnlyCollection<Internship>>> GetInternships([FromQuery] InternshipSearchQuery searchQuery, CancellationToken token)
        {
            var query =
                Context.Internships
                    .Include(i => i.Edition)
                    .Include(i => i.InternshipRegistration)
                    .Include(i => i.Student)
                    .Where(i => !searchQuery.EditionId.HasValue || i.Edition.Id.Equals(searchQuery.EditionId))
                    .Where(i => !searchQuery.InternshipState.HasValue || i.InternshipRegistration.State.Equals(searchQuery.InternshipState))
                    .Where(i => !searchQuery.StudentAlbumNumber.HasValue || i.Student.AlbumNumber.Equals(searchQuery.StudentAlbumNumber))
                    .Where(i => string.IsNullOrEmpty(searchQuery.StudentFirstName) || i.Student.FirstName.ToLower().Contains(searchQuery.StudentFirstName.ToLower()))
                    .Where(i => string.IsNullOrEmpty(searchQuery.StudentLastName) || i.Student.LastName.ToLower().Contains(searchQuery.StudentLastName.ToLower()))
                    .Skip(searchQuery.Page * searchQuery.PerPage)
                    .Take(searchQuery.PerPage);
            
            if (searchQuery.OrderByField.ToLower().Equals("date"))
            {
                query = searchQuery.SortOrder.Equals(SortOrder.Desc) ? 
                    query.OrderByDescending(i => i.InternshipRegistration.SubmissionDate) : 
                    query.OrderBy(i => i.InternshipRegistration.SubmissionDate);
            } 
            else if (searchQuery.OrderByField.ToLower().Equals("internshipstate"))
            {
                query = searchQuery.SortOrder.Equals(SortOrder.Desc) ? 
                    query.OrderByDescending(i => i.InternshipRegistration.State) : 
                    query.OrderBy(i => i.InternshipRegistration.State);
            }

            var result = await query.ToListAsync(token);

            return Ok(result);
        }

        [HttpGet("{internshipId}")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [Authorize(Policy = Policies.IsOverseer)]
        public async Task<ActionResult<Internship>> GetInternship(long internshipId, CancellationToken token)
        {
            var internship = await Context.Internships
                .Include(i => i.Student)
                .Include(i => i.InternshipRegistration)
                .Include(i => i.InternshipRegistration.Company)
                .Include(i => i.InternshipRegistration.BranchAddress)
                .Include(i => i.InternshipRegistration.Type)
                .Include(i => i.InternshipRegistration.Subjects)
                .ThenInclude(subject => subject.Subject)
                .Include(i => i.InternshipRegistration.Mentor)
                .Include(i => i.Report)
                .Include(i => i.Documentation)
                .Include(i => i.Edition)
                .Where(i => i.Id.Equals(internshipId))
                .FirstOrDefaultAsync(token);

            if (internship == null)
            {
                return NotFound();
            }
            
            return Ok(internship);
        }
        
        [HttpPut("accept/{internshipId}")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [Authorize(Policy = Policies.IsOverseer)]
        public async Task<ActionResult> AcceptInternship(long internshipId, CancellationToken token)
        {
            var internship = await Context.Internships
                .Include(i => i.InternshipRegistration)
                .FirstOrDefaultAsync(i => i.Id.Equals(internshipId), token);

            if (internship == null)
            {
                return NotFound();
            }

            internship.InternshipRegistration.State = DocumentState.Accepted;

            await Context.SaveChangesAsync(token);
            
            return Ok();
        }
        
        [HttpPut("reject/{internshipId}")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [Authorize(Policy = Policies.IsOverseer)]
        public async Task<ActionResult> RejectInternship(long internshipId, CancellationToken token)
        {
            var internship = await Context.Internships
                .Include(i => i.InternshipRegistration)
                .FirstOrDefaultAsync(i => i.Id.Equals(internshipId), token);
            
            if (internship == null)
            {
                return NotFound();
            }

            internship.InternshipRegistration.State = DocumentState.Rejected;

            await Context.SaveChangesAsync(token);

            return Ok();
        }
    }
}