import abc
from os import environ

import unittest

from sklearn.pipeline import Pipeline

from ibm_watson_machine_learning import APIClient
from ibm_watson_machine_learning.experiment import AutoAI
from ibm_watson_machine_learning.deployment import WebService, Batch
from ibm_watson_machine_learning.workspace import WorkSpace
from ibm_watson_machine_learning.helpers.connections import DataConnection
from ibm_watson_machine_learning.experiment.autoai.optimizers import RemoteAutoPipelines
from ibm_watson_machine_learning.tests.utils import (get_wml_credentials, get_cos_credentials, get_space_id,
                                                     is_cp4d)
from ibm_watson_machine_learning.tests.utils.cleanup import space_cleanup, delete_model_deployment
from ibm_watson_machine_learning.utils.autoai.enums import TShirtSize, PredictionType

from ibm_watson_machine_learning.tests.utils.assertions import get_and_predict_all_pipelines_as_lale


class AbstractTestWebservice(abc.ABC):
    """
    The abstract tests which covers:
    - deployment with lale pipeline
    - deployment deletion
    In order to execute test connection definitions must be provided
    in inheriting classes.
    """
    SPACE_ONLY = True

    DEPLOYMENT_NAME = "SDK tests Deployment"

    wml_client: 'APIClient' = None
    remote_auto_pipelines: 'RemoteAutoPipelines' = None
    wml_credentials = None
    service: 'WebService' = None

    space_id = None
    project_id = None
    target_space_id = None

    X_df = None

    @abc.abstractmethod
    def test_00a_space_cleanup(self):
        pass

    ###########################################################
    #      DEPLOYMENT SECTION    tests numbers start from 31  #
    ###########################################################

    def test_31_deployment_setup_and_preparation(self):
        # note: if target_space_id is not set, use the space_id
        if self.target_space_id is None:
            self.target_space_id = self.space_id
        # end note

        if self.SPACE_ONLY:
            AbstractTestWebservice.service = WebService(source_wml_credentials=self.wml_credentials,
                                                        source_space_id=self.space_id,
                                                        target_wml_credentials=self.wml_credentials,
                                                        target_space_id=self.target_space_id)
        else:
            AbstractTestWebservice.service = WebService(source_wml_credentials=self.wml_credentials,
                                                        source_project_id=self.project_id,
                                                        target_wml_credentials=self.wml_credentials,
                                                        target_space_id=self.target_space_id)

        self.wml_client.set.default_space(self.space_id)
        delete_model_deployment(self.wml_client, deployment_name=self.DEPLOYMENT_NAME)

        self.assertIsInstance(AbstractTestWebservice.service, WebService, msg="Deployment is not of WebService type.")
        self.assertIsInstance(AbstractTestWebservice.service._source_workspace, WorkSpace, msg="Workspace set incorrectly.")
        self.assertEqual(AbstractTestWebservice.service.id, None, msg="Deployment ID initialized incorrectly")
        self.assertEqual(AbstractTestWebservice.service.name, None, msg="Deployment name initialized incorrectly")

    def test_32__deploy__deploy_best_computed_pipeline_from_autoai_on_wml(self):
        best_pipeline = self.remote_auto_pipelines.summary()._series['Enhancements'].keys()[0]
        print('Deploying', best_pipeline)
        AbstractTestWebservice.service.create(
            experiment_run_id=self.remote_auto_pipelines._engine._current_run_id,
            model=best_pipeline,
            deployment_name=self.DEPLOYMENT_NAME)

        self.assertIsNotNone(AbstractTestWebservice.service.id, msg="Online Deployment creation - missing id")
        self.assertIsNotNone(AbstractTestWebservice.service.name, msg="Online Deployment creation - name not set")
        self.assertIsNotNone(AbstractTestWebservice.service.scoring_url,
                             msg="Online Deployment creation - mscoring url  missing")

    def test_33_score_deployed_model(self):
        nb_records = 5
        predictions = AbstractTestWebservice.service.score(payload=self.X_df[:nb_records])
        print(predictions)
        self.assertIsNotNone(predictions)
        self.assertEqual(len(predictions['predictions'][0]['values']), nb_records)

    def test_34_list_deployments(self):
        AbstractTestWebservice.service.list()
        params = AbstractTestWebservice.service.get_params()
        print(params)
        self.assertIsNotNone(params)

    def test_35_delete_deployment(self):
        print("Delete current deployment: {}".format(AbstractTestWebservice.service.deployment_id))
        AbstractTestWebservice.service.delete()
        self.wml_client.set.default_space(self.space_id) if not self.wml_client.default_space_id else None
        self.wml_client.repository.delete(AbstractTestWebservice.service.asset_id)
        self.wml_client.set.default_project(self.project_id) if is_cp4d() else None
        self.assertEqual(AbstractTestWebservice.service.id, None, msg="Deployment ID deleted incorrectly")
        self.assertEqual(AbstractTestWebservice.service.name, None, msg="Deployment name deleted incorrectly")
        self.assertEqual(AbstractTestWebservice.service.scoring_url, None,
                         msg="Deployment scoring_url deleted incorrectly")
