# Copyright 2024 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
from odoo.exceptions import UserError, ValidationError


class DmsFieldTemplate(models.Model):
    _name = "dms.field.template"
    _inherit = "dms.field.mixin"
    _description = "Dms Field Template"

    name = fields.Char(required=True)
    company_id = fields.Many2one(
        comodel_name="res.company",
        string="Company",
        store=True,
        index=True,
    )
    storage_id = fields.Many2one(
        comodel_name="dms.storage",
        domain=[("save_type", "!=", "attachment")],
        string="Storage",
    )
    parent_directory_id = fields.Many2one(
        comodel_name="dms.directory",
        domain="[('storage_id', '=', storage_id)]",
        string="Parent directory",
    )
    model_id = fields.Many2one(
        comodel_name="ir.model",
        string="Model",
        domain=[("transient", "=", False), ("model", "!=", "dms.field.template")],
        index=True,
    )
    model = fields.Char(
        compute="_compute_model", compute_sudo=True, store=True, string="Model name"
    )
    group_ids = fields.Many2many(
        comodel_name="dms.access.group",
        string="Groups",
    )
    user_field_id = fields.Many2one(
        comodel_name="ir.model.fields",
        domain="[('model_id', '=', model_id),('relation', '=', 'res.users')]",
        string="User field",
    )
    directory_format_name = fields.Char(
        string="Directory format name",
        default="{{object.display_name}}",
        help="""You can set expressions to be used for the directory name,
        e.g.: {{object.name}}""",
    )

    @api.depends("model_id")
    def _compute_model(self):
        for item in self:
            item.model = item.model_id.model

    def _get_template_from_model(self, model):
        return self.search([("model", "=", model)], limit=1)

    @api.model_create_multi
    def create(self, vals_list):
        """Create dms directory automatically in the creation in install mode."""
        result = super().create(vals_list)
        if self.env.context.get("install_mode"):
            for item in result:
                item_ctx = item.with_context(res_model=item._name, res_id=item.id)
                item_ctx.create_dms_directory()
        return result

    @api.model
    def create_dms_directory(self):
        """According to the model, create the directory linked to that record
        and the subdirectories."""
        res_model = self.env.context.get("res_model")
        res_id = self.env.context.get("res_id")
        record = self.env[res_model].browse(res_id)
        directory_model = self.env["dms.directory"].sudo()
        if res_model == "dms.field.template":
            return directory_model.create(
                {
                    "storage_id": record.storage_id.id,
                    "res_id": record.id,
                    "res_model": record._name,
                    "is_root_directory": True,
                    "name": record.display_name,
                    "group_ids": record.group_ids.ids,
                }
            )
        template = self._get_template_from_model(res_model).sudo()
        if not template:
            raise ValidationError(_("There is no template linked to this model"))
        total_directories = directory_model.search_count(
            [
                ("parent_id", "=", self.parent_directory_id.id),
                ("res_model", "=", res_model),
                ("res_id", "=", res_id),
            ]
        )
        if total_directories > 0:
            raise ValidationError(_("There is already a linked directory created."))
        # Create root directory + files
        dms_directory_ids = template.dms_directory_ids
        new_directory = directory_model.create(
            template._prepare_directory_vals(dms_directory_ids, record)
        )
        self._copy_files_from_directory(dms_directory_ids, new_directory)
        # Create child directories
        self._create_child_directories(new_directory, dms_directory_ids)
        return new_directory

    def _copy_files_from_directory(self, directory, new_directory):
        for file in directory.file_ids:
            file.copy({"directory_id": new_directory.id})

    def _prepare_autogenerated_group(self, record):
        group_name = _("Autogenerated group from %(model)s (%(name)s) #%(id)s") % {
            "model": record._description,
            "name": record.display_name,
            "id": record.id,
        }
        vals = {
            "name": group_name,
            # We need to set all the permissions so that the user can manage their
            # documents (directories and files)
            "perm_create": True,
            "perm_write": True,
            "perm_unlink": True,
            "dms_field_ref": "%s,%s" % (record._name, record.id),
            "explicit_user_ids": [(5, 0)],
        }
        # Apply sudo() because the user may not have permissions to access
        # ir.model.fields.
        user_field = self.sudo().user_field_id
        if user_field:
            user = record[user_field.name]
            if user:
                vals["explicit_user_ids"] += [(4, user.id)]
        return vals

    def _get_autogenerated_group(self, record):
        """Get the existing auto-generated group or create a new one.
        The permissions of the auto-generated group should be changed
        to make sure you have the correct data.
        """
        group_model = self.env["dms.access.group"]
        group_ref = group_model._get_item_from_dms_field_ref(record)
        if group_ref:
            group_ref.write(self._prepare_autogenerated_group(record))
            return group_ref
        # Create the autogenerated group linked to the record
        return group_model.create(self._prepare_autogenerated_group(record))

    def _create_child_directories(self, parent, directory):
        # Create child directories (all leves) + files
        directory_model = self.env["dms.directory"].sudo()
        for child_directory in directory.child_directory_ids:
            child = directory_model.create(
                {
                    "name": child_directory.name,
                    "is_root_directory": False,
                    "parent_id": parent.id,
                }
            )
            self._copy_files_from_directory(child_directory, child)
            self._create_child_directories(child, child_directory)

    def _prepare_directory_vals(self, directory, record):
        # Groups of the new directory will be those of the template + auto-generate
        groups = directory.group_ids
        groups += self._get_autogenerated_group(record)
        directory_name = self.env["mail.render.mixin"]._render_template(
            self.directory_format_name,
            record._name,
            record.ids,
            engine="inline_template",
        )[record.id]
        vals = {
            "storage_id": directory.storage_id.id,
            "res_id": record.id,
            "res_model": record._name,
            "name": directory_name,
            "group_ids": [(4, group.id) for group in groups],
        }
        if not self.parent_directory_id:
            vals.update({"is_root_directory": True})
        else:
            vals.update(
                {"parent_id": self.parent_directory_id.id, "inherit_group_ids": False}
            )
        return vals

    @api.constrains("model_id")
    def _check_model_id(self):
        for template in self:
            if self.env["dms.field.template"].search(
                [("model_id", "=", template.model_id.id), ("id", "!=", template.id)]
            ):
                raise UserError(
                    _("There is already a template created for this model.")
                )
