"""Job partner commission ledger — relocated from the now-merged
``partner_commission_management`` module and extended with V9 lifecycle
fields (transaction_type / state / source / service snapshot) so the
same model carries both commissions (auto-created on job publish) and
affiliate payout requests.

Existing rows from the legacy module are backfilled in
``migrations/18.0.2.4.0/post-migrate.py``: ``transaction_type='commission'``
and ``state='completed'`` (they were already final).
"""

from markupsafe import Markup

from odoo import Command, _, api, fields, models
from odoo.exceptions import UserError
from odoo.tools import format_datetime


class JobPartnerCommission(models.Model):
    _name = "job.partner.commission"
    _inherit = ["mail.thread", "portal.mixin"]
    _description = "Job Partner Commission"

    # ------------------------------------------------------------------
    # Legacy fields (kept as-is for backward compatibility)
    # ------------------------------------------------------------------
    # job_id is now optional: a payout request is partner-only.
    job_id = fields.Many2one(
        comodel_name="hr.job", ondelete="cascade", tracking=True,
    )
    partner_id = fields.Many2one(
        comodel_name="res.partner", required=True, tracking=True,
    )
    commission_percentage = fields.Float(
        string="Commission Percentage (%)",
        default=100.0,
        help="Percentage of the total commission this partner will receive.",
        tracking=True,
    )
    commission_amount = fields.Monetary(
        string="Commission Amount",
        compute="_compute_commission_amount",
        store=True,
        readonly=False,  # payout rows write directly; commission rows compute
        currency_field="currency_id",
    )
    currency_id = fields.Many2one(
        comodel_name="res.currency",
        compute="_compute_currency_id", store=True,
    )
    bill_invoice_id = fields.Many2one(
        comodel_name="account.move", tracking=True, readonly=True,
    )
    is_invoiced = fields.Boolean(
        string="Has Invoiced?",
        compute="_compute_invoice", store=True,
    )
    is_paid = fields.Boolean(
        string="Is Paid?",
        compute="_compute_invoice", store=True,
    )

    # ------------------------------------------------------------------
    # V9-parity fields (added by the merge)
    # ------------------------------------------------------------------
    transaction_type = fields.Selection(
        [("commission", "Commission"), ("payout", "Payout")],
        string="Type", default="commission", required=True, tracking=True,
        help="Commission = credit to the affiliate. "
             "Payout = withdrawal request from the affiliate.",
    )
    state = fields.Selection(
        [("pending", "Pending"),
         ("completed", "Completed"),
         ("canceled", "Canceled")],
        string="Status", default="pending", required=True, tracking=True,
    )
    widget_config_id = fields.Many2one(
        comodel_name="affiliate.widget.config",
        string="Widget Source",
        ondelete="set null",
        help="The widget config that referred this job purchase. "
             "Empty for manually-added or payout rows.",
    )
    referrer_source = fields.Selection(
        [("kj", "Direct (Karriere-Jura)"),
         ("overlay", "Widget — Overlay"),
         ("inline", "Widget — Inline"),
         ("grid", "Widget — Grid"),
         ("box", "Widget — Box")],
        string="Referrer Source", default="kj",
    )
    service_type = fields.Selection(
        [("job_offer_standard", "Job Offer — Standard"),
         ("job_offer_premium", "Job Offer — Premium"),
         ("company_profile_premium", "Company Profile — Premium")],
        string="Service Type",
    )
    service_price = fields.Monetary(
        string="Service Price",
        currency_field="currency_id",
        help="Snapshot of the job's base amount at the moment the "
             "commission was created.",
    )
    service_title = fields.Char(
        string="Service Title",
        help="Snapshot of the job title at the moment the commission "
             "was created — survives if the job is later renamed/deleted.",
    )
    completed_date = fields.Datetime(string="Completed On", readonly=True)
    canceled_date = fields.Datetime(string="Canceled On", readonly=True)
    affiliate_note = fields.Text(
        string="Affiliate Note",
        help="Free-text note attached by the affiliate when submitting a "
             "payout request.",
    )

    _sql_constraints = [
        ("job_required_for_commission",
         "CHECK(job_id IS NOT NULL OR transaction_type = 'payout')",
         "A commission row must be linked to a job. Only payout rows can "
         "omit the job."),
    ]

    # ------------------------------------------------------------------
    # Compute
    # ------------------------------------------------------------------
    @api.depends("job_id", "job_id.name", "partner_id", "partner_id.name",
                 "transaction_type")
    def _compute_display_name(self):
        for rec in self:
            if rec.transaction_type == "payout":
                rec.display_name = _("Payout — %s", rec.partner_id.name or "?")
            else:
                job = rec.job_id.name or "?"
                partner = rec.partner_id.name or "?"
                rec.display_name = f"{job} - {partner}"

    @api.depends("job_id.currency_id", "transaction_type")
    def _compute_currency_id(self):
        company_ccy = self.env.company.currency_id
        for rec in self:
            rec.currency_id = (
                rec.job_id.currency_id if rec.job_id else company_ccy
            )

    @api.depends("commission_percentage", "job_id.base_amount",
                 "job_id.tracker_factor", "transaction_type")
    def _compute_commission_amount(self):
        for rec in self:
            if rec.transaction_type == "payout":
                # Payouts store the requested amount directly — leave it.
                if not rec.commission_amount:
                    rec.commission_amount = 0.0
                continue
            base = rec.job_id.base_amount or 0.0
            factor = rec.job_id.tracker_factor or 1.0
            rec.commission_amount = base * factor * rec.commission_percentage / 100.0

    @api.depends("bill_invoice_id", "bill_invoice_id.payment_state",
                 "bill_invoice_id.matched_payment_ids",
                 "bill_invoice_id.matched_payment_ids.state")
    def _compute_invoice(self):
        for rec in self:
            rec.is_invoiced = bool(rec.bill_invoice_id)
            payments = rec.bill_invoice_id.matched_payment_ids.filtered_domain(
                [("state", "!=", "canceled")]
            )
            rec.is_paid = bool(payments) and all(p.state == "paid" for p in payments)

    # ------------------------------------------------------------------
    # Constraints
    # ------------------------------------------------------------------
    @api.constrains("job_id", "transaction_type")
    def _check_job_base_amount(self):
        for rec in self:
            if rec.transaction_type != "commission":
                continue
            if rec.job_id and rec.job_id.base_amount <= 0:
                raise UserError(_(
                    "Set Base Amount in Job Position %s before creating a "
                    "commission row.", rec.job_id.name,
                ))

    # ------------------------------------------------------------------
    # CRUD
    # ------------------------------------------------------------------
    @api.model_create_multi
    def create(self, vals_list):
        records = super().create(vals_list)
        user_tz = self.env.user.tz or "UTC"
        for rec in records:
            if rec.transaction_type != "commission":
                continue
            job, partner = rec.job_id, rec.partner_id
            if not (job and partner):
                continue
            when = format_datetime(self.env, rec.create_date, tz=user_tz)
            msg = Markup(f"""
                <p>New commission created:</p>
                <ul>
                    <li>Job: <a href="/odoo/recruitment/{job.id}">{job.name}</a></li>
                    <li>Commission Percentage: {rec.commission_percentage:.2f}%</li>
                    <li>Commission Amount: {rec.currency_id.format(rec.commission_amount)}</li>
                    <li>Status: {dict(self._fields['state'].selection).get(rec.state)}</li>
                    <li>Created at: {when}</li>
                </ul>
            """)
            partner.message_post(
                body=msg,
                subject="New Commission Created",
                subtype_xmlid="mail.mt_note",
            )
        return records

    def _get_portal_return_action(self):
        return self.env.ref(
            "kj_affiliate_widget.action_job_partner_commission"
        )

    # ------------------------------------------------------------------
    # State transitions (V9: btn_complete / btn_cancel / btn_reopen)
    # ------------------------------------------------------------------
    def action_complete(self):
        for rec in self:
            if rec.state == "completed":
                continue
            rec.write({
                "state": "completed",
                "completed_date": fields.Datetime.now(),
                "canceled_date": False,
            })
            if rec.transaction_type == "payout":
                rec.message_post(
                    body=_("Payout approved."),
                    subtype_xmlid="mail.mt_note",
                )

    def action_cancel(self):
        for rec in self:
            if rec.state == "canceled":
                continue
            rec.write({
                "state": "canceled",
                "canceled_date": fields.Datetime.now(),
            })

    def action_reopen(self):
        for rec in self:
            rec.write({
                "state": "pending",
                "completed_date": False,
                "canceled_date": False,
            })

    def action_approve_payout(self):
        """Wizard-less admin approval for a payout row: flip to completed
        and generate the vendor bill. V9 equivalent: btn_register_payout.
        """
        for rec in self:
            if rec.transaction_type != "payout":
                raise UserError(_(
                    "Only payout rows can be approved as payouts."
                ))
            if rec.state != "pending":
                raise UserError(_("This payout is not pending."))
            rec.action_complete()
            rec.generate_bill_invoice()

    # ------------------------------------------------------------------
    # Invoicing — moved verbatim from the legacy module, scoped to
    # commission rows (the monthly cron must not bill payouts twice).
    # ------------------------------------------------------------------
    @api.model
    def calculate_monthly_commissions(self):
        """Scheduler: bill every COMPLETED commission that has no invoice yet."""
        rows = self.search([
            ("transaction_type", "=", "commission"),
            ("state", "=", "completed"),
            ("bill_invoice_id", "=", False),
            ("job_id.active", "=", True),
        ])
        rows.generate_bill_invoice()

    def generate_bill_invoice(self):
        product = self.env.ref("kj_affiliate_widget.product_commission_data")
        for rec in self:
            if rec.bill_invoice_id:
                continue
            label = (
                f"Commission for Job {rec.service_title or rec.job_id.name}"
                if rec.transaction_type == "commission"
                else f"Payout for affiliate {rec.partner_id.name}"
            )
            move = self.env["account.move"].create({
                "move_type": "in_invoice",
                "partner_id": rec.partner_id.id,
                "invoice_date": fields.Date.today(),
                "invoice_line_ids": [Command.create({
                    "product_id": product.id,
                    "name": label,
                    "quantity": 1,
                    "price_unit": rec.commission_amount,
                })],
            })
            move.action_post()
            rec.bill_invoice_id = move
            rec.partner_id.message_post(
                body=Markup(f"""
                    <p>A vendor bill was created:</p>
                    <ul>
                      <li>{label}</li>
                      <li>Amount: {rec.currency_id.format(rec.commission_amount)}</li>
                      <li>Bill: <a href='#' data-oe-model='account.move' data-oe-id='{move.id}'>{move.name}</a></li>
                    </ul>
                """),
                subject=_("A Commission Bill Created"),
                subtype_xmlid="mail.mt_note",
            )

    def action_generate_bill_invoice(self):
        self.generate_bill_invoice()
