# Part of Odoo. See LICENSE file for full copyright and licensing details.

import json
from http import HTTPStatus
from unittest.mock import patch

import requests

from odoo.tests.common import JsonRpcException, new_test_user, tagged
from odoo.tools import mute_logger

from odoo.addons.base.tests.common import HttpCaseWithUserDemo

SAMPLE = {
    "text": "<p>Al mal tiempo, buena cara.</p>",
    "src": "es",
    "en": "<p>To bad weather, good face.</p>",
    "fr": "<p>Au mauvais temps, bonne tête.</p>",
    "nl": "<script src='xss-min.js'/><p onclick='XSS()'>Bij slecht weer, goed gezicht.</p>",
    "lang": {
        "fr": "espagnol",
        "en": "Spanish",
    },
}


def mock_response(fun):
    def wrapper(self, url, data=False, params=False, timeout=5):
        response = requests.Response()
        response.status_code = HTTPStatus.OK
        content = {"data": fun(self, url=url, data=data, params=params)}
        if not content["data"]:
            response.status_code = HTTPStatus.BAD_REQUEST
            content = {"error": {"message": "Mocked Error"}}
        response._content = json.dumps(content).encode()
        return response

    return wrapper


# Google Cloud Translation Documentation: https://cloud.google.com/translate/docs/reference/api-overview?hl=en
@tagged("mail_message")
class TestTranslationController(HttpCaseWithUserDemo):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.env["res.lang"]._activate_lang("fr_FR")
        cls.env["res.lang"]._activate_lang("en_US")
        cls.env.ref("base.user_admin").write({"lang": "fr_FR"})
        cls.api_key = "VALIDKEY"
        cls.env["ir.config_parameter"].set_bool("mail.use_google_translate_api", True)
        cls.env["ir.config_parameter"].set_str("mail.google_translate_api_key", cls.api_key)
        cls.message = cls.env["mail.message"].create(
            {
                "body": SAMPLE["text"],
                "model": "res.partner",
                "res_id": cls.env.ref("base.user_admin").partner_id.id,
                "message_type": "comment",
            }
        )
        cls.request_count = 0

    @mock_response
    def _patched_post(self, url, data, params, timeout=5):
        self.request_count += 1
        if f"/v2/detect?key={self.api_key}" in url:
            result = {
                "language": SAMPLE["src"],
                "isReliable": True,
                "confidence": 0.98,
            }
            return {"detections": [[result]]}
        if f"/v2/?key={self.api_key}" in url:
            return {"translations": [{"translatedText": SAMPLE[data.get("target")]}]}

    def _mock_translation_request(self, data):
        with patch.object(requests, "post", self._patched_post):
            return self.make_jsonrpc_request("/mail/message/translate", data)

    def test_update_message(self):
        self.authenticate("admin", "admin")
        result = self._mock_translation_request({"message_id": self.message.id})
        self.assertFalse(result.get("error"))
        self.assertEqual(self.env["mail.message.translation"].search_count([]), 1)
        # The translation records should not be discarded if the body did not change.
        self.make_jsonrpc_request(
            "/mail/message/update_content",
            {"message_id": self.message.id, "update_data": {"body": None, "attachment_ids": []}},
        )
        self.assertEqual(self.env["mail.message.translation"].search_count([]), 1)
        self.make_jsonrpc_request(
            "/mail/message/update_content",
            {
                "message_id": self.message.id,
                "update_data": {"body": "update", "attachment_ids": []},
            },
        )
        self.assertFalse(self.env["mail.message.translation"].search_count([]))

    def test_translation_multi_users(self):
        new_test_user(self.env, "user_test_fr", groups="base.group_user", lang="fr_FR")
        new_test_user(self.env, "user_test_en", groups="base.group_user", lang="en_US")
        for login, target_lang in [("user_test_fr", "fr"), ("user_test_en", "en"), ("admin", "fr")]:
            self.authenticate(login, login)
            result = self._mock_translation_request({"message_id": self.message.id})
            self.assertFalse(result.get("error"))
            self.assertEqual(result["body"], SAMPLE[target_lang])
            self.assertEqual(result["lang_name"], SAMPLE["lang"][target_lang])
        # There is one translation record per target language.
        self.assertEqual(self.env["mail.message.translation"].search_count([]), 2)
        # No API request should be sent if a translation value or source already exists.
        self.assertEqual(self.request_count, 3)

    def test_invalid_api_key(self):
        self.env["ir.config_parameter"].set_str("mail.google_translate_api_key", "INVALIDKEY")
        self.authenticate("demo", "demo")
        result = self._mock_translation_request({"message_id": self.message.id})
        self.assertNotIn("body", result)
        self.assertNotIn("lang_name", result)
        self.assertTrue(result["error"])

    def test_html_sanitization(self):
        self.env["res.lang"]._activate_lang("nl_NL")
        new_test_user(self.env, "user_test_nl", groups="base.group_user", lang="nl_NL")
        self.authenticate("user_test_nl", "user_test_nl")
        result = self._mock_translation_request({"message_id": self.message.id})
        self.assertFalse(result.get("error"))
        self.assertHTMLEqual(result["body"], "<p>Bij slecht weer, goed gezicht.</p>")
        translation = self.env["mail.message.translation"].search([])
        self.assertEqual(len(translation), 1)
        self.assertHTMLEqual(translation.body, "<p>Bij slecht weer, goed gezicht.</p>")

    def test_access_right(self):
        with self.assertRaises(JsonRpcException, msg="odoo.http.session.SessionExpiredException"):
            self._mock_translation_request({"message_id": self.message.id})
        new_test_user(self.env, "user_test_portal", groups="base.group_portal", lang="fr_FR")
        self.authenticate("user_test_portal", "user_test_portal")
        with self.assertRaises(JsonRpcException, msg="odoo.exceptions.AccessError"), mute_logger("odoo.http"):
            self._mock_translation_request({"message_id": self.message.id})

    def test_unknown_language(self):
        self.authenticate("admin", "admin")
        with patch.dict(SAMPLE, {"src": "unknown_by_babel_but_known_by_google_api"}):
            result = self._mock_translation_request({"message_id": self.message.id})
        self.assertEqual(result["body"], "<p>Au mauvais temps, bonne tête.</p>")
