# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

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


class AccountAssetTransfer(models.TransientModel):
    _name = "account.asset.transfer"
    _inherit = "analytic.mixin"
    _description = "Transfer Asset"
    _check_company_auto = True

    company_id = fields.Many2one(
        comodel_name="res.company",
        string="Company",
        readonly=True,
        required=True,
    )
    currency_id = fields.Many2one(
        comodel_name="res.currency",
        related="company_id.currency_id",
    )
    transfer_journal_id = fields.Many2one(
        comodel_name="account.journal",
        domain="[('type', '=', 'general'), ('company_id', '=', company_id)]",
        string="Transfer Journal",
        required=True,
        check_company=True,
    )
    date_transfer = fields.Date(
        string="Transfer Date",
        required=True,
        default=fields.Date.today,
        help="Transfer date must be after the asset journal entry",
    )
    note = fields.Text("Notes")
    from_asset_ids = fields.Many2many(
        comodel_name="account.asset",
        string="From Asset(s)",
        readonly=True,
    )
    to_asset_ids = fields.One2many(
        comodel_name="account.asset.transfer.line",
        inverse_name="transfer_id",
        string="To Asset(s)",
    )
    from_asset_value = fields.Monetary(
        string="From Value",
        compute="_compute_asset_value",
    )
    to_asset_value = fields.Monetary(
        string="To Value",
        compute="_compute_asset_value",
    )
    balance = fields.Monetary(
        compute="_compute_asset_value",
    )
    partner_id = fields.Many2one(
        comodel_name="res.partner",
        string="Partner",
    )

    @api.model
    def default_get(self, field_list):
        res = super().default_get(field_list)
        from_asset_ids = self.env.context.get("active_ids")
        assets = self.env["account.asset"].browse(from_asset_ids)
        # Prepare default values
        company = assets.mapped("company_id")
        company.ensure_one()
        journals = assets.mapped("profile_id.transfer_journal_id")
        partners = assets.mapped("partner_id")
        analytics = assets.mapped("analytic_distribution")
        # Combine analytic to dict
        analytic_dict = {}
        for analytic in analytics:
            if analytic:
                for key, value in analytic.items():
                    analytic_dict.setdefault(key, value)
        # Assign values
        res["company_id"] = company.id
        res["partner_id"] = partners[0].id if len(partners) == 1 else False
        res["from_asset_ids"] = [(4, asset_id) for asset_id in assets.ids]
        res["transfer_journal_id"] = journals[:1].id
        res["analytic_distribution"] = analytic_dict
        return res

    @api.depends("from_asset_ids", "to_asset_ids")
    def _compute_asset_value(self):
        for rec in self:
            rec.from_asset_value = sum(rec.from_asset_ids.mapped("purchase_value"))
            rec.to_asset_value = sum(rec.to_asset_ids.mapped("asset_value"))
            balance = rec.from_asset_value - rec.to_asset_value
            rec.balance = balance if balance > 0 else 0

    def _check_amount_transfer(self):
        self.ensure_one()
        if float_compare(self.from_asset_value, self.to_asset_value, 2) != 0:
            raise UserError(
                self.env._("Total values of new assets must equal to source assets")
            )
        if self.to_asset_ids.filtered(lambda asset: asset.asset_value <= 0):
            raise UserError(self.env._("Value of new asset must greater than 0.0"))

    def _get_new_move_transfer(self):
        return {
            "date": self.date_transfer,
            "journal_id": self.transfer_journal_id.id,
            "narration": self.note,
        }

    def transfer(self):
        self.ensure_one()
        self.from_asset_ids._check_can_transfer()
        self._check_amount_transfer()
        # Create transfer journal entry
        move_vals = self._get_new_move_transfer()
        move = self.env["account.move"].create(move_vals)
        move_lines = self._get_transfer_data()
        move.with_context(allow_asset=True).write({"line_ids": move_lines})
        # Post move and create new assets
        move.action_post()
        # Set source assets as removed
        self.from_asset_ids.write(
            {"state": "removed", "date_remove": self.date_transfer}
        )
        # Set all assets is transfer document
        move.line_ids.mapped("asset_id").write({"is_transfer": True})
        return {
            "name": self.env._("Asset Transfer Journal Entry"),
            "view_mode": "list,form",
            "res_model": "account.move",
            "type": "ir.actions.act_window",
            "context": self.env.context,
            "domain": [("id", "=", move.id)],
            "search_view_id": [
                self.env.ref("account.view_account_move_filter").id,
                "search",
            ],
            "views": [
                [self.env.ref("account.view_move_tree").id, "list"],
                [self.env.ref("account.view_move_form").id, "form"],
            ],
        }

    def _get_move_line_from_asset(self, asset):
        # Case asset created with account move
        if asset.account_move_line_ids:
            asset.account_move_line_ids.ensure_one()
            move_line = asset.account_move_line_ids[0]
            return {
                "name": move_line.name,
                "account_id": move_line.account_id.id,
                "analytic_distribution": move_line.analytic_distribution or {},
                "debit": move_line.credit,
                "credit": move_line.debit,
                "partner_id": move_line.partner_id.id,
                "asset_id": move_line.asset_id.id,  # Link to existing asset
            }
        # Case asset created without account moves
        else:
            return {
                "name": asset.name,
                "account_id": asset.profile_id.account_asset_id.id,
                "analytic_distribution": asset.analytic_distribution or {},
                "debit": 0.0,
                "credit": asset.purchase_value or 0.0,
                "partner_id": asset.partner_id.id,
                "asset_id": asset.id,  # Link to existing asset
            }

    def _get_move_line_to_asset(self, to_asset):
        return {
            "name": to_asset.asset_name,
            "account_id": to_asset.asset_profile_id.account_asset_id.id,
            "analytic_distribution": to_asset.analytic_distribution or {},
            "debit": to_asset.asset_value,
            "credit": 0.0,
            "partner_id": to_asset.partner_id.id,
            "asset_profile_id": to_asset.asset_profile_id.id,  # To create new asset
            "price_subtotal": to_asset.asset_value,
        }

    def _get_transfer_data(self):
        # Create lines from assets
        move_lines = [
            Command.create(self._get_move_line_from_asset(from_asset))
            for from_asset in self.from_asset_ids
        ]
        # Create lines for new assets
        move_lines += [
            Command.create(self._get_move_line_to_asset(to_asset))
            for to_asset in self.to_asset_ids
        ]
        return move_lines

    def expand_to_asset_ids(self):
        self.ensure_one()
        lines = self.to_asset_ids.filtered(
            lambda asset: asset.asset_profile_id and asset.quantity
        )
        for line in lines:
            line._expand_asset_line()
        result = self.env["ir.actions.act_window"]._for_xml_id(
            "account_asset_transfer.action_account_asset_transfer"
        )
        result.update({"res_id": self.id})
        return result


class AccountAssetTransferLine(models.TransientModel):
    _name = "account.asset.transfer.line"
    _inherit = "analytic.mixin"
    _description = "Transfer To Asset"

    transfer_id = fields.Many2one(
        comodel_name="account.asset.transfer",
        index=True,
    )
    asset_profile_id = fields.Many2one(
        comodel_name="account.asset.profile",
        string="Asset Profile",
        required=True,
    )
    asset_name = fields.Char(required=True)
    quantity = fields.Float(
        required=True,
        default=0.0,
    )
    price_unit = fields.Float(
        string="Unit Price",
        required=True,
        default=0.0,
    )
    asset_value = fields.Float(
        compute="_compute_asset_value",
        default=0.0,
        store=True,
        required=True,
    )
    partner_id = fields.Many2one(
        comodel_name="res.partner",
        string="Partner",
    )

    @api.depends("quantity", "price_unit")
    def _compute_asset_value(self):
        for rec in self:
            rec.asset_value = rec.quantity * rec.price_unit

    def _expand_asset_line(self):
        self.ensure_one()
        profile = self.asset_profile_id
        if profile and self.quantity > 1.0 and profile.asset_product_item:
            line = self
            qty = self.quantity
            name = self.asset_name
            self.update({"quantity": 1, "asset_name": f"{name} {1}"})
            for i in range(1, int(qty)):
                line.copy({"asset_name": f"{name} {i + 1}"})
