# © 2021 Florian Kantelberg - initOS GmbH
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging
import re
from hashlib import sha256
from uuid import uuid4

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

_logger = logging.getLogger(__name__)


class ResUsersKey(models.Model):
    _name = "res.users.key"
    _description = _("User data of a vault")
    _rec_name = "fingerprint"
    _order = "create_date DESC"

    user_id = fields.Many2one("res.users", required=True)
    uuid = fields.Char(default=lambda self: uuid4(), required=True, readonly=True)
    current = fields.Boolean(default=True, readonly=True)
    fingerprint = fields.Char(compute="_compute_fingerprint", store=True)
    public = fields.Char(required=True, readonly=True)
    salt = fields.Char(required=True, readonly=True)
    iv = fields.Char(required=True, readonly=True)
    iterations = fields.Integer(required=True, readonly=True)
    version = fields.Integer(readonly=True)
    # Encrypted with master password of user
    private = fields.Char(required=True, readonly=True)

    @api.depends("public")
    def _compute_fingerprint(self):
        for rec in self:
            if rec.public:
                hashed = sha256(rec.public.encode()).hexdigest()
                rec.fingerprint = ":".join(re.findall(r".{2}", hashed))
            else:
                rec.fingerprint = False

    def _prepare_values(self, iterations, iv, private, public, salt, version):
        return {
            "iterations": iterations,
            "iv": iv,
            "private": private,
            "public": public,
            "salt": salt,
            "user_id": self.env.uid,
            "current": True,
            "version": version,
        }

    def store(self, iterations, iv, private, public, salt, version):
        if not all(isinstance(x, str) and x for x in [public, private, iv, salt]):
            raise ValidationError(_("Invalid parameter"))

        if not isinstance(iterations, int) or iterations < 4000:
            raise ValidationError(_("Invalid parameter"))

        if not isinstance(version, int):
            raise ValidationError(_("Invalid parameter"))

        domain = [
            ("user_id", "=", self.env.uid),
            ("private", "=", private),
        ]
        key = self.search(domain)
        if not key:
            # Disable all current keys
            self.env.user.keys.write({"current": False})

            rec = self.create(
                self._prepare_values(iterations, iv, private, public, salt, version)
            )
            return rec.uuid

        return False

    def extract_public_key(self, user):
        user = self.sudo().search([("user_id", "=", user), ("current", "=", True)])
        return user.public or None
