# Copyright 2018-2019 Brainbean Apps (https://brainbeanapps.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

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


class ProjectAssignment(models.Model):
    _name = "project.assignment"
    _description = "Project Assignment"
    _inherit = ["mail.thread"]

    active = fields.Boolean(
        default=True,
    )
    name = fields.Char(
        compute="_compute_name",
        store=True,
        index=True,
    )
    company_id = fields.Many2one(
        comodel_name="res.company",
        string="Company",
        default=lambda self: self.env.company,
        ondelete="cascade",
    )
    project_id = fields.Many2one(
        comodel_name="project.project",
        string="Project",
        ondelete="cascade",
    )
    role_id = fields.Many2one(
        comodel_name="project.role",
        string="Role",
        required=True,
        ondelete="restrict",
    )
    user_id = fields.Many2one(
        comodel_name="res.users",
        string="User",
        required=True,
        ondelete="restrict",
    )

    _sql_constraints = [
        (
            "project_role_user_uniq",
            "UNIQUE (project_id, role_id, user_id)",
            "User may be assigned per role only once within a project!",
        ),
        (
            "company_role_user_uniq",
            (
                "EXCLUDE ("
                "    company_id WITH =, role_id WITH =, user_id WITH ="
                ") WHERE ("
                "    project_id IS NULL"
                ")"
            ),
            "User may be assigned per role only once within a company!",
        ),
        (
            "nocompany_role_user_uniq",
            (
                "EXCLUDE (role_id WITH =, user_id WITH =) WHERE ("
                "    project_id IS NULL AND company_id IS NULL"
                ")"
            ),
            "User may be assigned per role only once!",
        ),
    ]

    @api.depends(
        "company_id.name",
        "project_id.name",
        "role_id.name",
        "user_id.name",
    )
    def _compute_name(self):
        for assignment in self:
            if assignment.project_id:
                assignment.name = _("%(USER)s as %(ROLE)s on %(PROJECT)s") % {
                    "USER": assignment.user_id.name,
                    "ROLE": assignment.role_id.name,
                    "PROJECT": assignment.project_id.name,
                }
            elif assignment.company_id:
                assignment.name = _("%(USER)s as %(ROLE)s in %(PROJECT)s") % {
                    "USER": assignment.user_id.name,
                    "ROLE": assignment.role_id.name,
                    "PROJECT": assignment.company_id.name,
                }
            else:
                assignment.name = _("%(USER)s as %(ROLE)s") % {
                    "USER": assignment.user_id.name,
                    "ROLE": assignment.role_id.name,
                }

    def _get_conflicting_domain(self):
        self.ensure_one()
        return (
            [
                ("id", "!=", self.id),
                ("role_id", "=", self.role_id.id),
                ("user_id", "=", self.user_id.id),
            ]
            + (
                [("company_id", "in", [False, self.company_id.id])]
                if self.company_id
                else []
            )
            + (
                [("project_id", "in", [False, self.project_id.id])]
                if self.project_id
                else []
            )
        )

    @api.constrains("company_id", "project_id", "role_id", "user_id")
    def _check(self):
        """
        Check if assignment conflicts with any already-existing assignment and
        if specific role can be assigned at all (extension hook).
        """
        for assignment in self:
            conflicting_assignment = self.search(
                assignment._get_conflicting_domain(),
                limit=1,
            )
            if conflicting_assignment:
                raise ValidationError(
                    _(
                        "Assignment %(ASSIGNMENT)s conflicts with another assignment: "
                        "%(OTHER_ASSIGNMENT)s"
                    )
                    % {
                        "ASSIGNMENT": assignment.name,
                        "OTHER_ASSIGNMENT": conflicting_assignment.name,
                    }
                )
            if not assignment.role_id.can_assign(
                assignment.user_id, assignment.project_id
            ):
                if assignment.project_id:
                    error = _(
                        "User %(USER)s can not be assigned to role %(ROLE)s on %(PROJECT)s."
                    ) % {
                        "USER": assignment.user_id.name,
                        "ROLE": assignment.role_id.name,
                        "PROJECT": assignment.project_id.name,
                    }
                else:
                    error = _("User %(USER)s can not be assigned to role %(ROLE)s.") % {
                        "USER": assignment.user_id.name,
                        "ROLE": assignment.role_id.name,
                    }
                raise ValidationError(error)
