# Copyright 2023 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, fields, models


class CharId(fields.Id):
    type = "string"
    column_type = ("varchar", fields.pg_varchar())


class AccountAccountReconcile(models.Model):
    _name = "account.account.reconcile"
    _description = "Account Account Reconcile"
    _inherit = "account.reconcile.abstract"
    _auto = False

    reconcile_data_info = fields.Serialized(inverse="_inverse_reconcile_data_info")

    partner_id = fields.Many2one("res.partner", readonly=True)
    account_id = fields.Many2one("account.account", readonly=True)
    name = fields.Char(readonly=True)
    is_reconciled = fields.Boolean(readonly=True)

    @property
    def _table_query(self):
        return "%s %s %s %s %s" % (
            self._select(),
            self._from(),
            self._where(),
            self._groupby(),
            self._having(),
        )

    def _select(self):
        account_account_name_field = (
            self.env["ir.model.fields"]
            .sudo()
            .search([("model", "=", "account.account"), ("name", "=", "name")])
        )
        account_name = (
            f"a.name ->> '{self.env.user.lang}'"
            if account_account_name_field.translate
            else "a.name"
        )
        return f"""
            SELECT
                min(aml.id) as id,
                MAX({account_name}) as name,
                CASE
                    WHEN a.account_type in ('asset_receivable', 'liability_payable')
                        THEN aml.partner_id
                    ELSE NULL
                    END as partner_id,
                a.id as account_id,
                FALSE as is_reconciled,
                aml.currency_id as currency_id,
                a.company_id,
                false as foreign_currency_id
        """

    def _from(self):
        return """
            FROM
                account_account a
                INNER JOIN account_move_line aml ON aml.account_id = a.id
                INNER JOIN account_move am ON am.id = aml.move_id
            """

    def _where(self):
        return """
            WHERE a.reconcile
                AND am.state = 'posted'
                AND aml.amount_residual != 0
        """

    def _groupby(self):
        return """
            GROUP BY
                a.id,
                CASE
                    WHEN a.account_type in ('asset_receivable', 'liability_payable')
                        THEN aml.partner_id
                    ELSE NULL
                END,
                aml.currency_id,
                a.company_id
        """

    def _having(self):
        return """
            HAVING
                SUM(aml.debit) > 0
                AND SUM(aml.credit) > 0
        """

    def _compute_reconcile_data_info(self):
        data_obj = self.env["account.account.reconcile.data"]
        for record in self:
            if self.env.context.get("default_account_move_lines"):
                data = {
                    "data": [],
                    "counterparts": self.env.context.get("default_account_move_lines"),
                }
                record.reconcile_data_info = self._recompute_data(data)
                continue
            data_record = data_obj.search(
                [("user_id", "=", self.env.user.id), ("reconcile_id", "=", record.id)]
            )
            if data_record:
                record.reconcile_data_info = data_record.data
            else:
                record.reconcile_data_info = {"data": [], "counterparts": []}

    def _inverse_reconcile_data_info(self):
        data_obj = self.env["account.account.reconcile.data"]
        for record in self:
            data_record = data_obj.search(
                [("user_id", "=", self.env.user.id), ("reconcile_id", "=", record.id)]
            )
            if data_record:
                data_record.data = record.reconcile_data_info
            else:
                data_obj.create(
                    {
                        "reconcile_id": record.id,
                        "user_id": self.env.user.id,
                        "data": record.reconcile_data_info,
                    }
                )

    @api.onchange("add_account_move_line_id")
    def _onchange_add_account_move_line(self):
        if self.add_account_move_line_id:
            data = self.reconcile_data_info
            if self.add_account_move_line_id.id not in data["counterparts"]:
                data["counterparts"].append(self.add_account_move_line_id.id)
            else:
                del data["counterparts"][
                    data["counterparts"].index(self.add_account_move_line_id.id)
                ]
            self.reconcile_data_info = self._recompute_data(data)
            self.add_account_move_line_id = False

    @api.onchange("manual_reference", "manual_delete")
    def _onchange_manual_reconcile_reference(self):
        self.ensure_one()
        data = self.reconcile_data_info
        counterparts = []
        for line in data["data"]:
            if line["reference"] == self.manual_reference:
                if self.manual_delete:
                    continue
            counterparts.append(line["id"])
        data["counterparts"] = counterparts
        self.reconcile_data_info = self._recompute_data(data)
        self.manual_delete = False
        self.manual_reference = False

    def _recompute_data(self, data):
        new_data = {"data": [], "counterparts": data["counterparts"]}
        counterparts = data["counterparts"]
        amount = 0.0
        for line_id in counterparts:
            max_amount = amount if line_id == counterparts[-1] else 0
            lines = self._get_reconcile_line(
                self.env["account.move.line"].browse(line_id),
                "other",
                is_counterpart=True,
                max_amount=max_amount,
                move=True,
            )
            new_data["data"] += lines
            amount += sum(line["amount"] for line in lines)
        return new_data

    def clean_reconcile(self):
        self.ensure_one()
        self.reconcile_data_info = {"data": [], "counterparts": []}

    def reconcile(self):
        lines = self.env["account.move.line"].browse(
            self.reconcile_data_info["counterparts"]
        )
        lines.reconcile()
        data_record = self.env["account.account.reconcile.data"].search(
            [("user_id", "=", self.env.user.id), ("reconcile_id", "=", self.id)]
        )
        data_record.unlink()


class AccountAccountReconcileData(models.TransientModel):
    _name = "account.account.reconcile.data"
    _description = "Reconcile data model to store user info"

    user_id = fields.Many2one("res.users", required=True)
    reconcile_id = fields.Integer(required=True)
    data = fields.Serialized()
