# This file is part of Presik.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from datetime import datetime, date, timedelta

from trytond.model import ModelView, ModelSQL, Workflow, fields
from trytond.pyson import Eval
from trytond.wizard import Wizard, StateView, Button, StateReport
from trytond.pool import Pool
from trytond.report import Report
from trytond.transaction import Transaction

ROOM_STATUS = [
    ('inspected', 'Inspected'),
    ('not_authorized', 'Not Authorized'),
    ('dirty', 'Dirty'),
    ('clean', 'Clean'),
    ('maintenance', 'Maintenance'),
]


class CleaningType(ModelSQL, ModelView):
    "Room Cleaning Type"
    __name__ = "hotel.room.cleaning_type"
    name = fields.Char('Name', required=True)
    description = fields.Text('Description')
    kind = fields.Selection([
        ('tweak', 'Tweak'),
        ('deep', 'Deep'),
        ('standard', 'Standard'),
        ], 'Kind', required=True)


class Room(Workflow, ModelSQL, ModelView):
    'Hotel Room'
    __name__ = 'hotel.room'
    name = fields.Char('Name', required=True)
    code = fields.Char('Code')
    active = fields.Boolean('Active')
    templates = fields.Many2Many('hotel.room-product.template', 'room',
        'template', 'Hotel Room - Product Accommodation', domain=[
            ('type', '=', 'service'),
            ('kind', '=', 'accommodation'),
        ])
    classification = fields.Many2One('hotel.room.classification',
        'Classification')
    location = fields.Many2One('hotel.location', 'Location',
        select=True)
    amenities = fields.Many2Many('hotel.room-hotel.amenities', 'room',
        'amenities', 'Amenities')
    space = fields.Integer('Space', help='Space on m2')
    channel_id = fields.Char('Channel Manager ID')
    main_accommodation = fields.Many2One('product.template',
        'Main Accommodation', ondelete='RESTRICT', depends=['templates'],
            domain=[('id', 'in', Eval('templates', []))],
        )
    # TODO: Maybe add require a Current State field
    min_unit_price = fields.Numeric('Min. Unit Price', digits=(16, 4))
    max_unit_price = fields.Numeric('Max. Unit Price', digits=(16, 4))
    state = fields.Selection(ROOM_STATUS, 'Status', required=True, readonly=True)
    state_string = state.translated('state')
    cleaning_type = fields.Function(fields.Many2One(
        'hotel.room.cleaning_type', 'Cleaning Type', required=False),
        'get_cleaning_type')
    last_check_in = fields.DateTime('Last Check In')
    last_check_out = fields.DateTime('Last Check Out')
    last_clean = fields.DateTime('Last Clean')
    housekeeping = fields.Many2One('company.employee', 'Employee')
    notes = fields.Text('Notes')
    max_accommodation = fields.Integer('Max. Accommodation')
    guests = fields.Function(fields.Integer('Guests'), 'get_guests')
    current_guests = fields.Function(fields.Integer('Guests'),
        'get_current_guests')
    check_out_today = fields.Time('Check Out Today', states={
        "readonly": True,
    })
    arrival_today = fields.Function(fields.Boolean(
        'Arrival Today'), 'get_is_checked_today')
    check_in_today = fields.Time('Check In Today', states={
        "readonly": True,
    })
    departure_today = fields.Function(fields.Boolean(
        'Departure Today'), 'get_is_checked_today')
    occupied_today = fields.Function(fields.Boolean(
        'Occupied Today'), 'get_occupied_today')

    @classmethod
    def __setup__(cls):
        super(Room, cls).__setup__()
        cls._transitions |= set((
            ('clean', 'dirty'),
            ('clean', 'inspected'),
            ('clean', 'maintenance'),
            ('not_authorized', 'clean'),
            ('not_authorized', 'dirty'),
            ('dirty', 'clean'),
            ('dirty', 'maintenance'),
            ('dirty', 'inspected'),
            ('dirty', 'not_authorized'),
            ('maintenance', 'inspected'),
            ('maintenance', 'dirty'),
            ('inspected', 'dirty'),
            ('inspected', 'maintenance'),
            ('inspected', 'clean'),
        ))
        cls._order = [('code', 'DESC')]
        cls._buttons.update({
            'clean': {
                'invisible': Eval('state').in_(['maintenance', 'clean']),
            },
            'dirty': {
                'invisible': Eval('state').in_(['dirty']),
            },
            'inspected': {
                'invisible': Eval('state').in_(['inspected']),
            },
            'maintenance': {
                'invisible': Eval('state').in_(['maintenance']),
            },
            'not_authorized': {
                'invisible': Eval('state').in_(['maintenance']),
            },
        })

    @staticmethod
    def default_active():
        return True

    @staticmethod
    def default_state():
        return 'dirty'

    @classmethod
    @ModelView.button
    @Workflow.transition('clean')
    def clean(cls, records):
        for rec in records:
            rec.last_clean = datetime.now()
            rec.cleaning_type = None
            rec.save()

    @classmethod
    @ModelView.button
    @Workflow.transition('not_authorized')
    def not_authorized(cls, records):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('dirty')
    def dirty(cls, records):
        Config = Pool().get('hotel.configuration')
        config = Config.get_configuration()
        for rec in records:
            rec.cleaning_type = config.cleaning_occupied.id

    @classmethod
    @ModelView.button
    @Workflow.transition('maintenance')
    def maintenance(cls, records):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('inspected')
    def inspected(cls, records):
        pass

    @classmethod
    def available_by_classification(cls, start_date, end_date):
        pool = Pool()
        Classification = pool.get('hotel.room.classification')
        Folio = pool.get('hotel.folio')
        RatePlan = pool.get('hotel.rate_plan')
        fmt = "%Y-%m-%d"
        plan = {}
        # FIXME: Add estimation of plan based on arrival date
        plans = RatePlan.search([])
        if plans:
            _plan = plans[0]
            plan =  {"id": _plan.id, "rec_name": _plan.price_list.name }
        if isinstance(start_date, str):
            start_date = datetime.strptime(start_date, fmt).date()
            end_date = datetime.strptime(end_date, fmt).date()
        all_rooms = [ro['id'] for ro in Room.search_read([], fields_names=[])]

        rooms_available = Folio.get_available_rooms(start_date, end_date, all_rooms)
        classifications = Classification.search_read([],
            fields_names=['id', 'name'])

        clssf = {}
        rooms = cls.browse(rooms_available)
        for room in rooms:
            product = room.main_accommodation.products[0]
            _room = {
                "id": room.id,
                "name": room.name
            }
            try:
                clssf[product.id]['available'].append(1)
                clssf[product.id]['rooms'].append(_room)
            except:
                clssf[product.id] = {
                    "product": {"id": product.id, "name": product.name},
                    "sale_price_taxed": product.sale_price_taxed,
                    "available": [1],
                    "rooms": [_room],
                    "rate_plan": plan,
                }

        for k, v in clssf.items():
            v['available'] = sum(v['available'])
        return list(clssf.values())

    def get_current_guests(self, name=None):
        pool = Pool()
        Folio = pool.get('hotel.folio')
        today = date.today()
        folios = Folio.search(['OR', [
                ('room', '=', self.id),
                ('arrival_date', '=', today),
                ('registration_state', '=', 'check_in'),
            ], [
                ('room', '=', self.id),
                ('departure_date', '=', today),
                ('registration_state', '=', 'check_in'),
            ], [
                ('room', '=', self.id),
                ('arrival_date', '>', today),
                ('departure_date', '<', today),
            ]
        ])
        res = []
        for folio in folios:
            res.append(len(folio.guests))
        return sum(res)

    def get_occupied_today(self, name=None):
        Folio = Pool().get('hotel.folio')
        res = Folio.search_read([
            ('room', '=', self.id),
            ('arrival_date', '<', date.today()),
            ('departure_date', '>', date.today()),
        ], fields_names=['id'])
        return bool(res)

    def get_is_checked_today(self, name=None):
        pool = Pool()
        Folio = pool.get('hotel.folio')
        if name == 'arrival_today':
            column = "arrival_date"
        else:
            column = "departure_date"
        res = Folio.search_read([
            ('room', '=', self.id),
            (column, '=', date.today()),
        ], fields_names=['id'])
        return bool(res)

    def get_guests(self, name=None):
        pool = Pool()
        Folio = pool.get('hotel.folio')
        folios = Folio.search([
            ('room', '=', self.id),
            ('arrival_date', '<=', date.today()),
            ('departure_date', '>', date.today())
        ])
        res = []
        for folio in folios:
            res.append(len(folio.guests))
        return sum(res)

    def get_cleaning_type(self, name=None):
        pool = Pool()
        Config = pool.get('hotel.configuration')
        Folio = pool.get('hotel.folio')
        config = Config.get_configuration()
        today = date.today()
        yesterday = today - timedelta(days=-1)
        read = Folio.search_read
        folios = read([
            ('room', '=', self.id),
            ('departure_date', '=', today)
            ], fields_names=['registration_state'],
        )
        if folios:
            return config.cleaning_check_out.id
        else:
            folios = read([
                ('room', '=', self.id),
                ('departure_date', '>', today),
                ('arrival_date', '<', today),
                ], fields_names=['registration_state'],
            )
            if folios:
                return config.cleaning_occupied.id
            else:
                return config.cleaning_check_in.id

    @classmethod
    def set_dirty_rooms(cls, name=None):
        pool = Pool()
        rooms = cls.search([])
        cls.write(rooms, {'state': 'dirty'})


class RoomAmenities(ModelSQL):
    'Room - Amenities'
    __name__ = 'hotel.room-hotel.amenities'
    _rec_name = 'name'
    room = fields.Many2One('hotel.room', 'Room', required=True,
        ondelete='CASCADE')
    amenities = fields.Many2One('hotel.amenities', 'Amenities',
        required=True, ondelete='CASCADE')


class Amenities(ModelSQL, ModelView):
    'Amenities'
    __name__ = 'hotel.amenities'
    _rec_name = 'name'
    name = fields.Char('Name', required=True)
    notes = fields.Text('Notes')
    type = fields.Selection([
            ('', ''),
            ('lingerie', 'Lingerie Room'),
            ('service', 'Service'),
            ], 'Type of Service')
    cleaning_days = fields.One2Many('hotel.cleaning_days', 'amenities',
        'Cleanning Days', states={
            'invisible': Eval('type') != 'lingerie',
        }, depends=['type'])


class RoomClassification(ModelSQL, ModelView):
    'Room Classification'
    __name__ = 'hotel.room.classification'
    name = fields.Char('Name', required=True)


class CleanningDays(ModelSQL, ModelView):
    'Cleanning Days'
    __name__ = 'hotel.cleaning_days'
    amenities = fields.Many2One('hotel.amenities', 'Amenities',
        required=True, ondelete='CASCADE')
    weekday = fields.Selection([
            ('1', 'Monday'),
            ('2', 'Tuesday'),
            ('3', 'Wednesday'),
            ('4', 'Thursday'),
            ('5', 'Friday'),
            ('6', 'Saturday'),
            ('7', 'Sunday'),
        ], 'Weekday', required=True)
    note = fields.Text('Note')


class RoomTemplate(ModelSQL):
    'Room - Template'
    __name__ = 'hotel.room-product.template'
    room = fields.Many2One('hotel.room', 'Room', required=True,
        ondelete='CASCADE')
    template = fields.Many2One('product.template', 'Product Template',
        required=True, ondelete='CASCADE')


class HousekeepingStart(ModelView):
    'Print Housekeeping Service Start'
    __name__ = 'hotel.print_housekeeping.start'
    date = fields.Date('Date', required=True)
    employee = fields.Many2One('company.employee', 'Employee')
    company = fields.Many2One('company.company', 'Company', required=True)

    @staticmethod
    def default_date():
        Date_ = Pool().get('ir.date')
        return Date_.today()

    @staticmethod
    def default_company():
        return Transaction().context.get('company')


class Housekeeping(Wizard):
    'Housekeeping Service'
    __name__ = 'hotel.print_housekeeping'
    start = StateView(
        'hotel.print_housekeeping.start',
        'hotel.print_housekeeping_start_view_form', [
            Button('Cancel', 'end', 'tryton-cancel'),
            Button('Open', 'print_', 'tryton-print', default=True),
        ])
    print_ = StateReport('hotel.print_housekeeping.report')

    def do_print_(self, action):
        company = self.start.company
        data = {
            'date': self.start.date,
            'employee': self.start.employee.id if self.start.employee else None,
            'company': company.id,
        }
        return action, data

    def transition_print_(self):
        return 'end'


class HousekeepingReport(Report):
    __name__ = 'hotel.print_housekeeping.report'

    @classmethod
    def get_context(cls, records, header, data):
        report_context = super().get_context(records, header, data)
        pool = Pool()
        Company = pool.get('company.company')
        Room = pool.get('hotel.room')

        dom = []
        if data['employee']:
            dom.append(('employee.id', '=', data['employee']))

        rooms = Room.search(dom)
        total_guests = []
        for room in rooms:
            total_guests.append(room.guests)
        report_context['records'] = rooms
        report_context['company'] = Company(data['company'])
        report_context['date'] = datetime.now()
        report_context['total_guests'] = sum(total_guests)
        return report_context


class HotelTask(ModelSQL, ModelView):
    "Hotel Task"
    __name__ = "hotel.task"
    name = fields.Char('Name Task', required=True, select=True)
    frecuency = fields.Integer('Frecuency', select=True, help='In days')
    quantity = fields.Integer('Quantity', select=True)


# class HotelHousekeepingTask(ModelView, ModelSQL):
#     'Hotel Housekeeping Task'
#     __name__ = 'hotel.housekeeping.task'
#     room = fields.Many2One('hotel.room', 'Hotel Housekeeping',
#         ondelete='CASCADE', select=True, required=True)
#     task = fields.Many2One('hotel.task', 'Task', select=True, required=True)
#     frecuency = fields.Integer('Frecuency', select=True, help='In days')
#     quantity = fields.Integer('Quantity', select=True)
#
#     @fields.depends('task', 'frecuency')
#     def on_change_task(self, name=None):
#         if self.task:
#             self.frecuency = self.task.frecuency
