# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from datetime import timedelta
from odoo.tools import (
    DEFAULT_SERVER_DATE_FORMAT,
    DEFAULT_SERVER_DATETIME_FORMAT)
from odoo import models, api, _, fields
from odoo.exceptions import AccessError, ValidationError
_logger = logging.getLogger(__name__)


class HotelCalendarManagement(models.TransientModel):
    _name = 'hotel.calendar.management'

    # Business methods
    @api.multi
    def get_hcalendar_settings(self):
        return {
            'eday_week': self.env.user.hotel_id.pms_end_day_week,
            'eday_week_offset': self.env.user.hotel_id.pms_end_day_week_offset,
            'days': self.env.user.hotel_id.pms_default_num_days,
            'show_notifications': self.env.user.pms_show_notifications,
            'show_num_rooms': self.env.user.hotel_id.pms_show_num_rooms,
        }

    @api.model
    def _get_prices_values(self, price):
        vals = {
            'fixed_price': price['price'],
        }
        return vals

    @api.model
    def _get_restrictions_values(self, restriction):
        vals = {
            'min_stay': restriction['min_stay'],
            'min_stay_arrival': restriction['min_stay_arrival'],
            'max_stay': restriction['max_stay'],
            'max_stay_arrival': restriction['max_stay_arrival'],
            'closed': restriction['closed'],
            'closed_arrival': restriction['closed_arrival'],
            'closed_departure': restriction['closed_departure'],
        }
        return vals

    @api.model
    def _hcalendar_room_json_data(self, rooms):
        json_data = []
        for room in rooms:
            json_data.append({
                'id': room.id,
                'name': room.name,
                'capacity': room.get_capacity(),
                'price': room.list_price,
                'total_rooms': room.total_rooms_count,
            })
        return json_data

    @api.model
    def _hcalendar_pricelist_json_data(self, prices):
        json_data = {}
        room_type_obj = self.env['hotel.room.type']
        for rec in prices:
            room_type_id = room_type_obj.search([
                ('product_id.product_tmpl_id', '=', rec.product_tmpl_id.id)
            ], limit=1)
            if not room_type_id:
                continue

            # TODO: date_end - date_start loop
            json_data.setdefault(room_type_id.id, []).append({
                'id': rec.id,
                'price': rec.fixed_price,
                'date': rec.date_start,
            })
        return json_data

    @api.model
    def _hcalendar_restriction_json_data(self, restrictions):
        json_data = {}
        for rec in restrictions:
            json_data.setdefault(rec.room_type_id.id, []).append({
                'id': rec.id,
                'date': rec.date,
                'min_stay': rec.min_stay,
                'min_stay_arrival': rec.min_stay_arrival,
                'max_stay': rec.max_stay,
                'max_stay_arrival': rec.max_stay_arrival,
                'closed': rec.closed,
                'closed_departure': rec.closed_departure,
                'closed_arrival': rec.closed_arrival,
            })
        return json_data

    @api.model
    def _hcalendar_events_json_data(self, dfrom, dto):
        date_start = fields.Date.from_string(dfrom) - timedelta(days=1)
        date_start_str = date_start.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
        user_id = self.env['res.users'].browse(self.env.uid)
        domain = []
        if self.env.user.hotel_id.pms_allowed_events_tags:
            domain.append(('categ_ids', 'in', self.env.user.hotel_id.pms_allowed_events_tags))
        if self.env.user.hotel_id.pms_denied_events_tags:
            domain.append(
                ('categ_ids', 'not in', self.env.user.hotel_id.pms_denied_events_tags))
        events_raw = self.env['calendar.event'].search(domain)
        events_ll = self.env['calendar.event'].search([
            ('start', '<=', dto),
            ('stop', '>=', date_start_str)
        ])
        events_lr = self.env['calendar.event'].search([
            ('start', '>=', date_start_str),
            ('stop', '<=', dto)
        ])
        events = (events_ll | events_lr) & events_raw
        json_data = []
        for event in events:
            json_data.append([
                event.id,
                event.name,
                event.start,
                event.location,
            ])
        return json_data

    @api.model
    def _hcalendar_get_count_reservations_json_data(self, dfrom, dto):
        date_start = fields.Date.from_string(dfrom)
        date_end = fields.Date.from_string(dto)
        date_diff = abs((date_end - date_start).days) + 1
        room_type_obj = self.env['hotel.room.type']
        room_types = room_type_obj.search([])
        json_data = {}

        for room_type in room_types:
            for i in range(0, date_diff):
                cur_date = date_start + timedelta(days=i)
                cur_date_str = cur_date.strftime(DEFAULT_SERVER_DATE_FORMAT)
                self.env.cr.execute('''
                    SELECT
                      hrl.id
                    FROM hotel_reservation_line AS hrl
                    WHERE date = %s
                    ''', ((cur_date_str),))
                line_ids = [r[0] for r in self.env.cr.fetchall()]
                reservation_ids = self.env['hotel.reservation.line'].browse(line_ids).\
                    mapped('reservation_id.id')
                reservations = self.env['hotel.reservation'].\
                    browse(reservation_ids).filtered(
                        lambda r: r.state != 'cancelled'
                        and not r.overbooking and not r.reselling
                    )
                reservations_rooms = reservations.mapped('room_id.id')
                free_rooms = self.env['hotel.room'].search([
                    ('id', 'not in', reservations_rooms),
                ])
                rooms_linked = self.env['hotel.room.type'].search([
                    ('id', '=', room_type.id)
                ]).room_ids
                free_rooms = free_rooms & rooms_linked
                json_data.setdefault(room_type.id, []).append({
                    'date': cur_date_str,
                    'num': len(free_rooms),
                })

        return json_data

    @api.model
    def get_hcalendar_all_data(self, dfrom, dto, pricelist_id, restriction_id,
                               withRooms):
        hotel_id = self.env.user.hotel_id.id

        if not dfrom or not dto:
            raise ValidationError(_('Input Error: No dates defined!'))
        vals = {}
        # TODO: refactoring res.config.settings', 'default_pricelist_id' by the current hotel.property.pricelist_id
        if not pricelist_id:
            pricelist_id = self.env.user.hotel_id.default_pricelist_id.id
        # TODO: refactoring res.config.settings', 'default_restriction_id by the current hotel.property.restriction_id
        if not restriction_id:
            restriction_id = self.env.user.hotel_id.default_restriction_id.id

        # TODO: ensure pricelist_id and restriction_id belong to the current hotel
        vals.update({'pricelist_id': pricelist_id})
        vals.update({'restriction_id': restriction_id})

        restriction_item_ids = self.env['hotel.room.type.restriction.item'].search([
            ('date', '>=', dfrom), ('date', '<=', dto),
            ('restriction_id', '=', restriction_id),
        ])
        pricelist_item_ids = self.env['product.pricelist.item'].search([
            ('date_start', '>=', dfrom), ('date_end', '<=', dto),
            ('pricelist_id', '=', pricelist_id),
            ('applied_on', '=', '1_product'),
            ('compute_price', '=', 'fixed'),
        ])

        json_prices = self._hcalendar_pricelist_json_data(pricelist_item_ids)
        json_rest = self._hcalendar_restriction_json_data(restriction_item_ids)
        # TODO REVIEW: what are json_rc and json_events used for in calendar management ¿?
        json_rc = self._hcalendar_get_count_reservations_json_data(dfrom, dto)
        json_events = self._hcalendar_events_json_data(dfrom, dto)
        vals.update({
            'prices': json_prices or [],
            'restrictions': json_rest or [],
            'count_reservations': json_rc or [],
            'events': json_events or [],
        })

        if withRooms:
            room_ids = self.env['hotel.room.type'].search([
                ('hotel_id', '=', hotel_id)
            ], order='sequence ASC') or None

            if not room_ids:
                raise AccessError(
                    _("Wrong hotel and company access settings for this user. "
                      "No room types found for hotel %s") % self.env.user.hotel_id.name)

            json_rooms = self._hcalendar_room_json_data(room_ids)
            vals.update({'rooms': json_rooms or []})

        return vals

    @api.model
    def save_changes(self, pricelist_id, restriction_id, pricelist,
                     restrictions, availability=False):
        room_type_obj = self.env['hotel.room.type']
        product_pricelist_item_obj = self.env['product.pricelist.item']
        room_type_rest_item_obj = self.env['hotel.room.type.restriction.item']

        # Save Pricelist
        for k_price in pricelist.keys():
            room_type_id = room_type_obj.browse([int(k_price)])
            room_type_prod_tmpl_id = room_type_id.product_id.product_tmpl_id
            for price in pricelist[k_price]:
                price_id = product_pricelist_item_obj.search([
                    ('date_start', '>=', price['date']),
                    ('date_end', '<=', price['date']),
                    ('pricelist_id', '=', int(pricelist_id)),
                    ('applied_on', '=', '1_product'),
                    ('compute_price', '=', 'fixed'),
                    ('product_tmpl_id', '=', room_type_prod_tmpl_id.id),
                ], limit=1)
                vals = self._get_prices_values(price)
                if not price_id:
                    vals.update({
                        'date_start': price['date'],
                        'date_end': price['date'],
                        'pricelist_id': int(pricelist_id),
                        'applied_on': '1_product',
                        'compute_price': 'fixed',
                        'product_tmpl_id': room_type_prod_tmpl_id.id,
                    })
                    price_id = product_pricelist_item_obj.create(vals)
                else:
                    price_id.write(vals)

        # Save Restrictions
        for k_res in restrictions.keys():
            for restriction in restrictions[k_res]:
                res_id = room_type_rest_item_obj.search([
                    ('date', '=', restriction['date']),
                    ('restriction_id', '=', int(restriction_id)),
                    ('room_type_id', '=', int(k_res)),
                ], limit=1)
                vals = self._get_restrictions_values(restriction)
                if not res_id:
                    vals.update({
                        'date': restriction['date'],
                        'restriction_id': int(restriction_id),
                        'room_type_id': int(k_res),
                    })
                    res_id = room_type_rest_item_obj.create(vals)
                else:
                    res_id.write(vals)
