using BookingMicroservice.Exceptions;
using BookingMicroservice.Models;
using BookingMicroservice.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace BookingMicroservice.Controllers
{
    [ApiController]
    [Route("api/[Controller]")]
    public class BookingController : ControllerBase
    {
        private readonly IReservationComplianceService reservationComplianceService;
        private readonly IBookingService bookingService;

        public BookingController(IReservationComplianceService reservationComplianceService, IBookingService bookingService)
        {
            this.reservationComplianceService = reservationComplianceService;
            this.bookingService = bookingService;
        }

        [Authorize]
        [HttpGet()]
        public IActionResult GetBookings(int? flightId = null, int? userId = null, BookingClass? bookingClass = null) 
        {
            List<Booking> bookings = bookingService.GetBookings(flightId, userId, bookingClass);
            if (bookings == null)
                return BadRequest("Unable to get bookings");

            return Ok(bookings);
        }

        [Authorize]
        [HttpGet("{id}")]
        public IActionResult GetBooking([FromRoute] int id) 
        {
            Booking? booking = bookingService.GetBooking(id);
            if(booking == null)
                return NotFound($"Could not find booking with id: {id}");
        
            return Ok(booking);
        }

        [Authorize]
        [HttpPost()]
        public async Task<IActionResult> MakeBooking([FromBody] BookingCreation bookingCreationModel)
        {
            // TODO: add stripe handling
            string? userIdValue = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            if (!int.TryParse(userIdValue, out int userId))
                return BadRequest("Unable to get User Id from Token");

            try
            {
                Booking? booking = await reservationComplianceService.TryCreateBookingAsync(bookingCreationModel.FlightId, userId, bookingCreationModel.BookingClass, bookingCreationModel.SeatId);
                if (booking == null)
                    return BadRequest("Error in creating booking");

                return Ok(booking);
            } 
            catch (InvalidOperationException exception)
            {
                return BadRequest(exception.Message);
            } 
            catch (BookingException exception)
            {
                return BadRequest(exception.Message);
            }
        }

        [Authorize]
        [HttpPut("{bookingId}")]
        public async Task<IActionResult> UpdatedBookingSeat([FromRoute] int bookingId, [FromBody] BookingUpdate bookingUpdateModel)
        {
            try
            {
                await reservationComplianceService.TryBookSeatAsync(bookingId, bookingUpdateModel.SeatId);
                return Ok(new { BookedSeat = true, Message = "Seat booked successfully." });
            } 
            catch (InvalidOperationException exception)
            {
                return BadRequest(exception.Message);
            } 
            catch (BookingException exception)
            {
                return Ok(new { BookedSeat = false, Message = exception.Message });
            }
        }

        [Authorize]
        [HttpGet("upcoming")]
        public async Task<IActionResult> GetUpcomingFlightBookings()
        {
            string? userIdValue = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            if (!int.TryParse(userIdValue, out int userId))
                return BadRequest("Unable to get User Id from Token");

            try
            {
                IEnumerable<FlightBookingInfo> flights = await reservationComplianceService.TryGetUpcomingFlightsAsync(userId);
                return Ok(flights);
            } 
            catch(InvalidOperationException exception)
            {
                return BadRequest(exception.Message);
            }
        }

        [Authorize]
        [HttpGet("history")]
        public async Task<IActionResult> GetPreviousFlightBookings()
        {
            string? userIdValue = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            if (!int.TryParse(userIdValue, out int userId))
                return BadRequest("Unable to get User Id from Token");

            try
            {
                IEnumerable<FlightBookingInfo> flights = await reservationComplianceService.TryGetPreviousFlightsAsync(userId);
                return Ok(flights);
            }
            catch (InvalidOperationException exception)
            {
                return BadRequest(exception.Message);
            }
        }

    }
}