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

from trytond.model import ModelView, Workflow, ModelSQL, fields
from trytond.pool import Pool
from trytond.pyson import Eval, In, If, Get
from trytond.modules.company import CompanyReport
from trytond.wizard import Wizard, StateView, Button, StateTransition
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from trytond.i18n import gettext

STATES = {
    'readonly': Eval('state') == 'checked'
}
STATES_LINE = {
    'readonly': Eval('state') == 'loaded'
}


class ServiceKind(ModelSQL, ModelView):
    'Service Kind'
    __name__ = 'hotel.service.kind'
    name = fields.Char('Name', required=True)
    product = fields.Many2One('product.product', 'Product')
    salable = fields.Boolean('Salable')
    category = fields.Selection([
        ('breakfast', 'Breakfast'),
        ('lunch', 'Lunch'),
        ('dinner', 'Dinner'),
        ('restaurant', 'Restaurant'),
        ('laundry', 'Laundry'),
        ('spa', 'Spa'),
    ], 'Category')


class Service(Workflow, ModelSQL, ModelView):
    'Service'
    __name__ = 'hotel.service'
    _rec_name = 'kind.name'
    number = fields.Char('Number', required=False)
    kind = fields.Many2One('hotel.service.kind', 'Kind', required=True,
        states={
            'readonly': Eval('state').in_(['confirmed', 'checked'])
        })
    service_date = fields.Date('Date', required=True, states={
        'readonly': Eval('state').in_(['confirmed', 'checked'])
    })
    lines = fields.One2Many('hotel.service.line', 'service', 'Lines',
        states={'readonly': Eval('state') != 'open'}
    )
    state = fields.Selection([
        ('draft', 'Draft'),
        ('open', 'Open'),
        ('confirmed', 'Confirmed'),
        ('checked', 'Checked'),
    ], 'State', readonly=True, states=STATES)
    state_string = state.translated('state')
    space = fields.Many2One('analytic_account.space', 'Space', required=False)
    description = fields.Char('Description', states=STATES)
    count_services = fields.Function(fields.Integer('Count Services'),
        'get_count_services')
    company = fields.Many2One('company.company', 'Company', required=True,
        states=STATES, domain=[
            ('id', If(In('company', Eval('context', {})), '=', '!='),
                Get(Eval('context', {}), 'company', 0))
        ])

    @classmethod
    def __setup__(cls):
        super(Service, cls).__setup__()
        cls._order.insert(0, ('service_date', 'DESC'))
        cls._transitions |= set((
            ('draft', 'open'),
            ('open', 'confirmed'),
            ('confirmed', 'open'),
            ('confirmed', 'checked'),
        ))
        cls._buttons.update({
            'draft': {
                'invisible': Eval('state') != 'open',
            },
            'open': {
                'invisible': Eval('state') != 'draft',
            },
            'confirm': {
                'invisible': Eval('state') != 'open',
            },
            'checked': {
                'invisible': Eval('state') != 'confirmed',
            },
        })

    @staticmethod
    def default_state():
        return 'draft'

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

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

    @classmethod
    @ModelView.button
    @Workflow.transition('open')
    def open(cls, records):
        cls.set_number(records)

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

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

    def get_count_services(self, name=None):
        return sum([line.quantity for line in self.lines])

    @classmethod
    def set_number(cls, services):
        """
        Fill the number field with the service sequence
        """
        Config = Pool().get('hotel.configuration')
        config = Config.get_configuration()

        for service in services:
            if service.number or not config.hotel_service_sequence:
                continue
            number = config.hotel_service_sequence.get()
            cls.write([service], {'number': number})


class ServiceLine(Workflow, ModelSQL, ModelView):
    'Service Line'
    __name__ = 'hotel.service.line'
    service = fields.Many2One('hotel.service', 'Service', required=True)
    room = fields.Many2One('hotel.room', 'Room', required=True,
        states=STATES_LINE)
    time_service = fields.Time('Time Service', states=STATES_LINE)
    product = fields.Many2One('product.product', 'Product',
        domain=[
            ('salable', '=', True),
            ('active', '=', True),
        ], required=True)
    quantity = fields.Integer('Quantity', required=True, states=STATES_LINE)
    description = fields.Char('Description', states=STATES_LINE)
    order = fields.Char('Order', states=STATES_LINE)
    folio_line = fields.Many2One('hotel.folio', 'Folio Line',
        states={'readonly': True})
    guest = fields.Char('Guest', states=STATES_LINE)
    charge = fields.Many2One('hotel.folio.charge', 'Operation Line',
        states={'readonly': True})
    folio = fields.Many2One('hotel.folio', 'Folio', domain=[
        ('room', '=', Eval('room')),
        ('registration_state', '=', 'check_in')
    ], depends=['room'])
    state = fields.Selection([
        ('draft', 'Draft'),
        ('loaded', 'Loaded'),
        ('deleted', 'Deleted'),
        ('done', 'Done'),
        ], 'State', readonly=True)
    state_string = state.translated('state')

    @classmethod
    def __setup__(cls):
        super(ServiceLine, cls).__setup__()
        cls._transitions |= set((
            ('draft', 'invoiced'),
            ('draft', 'loaded'),
            ('draft', 'done'),
            ('loaded', 'draft'),
        ))
        cls._buttons.update({
            'draft': {
                'invisible': ~Eval('state').in_(['invoiced']),
            },
            'load': {
                'invisible': Eval('state') != 'draft',
            },
            'done': {
                'invisible': Eval('state') != 'draft',
            },
        })

    @staticmethod
    def default_quantity():
        return 1

    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def default_time_service():
        Company = Pool().get('company.company')
        local_date = Company.convert_timezone(datetime.now())
        return local_date.time()

    @fields.depends('product', 'description')
    def on_change_product(self):
        if self.product:
            self.description = self.product.name

    @classmethod
    @ModelView.button
    @Workflow.transition('loaded')
    def load(cls, records):
        for record in records:
            record.add_product_to_room()

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

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

    def add_product_to_room(self):
        FolioCharge = Pool().get('hotel.folio.charge')
        Folio = Pool().get('hotel.folio')
        if self.folio_line:
            return

        folios = Folio.search([
            ('room', '=', self.room.id),
            ('registration_state', '=', 'check_in'),
        ])
        if not folios:
            raise UserError(
                gettext('hotel.msg_room_not_occupied', s=self.room.name)
            )
        # elif len(folios) > 1:
        #     raise UserError(
        #         gettext('hotel.msg_multiple_rooms_active', s=self.room.name)
        #     )

        # if not folios:
        #     return

        # folio = folios[0]
        folio = self.folio
        invoice_to = None

        if folio.charges_blocked:
            raise UserError(
                gettext('hotel.msg_folio_charges_blocked')
            )
        if folio.booking.channel:
            invoice_to = self.folio.main_guest.id

        analytic_account = None
        if self.service.space:
            analytic_account = self.service.space.analytic_account.id

        record = {
            'folio': self.folio.id,
            'date_service': self.service.service_date,
            'order': self.order,
            'description': self.description,
            'quantity': int(self.quantity),
            'invoice_to': invoice_to,
            'unit_price': self.product.template.list_price,
            'product': self.product.id,
            'to_invoice': True,
            'analytic_account': analytic_account,
            'state': '',
        }
        charge, = FolioCharge.create([record])
        self.write([self], {'charge': charge.id})
        if self.product.template.type == 'goods':
            charge.do_stock_move()


class ServiceReport(CompanyReport):
    __name__ = 'hotel.service'

    # @classmethod
    # def get_context(cls, records, header, data):
    #     report_context = super().get_context(records, header, data)
    #     return report_context


class CreateDailyServicesStart(ModelView):
    'Create Daily Services Start'
    __name__ = 'hotel.daily_services.start'
    kind = fields.Many2One('hotel.service.kind', 'Kind', required=True,
        domain=[
            ('category', 'in', ['breakfast', 'dinner', 'lunch']),
            ('product', '!=', None)
        ])
    date = fields.Date('Date', required=True)
    company = fields.Many2One('company.company', 'Company', required=True)
    description = fields.Char('Description')

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


class CreateDailyServices(Wizard):
    'Create Daily Services'
    __name__ = 'hotel.daily_services'
    start = StateView(
        'hotel.daily_services.start',
        'hotel.create_daily_services_start_view_form', [
            Button('Cancel', 'end', 'tryton-cancel'),
            Button('Accept', 'accept_', 'tryton-ok', default=True),
        ])
    accept_ = StateTransition()

    def transition_accept_(self):
        pool = Pool()
        Service = pool.get('hotel.service')
        Folio = pool.get('hotel.folio')
        kind = self.start.kind

        if kind.category == 'breakfast':
            dom = [
                ('arrival_date', '<', self.start.date),
                ('departure_date', '>=', self.start.date),
                ('registration_state', '=', 'check_in'),
            ]
        elif kind.category == 'lunch':
            dom = [
                ('arrival_date', '<', self.start.date),
                ('departure_date', '>', self.start.date),
                ('registration_state', 'in', ['check_in', 'check_out']),
            ]
        elif kind.category == 'dinner':
            dom = [
                ('arrival_date', '<=', self.start.date),
                ('departure_date', '>', self.start.date),
                ('registration_state', 'in', ['check_in', 'pending']),
            ]
        if not dom:
            return 'end'

        folios = Folio.search(dom)

        services = Service.search_read([
            ('state', '=', 'open'),
            ('kind', '=', kind.id),
        ])
        if services:
            raise UserError(gettext('hotel.msg_services_not_confirmed'))
        lines_to_create = []
        service, = Service.create([{
            'service_date': self.start.date,
            'state': 'draft',
            'kind': kind.id,
        }])
        Service.open([service])
        product = kind.product
        for fol in folios:
            booking = fol.booking
            if booking.plan == 'no_breakfast':
                continue
            if kind == 'lunch' and booking.plan in [
                    'half_american', 'bed_breakfast']:
                continue
            if kind == 'dinner' and booking.plan == 'bed_breakfast':
                continue
            if len(fol.guests) >= fol.pax:
                guests = [guest.name for guest in fol.guests]
            else:
                if not fol.main_guest:
                    raise UserError(gettext(
                        'hotel.msg_missing_guest', number=booking.number))
                guests = [fol.main_guest.name] * fol.pax
            for guest in guests:
                lines_to_create.append({
                    'folio': fol.id,
                    'service': service.id,
                    'room': fol.room.id,
                    'guest': guest,
                    'product': product.id,
                    'quantity': 1,
                })
        if lines_to_create:
            ServiceLine.create(lines_to_create)
        return 'end'
