#!/usr/bin/env python
#
# (c) Copyright 2019-2022 Hewlett Packard Enterprise Development LP
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import unittest

from opsramp.api import ORapi
import opsramp.binding
from opsramp.rba import Category
import requests_mock


class StaticsTest(unittest.TestCase):
    def setUp(self):
        # Several of the tests need a filename, its contents and
        # the base64 encoded version, so compute all of that up
        # front and store the data for later use. The actual
        # contents are not important so use any file that we know
        # will exist.
        self.testfile = "setup.py"
        with open(self.testfile, "rb") as f:
            self.content_raw = f.read()
        # check it's not empty.
        assert self.content_raw
        self.content_64 = ORapi.b64encode_payload(self.testfile)

    def test_mkAttachment(self):
        tvalues = {"name": self.testfile, "content": self.content_raw}
        actual = Category.mkAttachment(name=tvalues["name"], payload=tvalues["content"])
        expected = {"name": tvalues["name"], "file": tvalues["content"]}
        assert actual == expected

    def test_mkBadParameter(self):
        with self.assertRaises(AssertionError):
            Category.mkParameter(
                name="pname",
                description="pdesc",
                datatype="STRING",
                optional=True
                # deliberately missing default value
            )

    def test_mkGoodParameter(self):
        tvalues = {
            "name": "venue",
            "description": "Where am I today?",
            "type": "STRING",
        }
        actual = Category.mkParameter(
            name=tvalues["name"],
            description=tvalues["description"],
            datatype=tvalues["type"],
        )
        expected = {
            "name": tvalues["name"],
            "description": tvalues["description"],
            "dataType": tvalues["type"],
            "type": "REQUIRED",
            "defaultValue": None,
        }
        assert actual == expected

    def test_mkBadScript(self):
        tvalues = {"name": "sname", "description": "pdesc", "platforms": ["LINUX"]}
        with self.assertRaises(AssertionError):
            Category.mkScript(
                execution_type="Firing squad is not valid",
                name=tvalues["name"],
                description=tvalues["description"],
                platforms=tvalues["platforms"],
                payload_file=self.testfile,
            )
        with self.assertRaises(AssertionError):
            Category.mkScript(
                execution_type="COMMAND",
                name=tvalues["name"],
                description=tvalues["description"],
                platforms=tvalues["platforms"],
                payload="if there is a payload",
                payload_file="cannot have payload_file as well",
            )

    def test_mkGoodCommandScript(self):
        p1 = Category.mkParameter(
            name="venue", description="Where am I today?", datatype="STRING"
        )
        tvalues = {
            "name": "Hello <venue>",
            "description": "Stereotypical rock star intro",
            "platforms": ["LINUX"],
            "type": "COMMAND",
            "payload": 'echo "hello $1"',
            "parameters": [p1],
        }
        actual = Category.mkScript(
            name=tvalues["name"],
            description=tvalues["description"],
            platforms=tvalues["platforms"],
            execution_type=tvalues["type"],
            payload=tvalues["payload"],
            parameters=tvalues["parameters"],
        )
        expected = {
            "name": tvalues["name"],
            "description": tvalues["description"],
            "platforms": tvalues["platforms"],
            "executionType": tvalues["type"],
            "command": tvalues["payload"],
            "parameters": tvalues["parameters"],
        }
        assert actual == expected

    def test_mkGoodPythonScript(self):
        tvalues = {
            "name": "Hello <venue>",
            "description": "Python rock star intro",
            "platforms": ["LINUX"],
            "type": "PYTHON",
            "process_name": "jabberwocky",
            "service_name": "silmarilion",
            "install_timeout": "a really long time",
        }
        actual = Category.mkScript(
            name=tvalues["name"],
            description=tvalues["description"],
            platforms=tvalues["platforms"],
            execution_type=tvalues["type"],
            script_name=self.testfile,
            payload_file=self.testfile,
            install_timeout=tvalues["install_timeout"],
            process_name=tvalues["process_name"],
            service_name=tvalues["service_name"],
        )
        expected = {
            "name": tvalues["name"],
            "description": tvalues["description"],
            "platforms": tvalues["platforms"],
            "executionType": tvalues["type"],
            "attachment": {"name": self.testfile, "file": self.content_64},
            "installTimeout": tvalues["install_timeout"],
            "processName": tvalues["process_name"],
            "serviceName": tvalues["service_name"],
        }
        assert actual == expected


class ApiTest(unittest.TestCase):
    def setUp(self):
        fake_url = "mock://api.example.com"
        fake_token = "unit-test-fake-token"
        self.ormp = opsramp.binding.Opsramp(fake_url, fake_token)

        self.fake_client_id = "client_for_unit_test"
        self.client = self.ormp.tenant(self.fake_client_id)
        assert self.client.is_client()

        self.rba = self.client.rba()
        assert "Rba" in str(self.rba)

    def test_categories(self):
        group = self.rba.categories()

        parent = {"id": 123456, "name": "unit-test-parent-category"}
        child = {"id": 789012, "name": "unit-test-child-category"}

        with requests_mock.Mocker() as m:
            # we will be doing two posts so mock both results now.
            url = group.api.compute_url()
            m.post(url, [{"json": parent}, {"json": child}], complete_qs=True)

            # Create a category with no parent ID specified
            actual = group.create(name=parent["name"])
            assert actual == parent

            # Create a category with parent ID specified
            actual = group.create(name=child["name"], parent_uuid=parent["id"])
            assert actual == child

    def test_delete_category(self):
        category_group = self.rba.categories()
        assert category_group
        categoryID = 12345
        expected = ""
        with requests_mock.Mocker() as m:
            url = category_group.api.compute_url(categoryID)
            m.delete(url, text=expected, complete_qs=True)
            actual = category_group.delete(uuid=categoryID)
            assert actual == expected

    def test_update_category(self):
        category_group = self.rba.categories()
        assert category_group
        categoryID = 12345
        updated_name = "some new name"
        expected = {"id": categoryID, "name": updated_name}

        with requests_mock.Mocker() as m:
            url = category_group.api.compute_url()
            m.put(url, json=expected, complete_qs=True)
            actual = category_group.update(categoryID, {"name": updated_name})
            assert actual == expected

    def test_create_script(self):
        this1 = self.rba.categories().category("unit-test-1")
        assert this1
        expected = {"unit": "test"}
        assert expected
        with requests_mock.Mocker() as m:
            url = this1.api.compute_url()
            m.post(url, json=expected, complete_qs=True)
            actual = this1.create(definition=expected)
            assert actual == expected

    def test_update_script(self):
        this1 = self.rba.categories().category("unit-test-1")
        assert this1
        scriptId = 12345
        expected = {"unit": "test"}
        assert expected
        with requests_mock.Mocker() as m:
            url = this1.api.compute_url(scriptId)
            m.post(url, json=expected, complete_qs=True)
            actual = this1.update(uuid=scriptId, definition=expected)
            assert actual == expected

    def test_delete_script(self):
        this1 = self.rba.categories().category("unit-test-1")
        assert this1
        scriptId = 67890
        expected = ""
        with requests_mock.Mocker() as m:
            url = this1.api.compute_url(scriptId)
            m.delete(url, text=expected, complete_qs=True)
            actual = this1.delete(uuid=scriptId)
            assert actual == expected
