# (Copyright) 2015 ABF OSIELL <http://osiell.com>
# (Copyright) 2018 Creu Blanca
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging

from odoo import _, api, fields, models
from odoo.tools.safe_eval import safe_eval

_logger = logging.getLogger(__name__)


class NscaCheck(models.Model):
    _name = "nsca.check"
    _description = "NSCA Check"
    _inherits = {"ir.cron": "cron_id"}

    cron_id = fields.Many2one(
        "ir.cron", string="Cron", required=True, ondelete="cascade", readonly=True
    )
    server_id = fields.Many2one("nsca.server", string="Server", required=True)
    service = fields.Char(required=True)
    nsca_model = fields.Char("NSCA Model")
    nsca_function = fields.Char("Method")
    nsca_args = fields.Char("Arguments")
    allow_void_result = fields.Boolean(
        "Allow void result",
        default=False,
        help="By default, a CRITICAL message is sent if the method does not "
        "return.\nIf checked, no message will be sent in such a case.",
    )

    @api.model
    def default_get(self, fields_list):
        """Set some default values on the fly, without overriding fields (which
        has the side effect to re-create the fields on the current model).
        """
        res = super(NscaCheck, self).default_get(fields_list)
        res["name"] = "TEMP"  # Required on 'ir.cron', replaced later
        res["interval_number"] = 10
        res["interval_type"] = "minutes"
        return res

    def _force_values(self):
        """Force some values:
        - Compute the name of the NSCA check to be readable
          among the others 'ir.cron' records.
        """
        model = self.env["ir.model"].search([("model", "=", self._name)])
        for check in self:
            vals = {
                "name": "{} - {}".format(_("NSCA Check"), check.service),
                "model_id": model.id,
                "state": "code",
                "code": "model._cron_check(%s,)" % check.id,
                "doall": False,
                "numbercall": -1,
            }
            res = super(NscaCheck, check).write(vals)
        return res

    @api.model
    def create(self, vals):
        if not vals.get("model_id", False):
            vals["model_id"] = (
                self.env["ir.model"].search([("model", "=", self._name)]).id
            )
        if not vals.get("state", False):
            vals["state"] = "code"
        check = super(NscaCheck, self).create(vals)
        check._force_values()
        return check

    def write(self, vals):
        res = super(NscaCheck, self).write(vals)
        if "service" in vals:
            self._force_values()
        return res

    @api.model
    def _cron_check(self, check_id):
        self.env["nsca.server"]._check_send_nsca_command()
        check = self.browse(check_id)
        rc, message, performance = 3, "Unknown", {}
        try:
            NscaModel = self.env[check.nsca_model]
            results = {"model": NscaModel}
            safe_eval(
                "result = model.{}({})".format(
                    check.nsca_function, check.nsca_args or ""
                ),
                results,
                mode="exec",
                nocopy=True,
            )
            result = results["result"]
            if not result:
                if check.allow_void_result:
                    return False
                raise ValueError("'%s' method does not return" % check.nsca_function)
            if len(result) == 2:
                rc, message = result
            else:
                rc, message, performance = result
        except Exception as exc:
            rc, message = 2, "%s" % exc
            _logger.warning("%s - %s", check.service, message)
        check._send_nsca(rc, message, performance)
        return True

    def _send_nsca(self, rc, message, performance):
        for check in self:
            check.server_id._send_nsca(check.service, rc, message, performance)
