# © 2021 Florian Kantelberg - initOS GmbH
# Copyright 2022 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging
from datetime import datetime
from uuid import uuid4

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError

_logger = logging.getLogger(__name__)


class VaultEntry(models.Model):
    _name = "vault.entry"
    _description = _("Entry inside a vault")
    _inherit = ["vault.abstract"]
    _order = "complete_name"
    _rec_name = "complete_name"

    parent_id = fields.Many2one(
        "vault.entry",
        "Parent",
        ondelete="cascade",
        domain="[('vault_id', '=', vault_id)]",
    )
    child_ids = fields.One2many("vault.entry", "parent_id", "Child")

    vault_id = fields.Many2one("vault", "Vault", ondelete="cascade", required=True)
    user_id = fields.Many2one(related="vault_id.user_id")
    field_ids = fields.One2many("vault.field", "entry_id", "Fields")
    file_ids = fields.One2many("vault.file", "entry_id", "Files")
    log_ids = fields.One2many("vault.log", "entry_id", "Log", readonly=True)

    perm_user = fields.Many2one(related="vault_id.perm_user", store=False)
    allowed_read = fields.Boolean(related="vault_id.allowed_read", store=False)
    allowed_create = fields.Boolean(related="vault_id.allowed_create", store=False)
    allowed_share = fields.Boolean(related="vault_id.allowed_share", store=False)
    allowed_write = fields.Boolean(related="vault_id.allowed_write", store=False)
    allowed_delete = fields.Boolean(related="vault_id.allowed_delete", store=False)

    complete_name = fields.Char(
        compute="_compute_complete_name",
        store=True,
        readonly=True,
        recursive=True,
    )
    uuid = fields.Char(default=lambda self: uuid4(), required=True, copy=False)
    name = fields.Char(required=True)
    url = fields.Char()
    note = fields.Text()
    tags = fields.Many2many("vault.tag")
    expire_date = fields.Datetime("Expires on", default=False)
    expired = fields.Boolean(
        compute="_compute_expired",
        search="_search_expired",
        store=False,
    )

    _sql_constraints = [
        ("vault_uuid_uniq", "UNIQUE(vault_id, uuid)", _("The UUID must be unique.")),
    ]

    @api.constrains("parent_id")
    def _check_parent_id(self):
        if not self._check_recursion():
            raise ValidationError(_("You can not create recursive entries."))

    @api.depends("name", "parent_id.complete_name")
    def _compute_complete_name(self):
        for rec in self:
            if rec.parent_id:
                rec.complete_name = f"{rec.parent_id.complete_name} / {rec.name}"
            else:
                rec.complete_name = rec.name

    @api.model
    def search_panel_select_range(self, field_name, **kwargs):
        """We add the following contexts related to searchpanel:
        - entry_short_name: Show just the name instead of full path.
        - from_search_panel: It will be used to overwrite domain.
        Remove the limit of records (default is 200).
        """
        return super(
            VaultEntry,
            self.with_context(from_search_panel=True, entry_short_name=True),
        ).search_panel_select_range(field_name, **kwargs)

    def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
        """Changes related to searchpanel:
        - Add a domain to only show records with children.
        """
        domain = domain if domain else []
        if self.env.context.get("from_search_panel"):
            domain += [("child_ids", "!=", False)]
        return super().search_read(
            domain=domain, fields=fields, offset=offset, limit=limit, order=order
        )

    def copy_data(self, default=None):
        self.ensure_one()

        if default is None:
            default = {}

        if "name" not in default:
            default["name"] = _("%s (copy)", self.name)

        if "field_ids" not in default:
            default["field_ids"] = [
                (0, 0, field.copy_data()[0]) for field in self.field_ids
            ]
        if "file_ids" not in default:
            default["file_ids"] = [
                (0, 0, field.copy_data()[0]) for field in self.file_ids
            ]
        return super().copy_data(default)

    @api.depends("name", "complete_name")
    def _compute_display_name(self):
        if not self.env.context.get("entry_short_name", False):
            return super()._compute_display_name()
        for record in self:
            record.display_name = record.name

    @api.depends("expire_date")
    def _compute_expired(self):
        now = datetime.now()
        for rec in self:
            rec.expired = rec.expire_date and now > rec.expire_date

    def _search_expired(self, operator, value):
        if (operator not in ["=", "!="]) or (value not in [True, False]):
            return []

        if (operator, value) in [("=", True), ("!=", False)]:
            return [("expire_date", "<", datetime.now())]

        return ["|", ("expire_date", ">=", datetime.now()), ("expire_date", "=", False)]

    def log_change(self, action):
        if self.env.context.get("vault_skip_log"):
            return

        for rec in self:
            rec.log_info(
                _("%(action)s entry %(name)s by %(user)s")
                % {
                    "action": action,
                    "name": rec.complete_name,
                    "user": rec.env.user.display_name,
                }
            )

    @api.model_create_multi
    def create(self, vals_list):
        res = super().create(vals_list)
        res.log_change("Created")
        return res

    def unlink(self):
        self.log_change("Deleted")

        return super().unlink()

    def _log_entry(self, msg, state):
        self.ensure_one()
        return (
            self.env["vault.log"]
            .sudo()
            .create(
                {
                    "vault_id": self.vault_id.id,
                    "entry_id": self.id,
                    "user_id": self.env.uid,
                    "message": msg,
                    "state": state,
                }
            )
        )

    def action_open_import_wizard(self):
        self.ensure_one()
        wizard = self.env.ref("vault.view_vault_import_wizard")
        return {
            "name": _("Import from file"),
            "type": "ir.actions.act_window",
            "view_mode": "form",
            "res_model": "vault.import.wizard",
            "views": [(wizard.id, "form")],
            "view_id": wizard.id,
            "target": "new",
            "context": {
                "default_vault_id": self.vault_id.id,
                "default_parent_id": self.id,
            },
        }

    def action_open_export_wizard(self):
        self.ensure_one()
        wizard = self.env.ref("vault.view_vault_export_wizard")
        return {
            "name": _("Export to file"),
            "type": "ir.actions.act_window",
            "view_mode": "form",
            "res_model": "vault.export.wizard",
            "views": [(wizard.id, "form")],
            "view_id": wizard.id,
            "target": "new",
            "context": {
                "default_vault_id": self.vault_id.id,
                "default_entry_id": self.id,
            },
        }
