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

REIMB_STATES = [
    ("submitted", "Submitted"),
    ("supervisor_approve", "Supervisor Reimburse Approve"),
    ("supervisor_reject", "Supervisor Reimburse Reject"),
    ("management_approve", "Management Approve"),
    ("management_reject", "Management Reject"),
    ("complete", "Complete"),
]

REIMBURSEMENT_TYPES = [
    ("fuel", "Fuel"),
    ("other", "Other"),
]


class XenoOrrReimbursement(models.Model):
    """One reimbursement ATTEMPT on an Outside Request. Section 7 requires
    every past attempt to stay visible (Current vs Past tabs, "Attempt 1",
    "Attempt 2", ...) rather than editing in place, so a Supervisor
    rejection creates a brand-new row via action_resend() instead of
    reopening this one -- is_current is computed by comparing
    attempt_number to the highest attempt_number among siblings, so it's
    always correct with no separate flag to remember to flip."""

    _name = "xeno.orr.reimbursement"
    _description = "Outside Request Reimbursement"
    _inherit = ["mail.thread"]
    _order = "request_id, attempt_number desc"

    request_id = fields.Many2one(
        "xeno.orr.request", required=True, ondelete="cascade", index=True, tracking=True)
    employee_id = fields.Many2one(related="request_id.employee_id", store=True)
    attempt_number = fields.Integer(default=1, readonly=True)
    is_current = fields.Boolean(compute="_compute_is_current", store=True)

    reimbursement_type = fields.Selection(REIMBURSEMENT_TYPES, required=True, tracking=True)

    fuel_distance_km = fields.Float(readonly=True, help="Trip distance snapshotted at submission time.")
    fuel_rate = fields.Float(readonly=True, help="Rate per km snapshotted at submission time.")
    fuel_amount = fields.Float(compute="_compute_fuel_amount", store=True)

    line_ids = fields.One2many("xeno.orr.reimbursement.line", "reimbursement_id")
    total_amount = fields.Float(compute="_compute_total_amount", store=True, tracking=True)

    state = fields.Selection(REIMB_STATES, default="submitted", required=True, tracking=True, copy=False)
    reject_reason = fields.Char(copy=False)
    category = fields.Char(help="Free-text category assigned by Finance once approved.")

    ask_back_ids = fields.One2many("xeno.orr.ask.back", "reimbursement_id")
    ask_back_total = fields.Float(compute="_compute_ask_back_total")

    supervisor_decided_by = fields.Many2one("res.users", readonly=True, copy=False)
    supervisor_decided_at = fields.Datetime(readonly=True, copy=False)
    management_decided_by = fields.Many2one("res.users", readonly=True, copy=False)
    management_decided_at = fields.Datetime(readonly=True, copy=False)
    completed_by = fields.Many2one("res.users", readonly=True, copy=False)
    completed_at = fields.Datetime(readonly=True, copy=False)

    can_supervisor_decide = fields.Boolean(compute="_compute_can_decide")
    can_management_decide = fields.Boolean(compute="_compute_can_decide")
    can_finance_decide = fields.Boolean(compute="_compute_can_decide")

    @api.depends_context("uid")
    @api.depends("request_id.supervisor_id")
    def _compute_can_decide(self):
        is_hr = self._xeno_is_hr()
        is_mgmt = self._xeno_is_management()
        is_finance = self._xeno_is_finance()
        for rec in self:
            rec.can_supervisor_decide = is_hr or rec._xeno_is_supervisor()
            rec.can_management_decide = is_hr or is_mgmt
            rec.can_finance_decide = is_hr or is_finance

    @api.depends("request_id.reimbursement_ids.attempt_number", "attempt_number")
    def _compute_is_current(self):
        for rec in self:
            siblings = rec.request_id.reimbursement_ids
            rec.is_current = rec.attempt_number == max(siblings.mapped("attempt_number") or [0])

    @api.depends("fuel_distance_km", "fuel_rate")
    def _compute_fuel_amount(self):
        for rec in self:
            rec.fuel_amount = (rec.fuel_distance_km or 0.0) * (rec.fuel_rate or 0.0)

    @api.depends("reimbursement_type", "fuel_amount", "line_ids.amount")
    def _compute_total_amount(self):
        for rec in self:
            if rec.reimbursement_type == "fuel":
                rec.total_amount = rec.fuel_amount
            else:
                rec.total_amount = sum(rec.line_ids.mapped("amount"))

    @api.depends("ask_back_ids.amount")
    def _compute_ask_back_total(self):
        for rec in self:
            rec.ask_back_total = sum(rec.ask_back_ids.mapped("amount"))

    @api.constrains("reimbursement_type", "line_ids")
    def _check_other_has_lines(self):
        for rec in self:
            if rec.reimbursement_type == "other" and not rec.line_ids:
                raise ValidationError(_(
                    "Add at least one Place/Description/Amount line for an "
                    "Other reimbursement before sending."))

    def _xeno_is_supervisor(self):
        self.ensure_one()
        return self.env.user.employee_id == self.request_id.supervisor_id

    def _xeno_is_management(self):
        return self.env.user.has_group("xeno_orr.group_orr_management")

    def _xeno_is_finance(self):
        return self.env.user.has_group("xeno_orr.group_orr_finance")

    def _xeno_is_hr(self):
        return self.env.is_superuser() or self.env.user.has_group("hr_holidays.group_hr_holidays_user")

    # ------------------------------------------------------------ actions

    def action_supervisor_approve(self):
        for rec in self:
            if rec.state != "submitted":
                raise UserError(_("Only a Submitted reimbursement can be approved by the Supervisor."))
            if not (rec._xeno_is_supervisor() or rec._xeno_is_hr()):
                raise UserError(_("Only the request's Supervisor can approve this reimbursement."))
            rec.write({
                "state": "supervisor_approve",
                "supervisor_decided_by": self.env.user.id,
                "supervisor_decided_at": fields.Datetime.now(),
            })
            rec.message_post(body=_("Reimbursement approved by Supervisor: %s", self.env.user.name))
            rec._xeno_notify_reimbursement_supervisor_approved()

    def action_supervisor_reject(self, reason=False):
        for rec in self:
            if rec.state != "submitted":
                raise UserError(_("Only a Submitted reimbursement can be rejected by the Supervisor."))
            if not (rec._xeno_is_supervisor() or rec._xeno_is_hr()):
                raise UserError(_("Only the request's Supervisor can reject this reimbursement."))
            if not reason:
                raise UserError(_("A reason is required to reject a reimbursement."))
            rec.write({
                "state": "supervisor_reject",
                "reject_reason": reason,
                "supervisor_decided_by": self.env.user.id,
                "supervisor_decided_at": fields.Datetime.now(),
            })
            rec.message_post(body=_("Reimbursement rejected by Supervisor: %s", self.env.user.name))
            rec._xeno_notify_reimbursement_supervisor_rejected()

    def action_resend(self, vals):
        """Creates the NEXT attempt on the same request -- see the class
        docstring for why this is a new record rather than editing this
        one. `vals` is whatever action_ask_reimbursement() itself accepts
        (reimbursement_type + fuel snapshot or line_ids)."""
        self.ensure_one()
        if self.state != "supervisor_reject":
            raise UserError(_("Only a Supervisor-rejected reimbursement can be resent."))
        if self.request_id.env.user.employee_id != self.request_id.employee_id and not self._xeno_is_hr():
            raise UserError(_("Only the requesting employee can resend a reimbursement."))
        return self.request_id._xeno_create_reimbursement(vals, attempt_number=self.attempt_number + 1)

    def action_management_approve(self):
        for rec in self:
            if rec.state != "supervisor_approve":
                raise UserError(_("Only a Supervisor-approved reimbursement can be approved by Management."))
            if not (rec._xeno_is_management() or rec._xeno_is_hr()):
                raise UserError(_("Only Management can approve this reimbursement."))
            rec.write({
                "state": "management_approve",
                "management_decided_by": self.env.user.id,
                "management_decided_at": fields.Datetime.now(),
            })
            rec.message_post(body=_("Reimbursement approved by Management: %s", self.env.user.name))
            rec._xeno_notify_reimbursement_management_decided(approved=True)

    def action_management_reject(self, reason=False):
        for rec in self:
            if rec.state != "supervisor_approve":
                raise UserError(_("Only a Supervisor-approved reimbursement can be rejected by Management."))
            if not (rec._xeno_is_management() or rec._xeno_is_hr()):
                raise UserError(_("Only Management can reject this reimbursement."))
            if not reason:
                raise UserError(_("A reason is required to reject a reimbursement."))
            rec.write({
                "state": "management_reject",
                "reject_reason": reason,
                "management_decided_by": self.env.user.id,
                "management_decided_at": fields.Datetime.now(),
            })
            rec.message_post(body=_("Reimbursement rejected by Management: %s", self.env.user.name))
            rec._xeno_notify_reimbursement_management_decided(approved=False)

    def action_finance_complete(self):
        for rec in self:
            if rec.state != "management_approve":
                raise UserError(_("Only a Management-approved reimbursement can be marked Complete."))
            if not (rec._xeno_is_finance() or rec._xeno_is_hr()):
                raise UserError(_("Only Finance can complete this reimbursement."))
            rec.write({
                "state": "complete",
                "completed_by": self.env.user.id,
                "completed_at": fields.Datetime.now(),
            })
            rec.message_post(body=_("Marked Complete by Finance: %s", self.env.user.name))
            rec._xeno_notify_reimbursement_completed()

    def action_set_category(self, category):
        for rec in self:
            if not (rec._xeno_is_finance() or rec._xeno_is_hr()):
                raise UserError(_("Only Finance can set a reimbursement's category."))
            if not category:
                raise UserError(_("Enter a category before adding it."))
            rec.category = category

    def action_ask_back(self, amount, reason):
        for rec in self:
            if not (rec._xeno_is_management() or rec._xeno_is_hr()):
                raise UserError(_("Only Management can ask back a reimbursed amount."))
            if rec.state not in ("management_approve", "complete"):
                raise UserError(_("Ask Back only applies to an approved/completed reimbursement."))
            if not amount or amount <= 0:
                raise UserError(_("Enter an amount greater than zero."))
            if not reason:
                raise UserError(_("A reason is required for Ask Back."))
            self.env["xeno.orr.ask.back"].create({
                "reimbursement_id": rec.id,
                "amount": amount,
                "reason": reason,
                "requested_by": self.env.user.id,
            })
            rec.message_post(body=_("Ask Back requested by %(user)s: %(amount)s (%(reason)s)",
                                     user=self.env.user.name, amount=amount, reason=reason))
            rec._xeno_notify_ask_back(amount, reason)
