using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SepehrHotelApiSampleClient.Availability.GetByDateRange.WebApi;
using SepehrHotelApiSampleClient.Availability.SearchByCityAndDate.WinForms;
using SepehrHotelApiSampleClient.Booking.Book.WinForms;
using SepehrHotelApiSampleClient.Booking.Book.WinForms.BookCommand;
using SepehrHotelApiSampleClient.BuiltinTypeExtensions;
using RequestModel = Sepehr.ApiGateway.Partners.Contracts.Hotel.Availability.V2.DateRange.RequestModel;
using ResponseModel = Sepehr.ApiGateway.Partners.Contracts.Hotel.Availability.V2.DateRange.ResponseModel;
using ViewModel = SepehrHotelApiSampleClient.Availability.GetByDateRange.WinForms.ViewModels;
using LockRequestModel = Sepehr.ApiGateway.Partners.Contracts.Hotel.Booking.V2.Lock.RequestModel;

namespace SepehrHotelApiSampleClient.Availability.GetByDateRange.WinForms
{
    public partial class GetByDateRangeForm : Form
    {
        private readonly MainForm _mainForm;

        private List<ResponseModel.Hotel> _hotelList = new List<ResponseModel.Hotel>();
        private List<ViewModel.DateRange> _dateRangeList = new List<ViewModel.DateRange>();

        public GetByDateRangeForm(MainForm mainForm)
        {
            _mainForm = mainForm;
            InitializeComponent();

            dtpCheckinDate.Value = DateTime.Now.AddDays(1);
            cmbRoomCount.SelectedItem = "1";
        }

        private void btnSearch_Click(object sender, EventArgs e)
        {
            var request = new RequestModel.Request
            {
                Username = _mainForm.txtUsername.Text,
                Password = _mainForm.txtPassword.Text,
                CheckinDate = dtpCheckinDate.Value.ToString("yyyy-MM-dd"),
            };


            var response = GetByDateRangeApi.Post(_mainForm.txtUrl.Text, request);
            _hotelList = response.HotelList;

            DataBindHotelGrid();
        }

        private BindingSource _bnsHotels;
        private BindingSource _bnsRoomTypes;
        private BindingSource _bnsDateRanges;
        private void DataBindHotelGrid()
        {
            _bnsHotels = new BindingSource();

            var hotelsViewModel = _hotelList
                .Select(h => new ViewModel.Hotel(h))
                .ToList();

            _dateRangeList = hotelsViewModel
                .SelectMany(x => x.RoomTypeList)
                .SelectMany(x => x.DateRangeList)
                .ToList();

            _bnsHotels.DataSource = hotelsViewModel;

            gridHotelList.DataSource = _bnsHotels;
        }

        public ViewModel.Hotel GetCurrentHotelFromGrid()
        {
            return (ViewModel.Hotel)_bnsHotels?.Current;
        }

        private void gridHotelList_SelectionChanged(object sender, EventArgs e)
        {
            if (_bnsHotels.Current == null) return;

            var hotel = GetCurrentHotelFromGrid();

            _bnsRoomTypes = new BindingSource();
            _bnsRoomTypes.DataSource = hotel.RoomTypeList;
            _bnsRoomTypes.PositionChanged += _bnsRoomTypes_PositionChanged;
            RoomTypesPositionChanged();

            cmbRoomCount.SelectedItem = "1";
            DataBindRoomCount();

            gridRoomTypeList.DataSource = _bnsRoomTypes;
        }

        private void _bnsRoomTypes_PositionChanged(object sender, EventArgs e)
        {
            RoomTypesPositionChanged();
        }

        private void _bnsDateRanges_PositionChanged(object sender, EventArgs e)
        {
            DateRangesPositionChanged();
        }

        private void RoomTypesPositionChanged()
        {
            var currentRoomType = GetCurrentRoomTypeFromGrid();

            if (currentRoomType == null)
            {
                gridDateRangeList.DataSource = null;
                return;
            }

            _bnsDateRanges = new BindingSource();
            _bnsDateRanges.DataSource = currentRoomType.DateRangeList;
            _bnsDateRanges.PositionChanged += _bnsDateRanges_PositionChanged;
            DateRangesPositionChanged();

            gridDateRangeList.DataSource = _bnsDateRanges;
        }

        private void DateRangesPositionChanged()
        {
            var currentDateRange = GetCurrentDateRangeFromGrid();

            if (currentDateRange == null)
            {
                gridRateDetailList.DataSource = null;
                return;
            }

            gridRateDetailList.DataSource = currentDateRange.RateDetailList;
        }

        public ViewModel.RoomType GetCurrentRoomTypeFromGrid()
        {
            if (_bnsRoomTypes?.Current == null) return null;

            return (ViewModel.RoomType)_bnsRoomTypes?.Current;
        }

        public ViewModel.RoomType GetRoomTypeFromGrid(int sepehrGlobalId)
        {
            return ((List<ViewModel.RoomType>)_bnsRoomTypes.DataSource)
                .First(x => x.SepehrGlobalId == sepehrGlobalId);
        }

        public ViewModel.DateRange GetCurrentDateRangeFromGrid()
        {
            if (_bnsDateRanges?.Current == null) return null;

            return (ViewModel.DateRange)_bnsDateRanges?.Current;
        }

        public ViewModel.DateRange GetDateRangeByRoomTypeAndDate(
            int sepehrGlobalId)
        {
            var currentDateRange = GetCurrentDateRangeFromGrid();

            var result =  _dateRangeList
                .FirstOrDefault(x =>
                    x.RoomType.SepehrGlobalId == sepehrGlobalId
                    &&
                    x.CheckinDate == currentDateRange.CheckinDate
                    &&
                    x.CheckoutDate == currentDateRange.CheckoutDate);

            if (result == null)
            {
                throw new Exception("No rate is available for this room type");
            }

            return result;
        }

        private void btnLock_Click(object sender, EventArgs e)
        {
            var currentRoomType = GetCurrentRoomTypeFromGrid();
            var currentDateRange = GetCurrentDateRangeFromGrid();

            if (currentDateRange == null)
            {
                MessageBox.Show("Please select date range.");
                return;
            }

            var requestRoomList = GetRoomsForLockRequest();

            var totalNetPriceTaxIncluded = GetTotalNetPrice_TaxIncluded(requestRoomList);

            var lockRequest = new LockRequestModel.Request
            {
                Username = _mainForm.txtUsername.Text,
                Password = _mainForm.txtPassword.Text,
                CheckinDate = dtpCheckinDate.Value.ToString("yyyy-MM-dd"),
                CheckoutDate = currentDateRange.CheckoutDate,
                TotalPayable = totalNetPriceTaxIncluded,
                RoomList = requestRoomList,
            };

            var lockResponse = Booking.Lock.WebApi.LockApi.Post(
                _mainForm.txtUrl.Text,
                lockRequest);

            var bookForm = new BookForm(
                mainForm: _mainForm,
                command:new Command
                {
                    CheckinDate = dtpCheckinDate.Value,
                    CheckoutDate = currentDateRange.CheckoutDate.ParseAsGregorianDate(),
                    TotalNetPrice_TaxIncluded= totalNetPriceTaxIncluded,
                    HotelName= currentRoomType.Hotel.Name,
                    RoomList= GetRoomsForBookForm(),
                    LockId= lockResponse.LockId
                });

            bookForm.ShowDialog();
        }

        private List<LockRequestModel.Room> GetRoomsForLockRequest()
        {
            var result = new List<LockRequestModel.Room>();

            var roomCount = Convert.ToByte(cmbRoomCount.SelectedItem);
            for (int counter = roomCount - 1; counter >= 0; counter--)
            {
                var roomUserControl = (RoomUserControl)roomsPanel.Controls[counter];

                var requestRoom = new LockRequestModel.Room
                {
                    RoomTypeSepehrGlobalId = Convert.ToInt32(roomUserControl.cmbRoomType.SelectedValue),
                    AdultCount = Convert.ToByte(roomUserControl.nudAdultCount.Value),
                    ChildAgeList = new List<byte>(),
                };

                for (int childCounter = 1; childCounter <= Convert.ToByte(roomUserControl.nudChildCount.Value); childCounter++)
                {
                    var comboboxItem = (RoomUserControl.ComboboxItem)(((ComboBox)roomUserControl.Controls[$"cmbChild{childCounter}Age"]).SelectedItem);

                    if (comboboxItem.Value == -1)
                    {
                        throw new Exception($"Please select child age");
                    }

                    requestRoom.ChildAgeList.Add(comboboxItem.ValueAsByte);
                }

                result.Add(requestRoom);
            }

            return result;
        }

        private List<Room> GetRoomsForBookForm()
        {
            var result = new List<Room>();

            var roomCount = Convert.ToByte(cmbRoomCount.SelectedItem);
            for (int counter = roomCount - 1; counter >= 0; counter--)
            {
                var roomUserControl = (RoomUserControl)roomsPanel.Controls[counter];

                var room = new Room
                {
                    AdultCount = Convert.ToByte(roomUserControl.nudAdultCount.Value),
                    ChildAgeList = new List<byte>(),
                    RoomTypeName = ((RoomUserControl.RoomType)roomUserControl.cmbRoomType.SelectedItem).Name,
                }; 

                for (int childCounter = 1; childCounter <= Convert.ToByte(roomUserControl.nudChildCount.Value); childCounter++)
                {
                    var comboboxItem = (RoomUserControl.ComboboxItem)(((ComboBox)roomUserControl.Controls[$"cmbChild{childCounter}Age"]).SelectedItem);

                    if (comboboxItem.Value == -1)
                    {
                        throw new Exception($"Please select child age");
                    }

                    room.ChildAgeList.Add(comboboxItem.ValueAsByte);
                }

                result.Add(room);
            }

            return result;
        }

        private decimal GetTotalNetPrice_TaxIncluded(List<LockRequestModel.Room> requestRoomList)
        {
            var result = 0M;

            
            var roomCount = Convert.ToByte(cmbRoomCount.SelectedItem);
            for (int counter = roomCount - 1; counter >= 0; counter--)
            {
                var roomUserControl = (RoomUserControl)roomsPanel.Controls[counter];

                var roomTypeSepehrGlobalId = Convert.ToInt32(roomUserControl.cmbRoomType.SelectedValue);

                var roomType = ((List<ViewModel.RoomType>)_bnsRoomTypes.DataSource)
                    .First(x => x.SepehrGlobalId == roomTypeSepehrGlobalId);

                var dateRange = GetDateRangeByRoomTypeAndDate(
                    roomType.SepehrGlobalId);

                result += dateRange.RateDetailList.Sum(x => x.Room_NetPrice_TaxIncluded);

                var requestAdultCount = GetRequestAdultCount(roomUserControl, roomType);
                var neededExtrabedCount = GetNeededExtrabedCount(requestAdultCount, roomType, dateRange);

                result +=
                    dateRange.RateDetailList.Sum(x => x.Extrabed_NetPrice_TaxIncluded)
                    *
                    neededExtrabedCount;
            }

            return result;
        }

        private static byte GetRequestAdultCount(
            RoomUserControl roomUserControl, 
            ViewModel.RoomType roomType)
        {
            var requestAdultCount = Convert.ToByte(roomUserControl.nudAdultCount.Value);

            for (int childCounter = 1; childCounter <= Convert.ToByte(roomUserControl.nudChildCount.Value); childCounter++)
            {
                var comboboxItem =
                    (RoomUserControl.ComboboxItem)(((ComboBox)roomUserControl.Controls[$"cmbChild{childCounter}Age"])
                        .SelectedItem);

                if (comboboxItem.Value == -1)
                {
                    throw new Exception($"Please select child age");
                }

                if (comboboxItem.ValueAsByte > roomType.ChildAge)
                {
                    requestAdultCount++;
                }
            }

            return requestAdultCount;
        }

        private static int GetNeededExtrabedCount(
            byte requestAdultCount, 
            ViewModel.RoomType roomType,
            ViewModel.DateRange dateRange)
        {
            var neededExtrabedCount = 0;
            if (requestAdultCount > roomType.AdultCapacity)
            {
                neededExtrabedCount =
                    requestAdultCount
                    -
                    roomType.AdultCapacity;

                if (neededExtrabedCount > dateRange.ExtrabedCapacity)
                {
                    throw new Exception($"No enough extrabed is available.");
                }
            }

            return neededExtrabedCount;
        }

        private void cmbRoomCount_SelectedIndexChanged(object sender, EventArgs e)
        {
            DataBindRoomCount();
        }

        private void DataBindRoomCount()
        {
            //moghe load form in event seda mishe, vali _bnsRoomTypes null hast
            //ya zamani ke hanoz availability nagereftim in null hhast
            if (_bnsRoomTypes == null)
            {
                return;
            }

            var roomTypeList = ((List<ViewModel.RoomType>)_bnsRoomTypes.DataSource)
                .ConvertAll(x => new RoomUserControl.RoomType
                {
                    SepehrGlobalId = x.SepehrGlobalId,
                    Name = x.Name,
                });

            var roomCount = Convert.ToByte(cmbRoomCount.SelectedItem);



            roomsPanel.Controls.Clear();
            for (int counter = 0; counter < roomCount; counter++)
            {
                var roomUserControl = new RoomUserControl(
                    roomTypeList,
                    this.lblChild1Age,
                    this.lblChild2Age,
                    this.lblChild3Age,
                    this.lblChild4Age);
                roomUserControl.Dock = DockStyle.Top;
                roomsPanel.Controls.Add(roomUserControl);
            }
        }
    }
}
