# Copyright 2017-2018  Alexandre Díaz
# Copyright 2017  Dario Lodeiros
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import datetime
import logging

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError

_logger = logging.getLogger(__name__)


class PmsServiceLine(models.Model):
    _name = "pms.service.line"
    _description = "Service by day"
    _order = "date"
    _rec_name = "service_id"
    _check_pms_properties_auto = True

    service_id = fields.Many2one(
        string="Service Room",
        help="Service identifier",
        required=True,
        copy=False,
        index=True,
        comodel_name="pms.service",
        ondelete="cascade",
    )
    is_board_service = fields.Boolean(
        string="Is Board Service",
        help="Indicates if the service line is part of a board service",
        store=True,
        related="service_id.is_board_service",
    )
    product_id = fields.Many2one(
        string="Product",
        help="Product associated with this service line",
        store=True,
        index=True,
        related="service_id.product_id",
        check_pms_properties=True,
    )
    tax_ids = fields.Many2many(
        string="Taxes",
        help="Taxes applied in the service line",
        readonly="True",
        comodel_name="account.tax",
        related="service_id.tax_ids",
    )
    pms_property_id = fields.Many2one(
        string="Property",
        help="Property to which the service belongs",
        readonly=True,
        store=True,
        index=True,
        comodel_name="pms.property",
        related="service_id.pms_property_id",
        check_pms_properties=True,
    )
    sale_line_ids = fields.Many2many(
        string="Sales Lines",
        readonly=True,
        copy=False,
        comodel_name="folio.sale.line",
        check_pms_properties=True,
    )
    date = fields.Date(
        string="Date",
        help="Sate on which the product is to be consumed",
    )
    day_qty = fields.Integer(
        string="Units",
        help="Amount to be consumed per day",
    )
    price_unit = fields.Float(
        string="Unit Price",
        help="Price per unit of service",
        digits=("Product Price"),
    )
    price_day_subtotal = fields.Monetary(
        string="Subtotal",
        help="Subtotal price without taxes",
        readonly=True,
        store=True,
        compute="_compute_day_amount_service",
    )
    price_day_total = fields.Monetary(
        string="Total",
        help="Total price with taxes",
        readonly=True,
        store=True,
        compute="_compute_day_amount_service",
    )
    price_day_tax = fields.Float(
        string="Taxes Amount",
        help="",
        readonly=True,
        store=True,
        compute="_compute_day_amount_service",
    )
    currency_id = fields.Many2one(
        string="Currency",
        help="The currency used in relation to the service where it's included",
        readonly=True,
        store=True,
        index=True,
        related="service_id.currency_id",
    )
    reservation_id = fields.Many2one(
        string="Reservation",
        help="Room to which the services will be applied",
        readonly=True,
        store=True,
        index=True,
        related="service_id.reservation_id",
        check_pms_properties=True,
    )
    discount = fields.Float(
        string="Discount (%)",
        help="Discount in the price of the service.",
        readonly=False,
        store=True,
        default=0.0,
        digits=("Discount"),
        compute="_compute_discount",
    )
    cancel_discount = fields.Float(
        string="Cancelation Discount",
        help="",
        compute="_compute_cancel_discount",
        readonly=True,
        store=True,
    )
    auto_qty = fields.Boolean(
        string="Qty automated setted",
        help="Show if the day qty was calculated automatically",
        compute="_compute_auto_qty",
        readonly=False,
        store=True,
    )
    default_invoice_to = fields.Many2one(
        string="Invoice to",
        help="""Indicates the contact to which this line will be
        billed by default, if it is not established,
        a guest or the generic contact will be used instead""",
        comodel_name="res.partner",
        store=True,
        index=True,
        related="service_id.default_invoice_to",
        ondelete="restrict",
    )

    @api.depends("day_qty", "discount", "price_unit", "tax_ids")
    def _compute_day_amount_service(self):
        for line in self:
            amount_service = line.price_unit
            if amount_service > 0:
                currency = line.service_id.currency_id
                product = line.product_id
                price = amount_service * (1 - (line.discount or 0.0) * 0.01)
                # REVIEW: line.day_qty is not the total qty (the total is on service_id)
                taxes = line.tax_ids.compute_all(
                    price, currency, line.day_qty, product=product
                )
                line.update(
                    {
                        "price_day_tax": sum(
                            t.get("amount", 0.0) for t in taxes.get("taxes", [])
                        ),
                        "price_day_total": taxes["total_included"],
                        "price_day_subtotal": taxes["total_excluded"],
                    }
                )
            else:
                line.update(
                    {
                        "price_day_tax": 0,
                        "price_day_total": 0,
                        "price_day_subtotal": 0,
                    }
                )

    @api.depends("service_id.reservation_id", "service_id.reservation_id.discount")
    def _compute_discount(self):
        """
        On board service the line discount is always
        equal to reservation line discount
        """
        for record in self:
            if not record.discount:
                record.discount = 0

    # TODO: Refact method and allowed cancelled single days
    @api.depends("service_id.reservation_id.reservation_line_ids.cancel_discount")
    def _compute_cancel_discount(self):
        for line in self:
            line.cancel_discount = 0
            reservation = line.reservation_id
            if reservation.state == "cancel":
                if (
                    reservation.cancelled_reason
                    and reservation.pricelist_id
                    and reservation.pricelist_id.cancelation_rule_id
                    and reservation.reservation_line_ids.mapped("cancel_discount")
                ):
                    if line.is_board_service:
                        consumed_date = (
                            line.date
                            if line.product_id.consumed_on == "before"
                            else line.date + datetime.timedelta(days=-1)
                        )
                        line.cancel_discount = (
                            reservation.reservation_line_ids.filtered(
                                lambda l: l.date == consumed_date
                            ).cancel_discount
                        )
                    elif not line.service_id.is_cancel_penalty:
                        line.cancel_discount = 100
                else:
                    line.cancel_discount = 0
            else:
                line.cancel_discount = 0

    @api.depends("day_qty")
    def _compute_auto_qty(self):
        """
        Set auto_qty = False if the service is no linked to room or
        if the day_qty was set manually
        (See autogeneration of service lines in
        _compute_service_line_ids -pms.service-)
        """
        self.auto_qty = False

    # Constraints and onchanges
    @api.constrains("day_qty")
    def no_free_resources(self):
        for record in self:
            limit = record.product_id.daily_limit
            if limit > 0:
                out_qty = sum(
                    self.env["pms.service.line"]
                    .search(
                        [
                            ("product_id", "=", record.product_id.id),
                            ("date", "=", record.date),
                            ("service_id", "!=", record.service_id.id),
                        ]
                    )
                    .mapped("day_qty")
                )
                if limit < out_qty + record.day_qty:
                    raise ValidationError(
                        _("%s limit exceeded for %s")
                        % (record.service_id.product_id.name, record.date)
                    )

    # Business methods
    def _cancel_discount(self):
        for record in self:
            if record.reservation_id:
                day = record.reservation_id.reservation_line_ids.filtered(
                    lambda d: d.date == record.date
                )
                record.cancel_discount = day.cancel_discount
