using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SepehrHotelApiSampleClient.Availability.GetCheckinAndCheckoutCompositions.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.V4.GetCheckinAndCheckoutCompositions.RequestModel;
using ResponseModel = Sepehr.ApiGateway.Partners.Contracts.Hotel.Availability.V4.GetCheckinAndCheckoutCompositions.ResponseModel;
using ViewModel = SepehrHotelApiSampleClient.Availability.GetCheckinAndCheckoutCompositions.WinForms.ViewModels;
using LockRequestModel = Sepehr.ApiGateway.Partners.Contracts.Hotel.Booking.V5.Lock.RequestModel;

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

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

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

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

        private void btnSearch_Click(object sender, EventArgs e)
        {
            try
            {
                btnSearch.Enabled = false;

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


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

                DataBindHotelGrid();
            }
            finally
            {
                btnSearch.Enabled = true;
            }
        }

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

            var hotelsViewModel = _hotelList
                .Select(h => new ViewModel.Hotel(h))
                .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();

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

            gridDateRangeList.DataSource = _bnsDateRanges;
        }

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

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


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

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


            _bnsBoardTypes = new BindingSource();
            _bnsBoardTypes.DataSource = currentRoomType.BoardTypeList;
            _bnsBoardTypes.PositionChanged += _bnsBoardTypes_PositionChanged; ;
            BoardTypesPositionChanged();

            gridBoardTypeList.DataSource = _bnsBoardTypes;
        }

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

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

            //bind room types
            _bnsRoomTypes = new BindingSource();
            _bnsRoomTypes.DataSource = currentDateRange.RoomTypeList;
            _bnsRoomTypes.PositionChanged += _bnsRoomTypes_PositionChanged;
            RoomTypesPositionChanged();

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

            gridRoomTypeList.DataSource = _bnsRoomTypes;

            //bind amenity
            _bnsAmenity = new BindingSource();
            _bnsAmenity.DataSource = currentDateRange.AmenityList;
            _bnsAmenity.PositionChanged += _bnsAmenity_PositionChanged;
            AmenityPositionChanged();
            gridAmenityList.DataSource = _bnsAmenity;
        }

        private void _bnsAmenity_PositionChanged(object sender, EventArgs e)
        {
            AmenityPositionChanged();
        }

        private void _bnsBoardTypes_PositionChanged(object sender, EventArgs e)
        {
            BoardTypesPositionChanged();
        }

        private void BoardTypesPositionChanged()
        {
            var currentBoardType = GetCurrentBoardTypeFromGrid();

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

            gridRateDetailList.DataSource = currentBoardType.RateDetailList;
        }

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

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

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

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

        public ViewModel.BoardType GetCurrentBoardTypeFromGrid()
        {
            if (_bnsBoardTypes?.Current == null) return null;

            return (ViewModel.BoardType)_bnsBoardTypes?.Current;
        }

        private void AmenityPositionChanged()
        {
            var currentAmenity = GetCurrentAmenityFromGrid();

            if (currentAmenity == null)
            {
                gridAmenity_RateDetailList.DataSource = null;
                return;
            }

            gridAmenity_RateDetailList.DataSource = currentAmenity.RateDetailList;
        }

        public ViewModel.Amenity GetCurrentAmenityFromGrid()
        {
            if (_bnsAmenity?.Current == null) return null;

            return (ViewModel.Amenity)_bnsAmenity?.Current;
        }

        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.DateRange.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),
                    BoardTypeCode = roomUserControl.cmbBoardType.SelectedValue.ToString(),
                    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,
                    BoardTypeCode = roomUserControl.cmbBoardType.SelectedItem.ToString(),
                }; 

                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)
        {
            return 
                GetRoomNetPrice(requestRoomList)
                +
                GetAmenityNetPrice(requestRoomList);
        }

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

            foreach (var requestRoom in requestRoomList)
            {
                var roomType = ((List<ViewModel.RoomType>)_bnsRoomTypes.DataSource)
                    .First(x => x.SepehrGlobalId == requestRoom.RoomTypeSepehrGlobalId);

                var boardType = roomType
                    .BoardTypeList
                    .First(bt => bt.Code == requestRoom.BoardTypeCode);

                result += boardType
                    .RateDetailList
                    .Sum(x => x.Room_NetPrice_TaxIncluded_AsDecimal);

                var requestAdultCount = GetRequestAdultCount(requestRoom, roomType);
                var neededExtrabedCount = GetNeededExtrabedCount(
                    requestAdultCount: requestAdultCount,
                    roomType: roomType,
                    boardType: boardType);

                result +=
                    boardType
                        .RateDetailList
                        .Sum(x => x.Extrabed_NetPrice_TaxIncluded_AsDecimal)
                    *
                    neededExtrabedCount;
            }

            return result;
        }

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

            var amenityList = ((List<ViewModel.Amenity>)_bnsAmenity.DataSource);

            foreach (var amenity in amenityList)
            {
                foreach (var requestRoom in requestRoomList)
                {
                    var adultAmenityTotalNet = amenity
                                                   .RateDetailList
                                                   .Where(rateDetail =>
                                                       rateDetail.GuestAgeRangeStart <= 30
                                                       &&
                                                       30 <= rateDetail.GuestAgeRangeEnd)
                                                   .Sum(rateDetail => rateDetail.NetPrice_TaxIncluded_AsDecimal)
                                               *
                                               requestRoom.AdultCount;

                    result += adultAmenityTotalNet;

                    foreach (var childAge in requestRoom.ChildAgeList)
                    {
                        var childAmenityTotalNet = amenity
                            .RateDetailList
                            .Where(rateDetail =>
                                rateDetail.GuestAgeRangeStart <= childAge
                                &&
                                childAge <= rateDetail.GuestAgeRangeEnd)
                            .Sum(rateDetail => rateDetail.NetPrice_TaxIncluded_AsDecimal);

                        result += childAmenityTotalNet;
                    }
                }
            }

            return result;
        }

        private static byte GetRequestAdultCount(
            LockRequestModel.Room requestRoom,
            ViewModel.RoomType roomType)
        {
            var requestAdultCount =
                requestRoom.AdultCount 
                + 
                requestRoom.ChildAgeList.Count(c => c > roomType.ChildAge);

            var freeChildCount = requestRoom.ChildAgeList.Count(c => c <= roomType.ChildAge);

            if (freeChildCount > roomType.ChildCapacity)
            {
                requestAdultCount += freeChildCount - roomType.ChildCapacity;
            }

            return Convert.ToByte(requestAdultCount);
        }

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

                var availableExtrabedCount = boardType.ExtrabedCapacity;

                if (neededExtrabedCount > availableExtrabedCount)
                {
                    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,
                    BoardTypeCodeList = x
                        .BoardTypeList
                        .Select(bt => bt.Code)
                        .Distinct()
                        .ToList()
                });

            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);
            }
        }
    }
}
