from odoo import _, api, exceptions, models


class AccountMove(models.Model):
    _inherit = "account.move"

    @api.depends("company_id", "invoice_filter_type_domain")
    def _compute_suitable_journal_ids(self):
        res = super()._compute_suitable_journal_ids()
        for move in self:
            if move.move_type in {"in_receipt", "out_receipt"}:
                move.suitable_journal_ids = move.suitable_journal_ids.filtered(
                    "receipts"
                )
                continue
            move.suitable_journal_ids = move.suitable_journal_ids.filtered(
                lambda x: not x.receipts
            )
        return res

    def _search_default_receipt_journal(self, journal_types):
        company_id = self.env.context.get("default_company_id", self.env.company.id)
        currency_id = self.env.context.get("default_currency_id")
        domain = [
            ("company_id", "=", company_id),
            ("type", "in", journal_types),
            ("receipts", "=", True),
        ]
        journal = None
        if currency_id:
            journal = self.env["account.journal"].search(
                domain + [("currency_id", "=", currency_id)], limit=1
            )
        if not journal:
            journal = self.env["account.journal"].search(domain, limit=1)
        return journal

    def _search_default_journal(self):
        journal = super()._search_default_journal()
        # We can assume that if move_type is not in receipts, a journal without
        # receipts it's coming because of the Journal constraint
        if self.move_type not in {"in_receipt", "out_receipt"} or journal.receipts:
            return journal
        journal_types = self._get_valid_journal_types()
        return self._search_default_receipt_journal(journal_types) or journal

    def _get_journal_types(self, move_type):
        if move_type in self.get_sale_types(include_receipts=True):
            journal_types = ["sale"]
        elif move_type in self.get_purchase_types(include_receipts=True):
            journal_types = ["purchase"]
        else:
            journal_types = self.env.context.get(
                "default_move_journal_types", ["general"]
            )
        return journal_types

    @api.model
    def _update_receipts_journal(self, vals_list):
        """
        Update `vals_list` in place to set journal_id to the receipt journal
        when move_type is receipt.

        Model defaults are also considered it move_type or journal_id
        are not in a `vals_list`.
        """
        defaults = self.default_get(["journal_id", "move_type"])
        default_journal = defaults.get("journal_id")
        default_move_type = defaults.get("move_type")
        for vals in vals_list:
            move_type = vals.get("move_type", default_move_type)
            if move_type in ("in_receipt", "out_receipt"):
                selected_journal_id = vals.get("journal_id", default_journal)
                selected_journal = self.env["account.journal"].browse(
                    selected_journal_id
                )
                if not selected_journal.receipts:
                    journal_types = self._get_journal_types(move_type)
                    receipt_journal = self._search_default_receipt_journal(
                        journal_types
                    )
                    if receipt_journal:
                        vals["journal_id"] = receipt_journal.id

    @api.model_create_multi
    def create(self, vals_list):
        self._update_receipts_journal(vals_list)
        return super().create(vals_list)

    @api.constrains("move_type", "journal_id")
    def _check_receipts_journal(self):
        """
        Ensure that Receipt Journal is only used in Receipts
        if exists Receipt Journals for its type
        """
        aj_model = self.env["account.journal"]
        receipt_domain = [("receipts", "=", True)]
        has_in_rjournals = aj_model.search([("type", "=", "purchase")] + receipt_domain)
        has_out_rjournals = aj_model.search([("type", "=", "sale")] + receipt_domain)
        for move in self:
            is_rj = move.journal_id.receipts
            if move.move_type not in {"in_receipt", "out_receipt"} and is_rj:
                raise exceptions.ValidationError(
                    _("Receipt Journal is restricted to Receipts")
                )
            elif move.move_type == "in_receipt" and not is_rj and has_in_rjournals:
                raise exceptions.ValidationError(
                    _(
                        "Purchase Receipt must use a Receipt Journal because "
                        "there is already a Receipt Journal for Purchases"
                    )
                )
            elif move.move_type == "out_receipt" and not is_rj and has_out_rjournals:
                raise exceptions.ValidationError(
                    _(
                        "Sale Receipt must use a Receipt Journal because "
                        "there is already a Receipt Journal for Sales"
                    )
                )
