#  (C) Copyright IBM Corp. 2021.
#
#  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
import time
import pandas as pd
import logging
from ibm_watson_machine_learning.wml_client_error import ApiRequestFailure
from ibm_watson_machine_learning.tests.ICP.preparation_and_cleaning import *


class TestWMLClientWithDO(unittest.TestCase):
    deployment_uid = None
    model_uid = None
    scoring_url = None
    scoring_uid = None
    logger = logging.getLogger(__name__)
    skip_tests = False

    @classmethod
    def setUpClass(self):
        TestWMLClientWithDO.logger.info("Service Instance: setting up credentials")
        self.wml_credentials = get_wml_credentials()
        self.client = get_client()
        self.model_path = os.path.join(os.getcwd(), 'artifacts', 'do-model.tar.gz')

        if self.wml_credentials['version'] == '2.0':
           TestWMLClientWithDO.skip_tests = True

    def test_01_set_space(self):
        if not TestWMLClientWithDO.skip_tests:
            space = self.client.spaces.store({self.client.spaces.ConfigurationMetaNames.NAME: "DO_test_case"})

            TestWMLClientWithDO.space_id = self.client.spaces.get_uid(space)
            self.client.set.default_space(TestWMLClientWithDO.space_id)
            self.assertTrue("SUCCESS" in self.client.set.default_space(TestWMLClientWithDO.space_id))


    # Doesn't work since it doesn't recognize the class variables
    # @unittest.skipUnless(not skip_tests, 'WML Server 2.0')
    def test_02_publish_do_model_in_repository(self):
        if not TestWMLClientWithDO.skip_tests:
            TestWMLClientWithDO.logger.info("Saving trained model in repo ...")
            TestWMLClientWithDO.logger.debug("Model path: {}".format(self.model_path))

            self.client.repository.ModelMetaNames.show()

            sw_spec_uid = self.client.software_specifications.get_uid_by_name("do_12.9")
            output_data_schema = [{'id': 'stest',
                                  'type': 'list',
                                  'fields': [{'name': 'age', 'type': 'float'},
                                             {'name': 'sex', 'type': 'float'},
                                             {'name': 'cp', 'type': 'float'},
                                             {'name': 'restbp', 'type': 'float'},
                                             {'name': 'chol', 'type': 'float'},
                                             {'name': 'fbs', 'type': 'float'},
                                             {'name': 'restecg', 'type': 'float'},
                                             {'name': 'thalach', 'type': 'float'},
                                             {'name': 'exang', 'type': 'float'},
                                             {'name': 'oldpeak', 'type': 'float'},
                                             {'name': 'slope', 'type': 'float'},
                                             {'name': 'ca', 'type': 'float'},
                                             {'name': 'thal', 'type': 'float'}]
                                   }, {'id': 'teste2',
                     'type': 'test',
                     'fields': [{'name': 'age', 'type': 'float'},
                                {'name': 'sex', 'type': 'float'},
                                {'name': 'cp', 'type': 'float'},
                                {'name': 'restbp', 'type': 'float'},
                                {'name': 'chol', 'type': 'float'},
                                {'name': 'fbs', 'type': 'float'},
                                {'name': 'restecg', 'type': 'float'},
                                {'name': 'thalach', 'type': 'float'},
                                {'name': 'exang', 'type': 'float'},
                                {'name': 'oldpeak', 'type': 'float'},
                                {'name': 'slope', 'type': 'float'},
                                {'name': 'ca', 'type': 'float'},
                                {'name': 'thal', 'type': 'float'}]}]
            model_meta_props = {self.client.repository.ModelMetaNames.NAME: "LOCALLY created DO model",
                           self.client.repository.ModelMetaNames.TYPE: "do-docplex_12.9",
                           self.client.repository.ModelMetaNames.SOFTWARE_SPEC_UID: sw_spec_uid,
                           self.client.repository.ModelMetaNames.OUTPUT_DATA_SCHEMA: output_data_schema
                                }
            published_model = self.client.repository.store_model(model=self.model_path, meta_props=model_meta_props)
            TestWMLClientWithDO.model_uid = self.client.repository.get_model_uid(published_model)
            TestWMLClientWithDO.logger.info("Published model ID:" + str(TestWMLClientWithDO.model_uid))
            self.assertIsNotNone(TestWMLClientWithDO.model_uid)

    def test_04_get_details(self):
        if not TestWMLClientWithDO.skip_tests:
            TestWMLClientWithDO.logger.info("Get details")
            self.assertIsNotNone(self.client.repository.get_details(TestWMLClientWithDO.model_uid))

    def test_05_get_model_details(self):
        if not TestWMLClientWithDO.skip_tests:
            TestWMLClientWithDO.logger.info("Get model details")
            self.assertIsNotNone(self.client.repository.get_model_details(TestWMLClientWithDO.model_uid))

    def test_07_create_deployment(self):
        if not TestWMLClientWithDO.skip_tests:
            TestWMLClientWithDO.logger.info("Create deployment")

            deploy_meta = {
                self.client.deployments.ConfigurationMetaNames.NAME: "deployment_DO",
                self.client.deployments.ConfigurationMetaNames.DESCRIPTION: "DO deployment",
                self.client.deployments.ConfigurationMetaNames.BATCH: {},
                self.client.deployments.ConfigurationMetaNames.HARDWARE_SPEC: {"name": "S", "num_nodes": 1}
            }


            deployment_details = self.client.deployments.create(TestWMLClientWithDO.model_uid,
                                                                meta_props=deploy_meta)

            TestWMLClientWithDO.logger.debug("Deployment details: {}".format(deployment_details))

            TestWMLClientWithDO.deployment_uid = self.client.deployments.get_uid(deployment_details)
            TestWMLClientWithDO.logger.debug("Deployment uid: {}".format(TestWMLClientWithDO.deployment_uid))


    def test_08_get_deployment_details(self):
        if not TestWMLClientWithDO.skip_tests:
            TestWMLClientWithDO.logger.info("Get deployment details")
            deployment_details = self.client.deployments.get_details(deployment_uid=TestWMLClientWithDO.deployment_uid)
            self.assertIsNotNone(deployment_details)


    def test_09_create_job(self):
        if not TestWMLClientWithDO.skip_tests:
            TestWMLClientWithDO.logger.info("Create job details")
            TestWMLClientWithDO.logger.debug("Create job")

            # initialize list of lists
            diet_food = pd.DataFrame([["Roasted Chicken", 0.84, 0, 10],
                                      ["Spaghetti W/ Sauce", 0.78, 0, 10],
                                      ["Tomato,Red,Ripe,Raw", 0.27, 0, 10],
                                      ["Apple,Raw,W/Skin", 0.24, 0, 10],
                                      ["Grapes", 0.32, 0, 10],
                                      ["Chocolate Chip Cookies", 0.03, 0, 10],
                                      ["Lowfat Milk", 0.23, 0, 10],
                                      ["Raisin Brn", 0.34, 0, 10],
                                      ["Hotdog", 0.31, 0, 10]], columns=["name", "unit_cost", "qmin", "qmax"])

            diet_food_nutrients = pd.DataFrame([
                ["Spaghetti W/ Sauce", 358.2, 80.2, 2.3, 3055.2, 11.6, 58.3, 8.2],
                ["Roasted Chicken", 277.4, 21.9, 1.8, 77.4, 0, 0, 42.2],
                ["Tomato,Red,Ripe,Raw", 25.8, 6.2, 0.6, 766.3, 1.4, 5.7, 1],
                ["Apple,Raw,W/Skin", 81.4, 9.7, 0.2, 73.1, 3.7, 21, 0.3],
                ["Grapes", 15.1, 3.4, 0.1, 24, 0.2, 4.1, 0.2],
                ["Chocolate Chip Cookies", 78.1, 6.2, 0.4, 101.8, 0, 9.3, 0.9],
                ["Lowfat Milk", 121.2, 296.7, 0.1, 500.2, 0, 11.7, 8.1],
                ["Raisin Brn", 115.1, 12.9, 16.8, 1250.2, 4, 27.9, 4],
                ["Hotdog", 242.1, 23.5, 2.3, 0, 0, 18, 10.4]
            ], columns=["Food", "Calories", "Calcium", "Iron", "Vit_A", "Dietary_Fiber", "Carbohydrates", "Protein"])

            diet_nutrients = pd.DataFrame([
                ["Calories", 2000, 2500],
                ["Calcium", 800, 1600],
                ["Iron", 10, 30],
                ["Vit_A", 5000, 50000],
                ["Dietary_Fiber", 25, 100],
                ["Carbohydrates", 0, 300],
                ["Protein", 50, 100]
            ], columns=["name", "qmin", "qmax"])

            job_payload_ref = {
                self.client.deployments.DecisionOptimizationMetaNames.INPUT_DATA: [
                    {
                        "id": "diet_food.csv",
                        "values": diet_food
                    },
                    {
                        "id": "diet_food_nutrients.csv",
                        "values": diet_food_nutrients
                    },
                    {
                        "id": "diet_nutrients.csv",
                        "values": diet_nutrients
                    }
                ],
                self.client.deployments.DecisionOptimizationMetaNames.OUTPUT_DATA: [
                    {
                        "id": ".*.csv"
                    }
                ]
            }

            TestWMLClientWithDO.job_details = self.client.deployments.create_job(TestWMLClientWithDO.deployment_uid,
                                                                                 meta_props=job_payload_ref)
            TestWMLClientWithDO.job_id = self.client.deployments.get_job_uid(TestWMLClientWithDO.job_details)


    def test_10_get_job_status(self):
        if not TestWMLClientWithDO.skip_tests:
            start_time = time.time()
            diff_time = start_time - start_time
            while True and diff_time < 10 * 60:
                time.sleep(3)
                response = self.client.deployments.get_job_status(TestWMLClientWithDO.job_id)
                if response['state'] == 'completed' or response['state'] == 'error' or response['state'] == 'failed':
                    break
                diff_time = time.time() - start_time

            self.assertIsNotNone(response)
            self.assertTrue(response['state'] == 'completed')

    def test_11_extract_and_display_solution(self):
        if not TestWMLClientWithDO.skip_tests:
            job_details_do = self.client.deployments.get_job_details(TestWMLClientWithDO.job_id)
            kpi = job_details_do['entity']['decision_optimization']['solve_state']['details']['KPI.Total Calories']
            print(kpi)
            TestWMLClientWithDO.logger.debug("The value of kpi is ", kpi)
            self.assertTrue(kpi == '2000.0')


    def test_12_list_jobs(self):
        if not TestWMLClientWithDO.skip_tests:
            self.client.deployments.list_jobs()

    def test_12_delete_job(self):
        if not TestWMLClientWithDO.skip_tests:
            self.client.deployments.delete_job(TestWMLClientWithDO.job_id)

    def test_13_delete_deployment(self):
        if not TestWMLClientWithDO.skip_tests:
            TestWMLClientWithDO.logger.info("Delete deployment")
            self.client.deployments.delete(TestWMLClientWithDO.deployment_uid)

    def test_14_delete_model(self):
        if not TestWMLClientWithDO.skip_tests:
            TestWMLClientWithDO.logger.info("Delete function")
            self.client.script.delete(TestWMLClientWithDO.model_uid)

    def test_15_delete_space(self):
        if not TestWMLClientWithDO.skip_tests:
            self.client.spaces.delete(TestWMLClientWithDO.space_id)


if __name__ == '__main__':
    unittest.main()
