#   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.
#
'''ModelArts model v2 action implementations'''
import logging

from osc_lib import utils
from osc_lib.command import command
from otcextensions.common import sdk_utils

from otcextensions.i18n import _

LOG = logging.getLogger(__name__)


def _flatten_output(obj):
    data = {
        'model_name': obj.model_name,
        'model_type': obj.model_type,
        'model_version': obj.model_version,
        'model_id': obj.model_id,
        'model_size': obj.model_size,
        'description': obj.description
    }
    return data


def _get_columns(item):
    column_map = {
    }
    return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)


class DeleteModel(command.Command):
    _description = _('Delete ModelArts Model')

    def get_parser(self, prog_name):
        parser = super(DeleteModel, self).get_parser(prog_name)
        parser.add_argument(
            'model_id',
            metavar='<model_id>',
            help=_('Name of the model to delete.')
        )
        return parser

    def take_action(self, parsed_args):
        client = self.app.client_manager.modelarts
        client.delete_model(model=parsed_args.model_id, ignore_missing=False)


class CreateModel(command.ShowOne):
    _description = _('Create a ModelArts model')

    def get_parser(self, prog_name):
        parser = super(CreateModel, self).get_parser(prog_name)
        parser.add_argument(
            '--model_name',
            metavar='<model_name>',
            required=True,
            help=_('Model name.'
                   'Model name. The value can contain 1 to 64 visible characters, ',
                   'including Chinese characters. Only letters, Chinese characters, digits, '
                   'hyphens (-), and underscores (_) are allowed.')
        )
        parser.add_argument(
            '--model_version',
            metavar='<model_version>',
            required=True,
            help=_('Model version in the format of Digit.Digit.Digit. ',
                   'The value range of the digits is [1, 99]. ',
                   'Note that no part of the version number can start with 0. ',
                   'For example, 01.01.01 is not allowed.')

        )
        parser.add_argument(
            '--source_location',
            metavar='<source_location>',
            required=True,
            help=_('OBS path where the model is located or the template address of the SWR image')
        )
        parser.add_argument(
            '--source_job_id',
            metavar='<source_job_id>',
            help=_('ID of the source training job. If the model is generated from a training job, ',
                   'input this parameter for source tracing.',
                   ' If the model is imported from a third-party meta model, leave this parameter blank. ',
                   'By default, this parameter is left blank. ')
        )
        parser.add_argument(
            '--source_job_version',
            metavar='<source_job_version>',
            help=_('Version of the source training job. If the model is generated from a training job, '
                   'input this parameter for source tracing. '
                   'If the model is imported from a third-party meta model, leave this parameter blank. '
                   'By default, this parameter is left blank.')
        )

        parser.add_argument(
            '--source_type',
            metavar='<source_type>',
            help=_('Model source type. Currently, the value can only be auto, ',
                   'which indicates ExeML models (model download is not supported). ',
                   'If the model is deployed by a training job, leave this parameter blank.',
                   ' By default, this parameter is left blank.')
        )
        parser.add_argument(
            '--model_type',
            metavar='<model_type>',
            required=True,
            help=_('Model type. The value can be TensorFlow, MXNet, Caffe, Spark_MLlib, ',
                   'Scikit_Learn, XGBoost, Image, or PyTorch, which is read from the configuration file.')
        )
        parser.add_argument(
            '--runtime',
            metavar='<runtime>',
            help=_('Model running environment. The possible values of runtime are related to model_type.')
        )
        parser.add_argument(
            '--description',
            metavar='<description>',
            help=_('Model remarks. The value contains a maximum of 100 characters and ',
                   'cannot contain the following special characters and more: &!\'\"<>= ')
        )
        parser.add_argument(
            '--execution_code',
            metavar='<execution_code>',
            help=_('OBS path for storing the execution code. By default, this parameter is left blank.'
                   ' The name of the execution code file is fixed to customize_service.py. ')
        )
        parser.add_argument(
            '--input_params',
            metavar='<input_params>',
            help=_('Collection of input parameters of a model. By default, this parameter is left blank.')
        )
        parser.add_argument(
            '--output_params',
            metavar='<output_params>',
            help=_('Collection of output parameters of a model. By default, this parameter is left blank.')
        )
        parser.add_argument(
            '--dependencies',
            metavar='<dependencies>',
            help=_('Package required for inference code and model. By default, this parameter is left blank.')
        )
        parser.add_argument(
            '--model_algorithm',
            metavar='<model_algorithm>',
            help=_('Model algorithm. If the algorithm is read from the configuration file, '
                   'this parameter can be left blank. For example, the value can be predict_analysis,'
                   ' object_detection, or image_classification. ')
        )
        parser.add_argument(
            '--model_metrics',
            metavar='<model_metrics>',
            help=_('Model precision, which is read from the configuration file ')
        )
        parser.add_argument(
            '--apis',
            metavar='<apis>',
            help=_('All apis input and output parameters of the model. '
                   'If the parameters are read from the configuration file, this parameter can be left blank.')
        )
        parser.add_argument(
            '--initial_config',
            metavar='<initial_config>',
            help=_('Character string converted from the final model configuration file. '
                   'It is recommended that the initial_config file be used to provide information'
                   ' about the fields such as apis, dependencies, input_params, and output_params.')
        )
        parser.add_argument(
            '--workspace_id',
            metavar='<workspace_id>',
            help=_('Workspace ID. Default value: 0')
        )
        parser.add_argument(
            '--model_docs',
            metavar='<model_docs>',
            help=_('List of model description documents. A maximum of three documents are supported.')
        )
        parser.add_argument(
            '--install_type',
            metavar='<install_type>',
            help=_('Deployment type. Only lowercase letters are supported. '
                   'The value can be real-time, or batch. Default value: ["real-time","batch"]')
        )
        return parser

    def take_action(self, parsed_args):

        client = self.app.client_manager.modelarts

        attrs = {}

        if parsed_args.model_name:
            attrs['model_name'] = parsed_args.model_name
        if parsed_args.model_version:
            attrs['model_version'] = parsed_args.model_version
        if parsed_args.source_location:
            attrs['source_location'] = parsed_args.source_location
        if parsed_args.source_job_id:
            attrs['source_job_id'] = parsed_args.source_job_id
        if parsed_args.source_job_version:
            attrs['source_job_version'] = parsed_args.source_job_version
        if parsed_args.source_type:
            attrs['source_type'] = parsed_args.source_type
        if parsed_args.model_type:
            attrs['model_type'] = parsed_args.model_type
        if parsed_args.runtime:
            attrs['runtime'] = parsed_args.runtime
        if parsed_args.description:
            attrs['description'] = parsed_args.description
        if parsed_args.execution_code:
            attrs['execution_code'] = parsed_args.execution_code
        if parsed_args.input_params:
            attrs['input_params'] = parsed_args.input_params
        if parsed_args.output_params:
            attrs['output_params'] = parsed_args.output_params
        if parsed_args.dependencies:
            attrs['dependencies'] = parsed_args.dependencies
        if parsed_args.model_algorithm:
            attrs['model_algorithm'] = parsed_args.model_algorithm
        if parsed_args.model_metrics:
            attrs['model_metrics'] = parsed_args.model_metrics
        if parsed_args.apis:
            attrs['apis'] = parsed_args.apis
        if parsed_args.initial_config:
            attrs['initial_config'] = parsed_args.initial_config
        if parsed_args.workspace_id:
            attrs['workspace_id'] = parsed_args.workspace_id
        if parsed_args.model_docs:
            attrs['model_docs'] = parsed_args.model_docs
        if parsed_args.install_type:
            attrs['install_type'] = parsed_args.install_type
        obj = client.create_model(**attrs)

        display_columns, columns = _get_columns(obj)
        data = utils.get_item_properties(obj, columns)

        return (display_columns, data)


class ShowModel(command.ShowOne):
    _description = _('Show details of a modelarts model')

    def get_parser(self, prog_name):
        parser = super(ShowModel, self).get_parser(prog_name)
        parser.add_argument(
            'model_id',
            metavar='<model_id>',
            help=_('Enter model id')
        )
        return parser

    def take_action(self, parsed_args):
        client = self.app.client_manager.modelarts

        data = client.show_model(
            model=parsed_args.model_id,
        )

        display_columns, columns = _get_columns(data)
        data = utils.get_item_properties(data, columns)

        return (display_columns, data)


class Models(command.Lister):
    _description = _('Get properties of a model')
    columns = (
        'model_id',
        'model_name',
        'model_version',
        'model_size',
        'description',
        'dimensions',
        'metric_name',
        'unit',
    )

    table_columns = (
        'model_name',
        'dimensions.name',
        'dimensions.value',
        'metric_name',
        'unit',
    )

    def get_parser(self, prog_name):
        parser = super(Models, self).get_parser(prog_name)
        return parser

    def take_action(self, parsed_args):
        client = self.app.client_manager.modelarts

        query = {}

        data = client.models(**query)

        table = (self.columns,
                 (utils.get_dict_properties(
                     _flatten_output(s), self.columns
                 ) for s in data))
        return table

#   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.
#
'''ModelArts model v2 action implementations'''
import logging

from osc_lib import utils
from osc_lib.command import command
from otcextensions.common import sdk_utils

from otcextensions.i18n import _

LOG = logging.getLogger(__name__)


def _flatten_output(obj):
    data = {
        'model_name': obj.model_name,
        'model_type': obj.model_type,
        'model_version': obj.model_version,
        'model_id': obj.model_id,
        'model_size': obj.model_size,
        'description': obj.description
    }
    return data


def _get_columns(item):
    column_map = {
    }
    return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)


class DeleteModel(command.Command):
    _description = _('Delete ModelArts Model')

    def get_parser(self, prog_name):
        parser = super(DeleteModel, self).get_parser(prog_name)
        parser.add_argument(
            'model_id',
            metavar='<model_id>',
            help=_('Name of the model to delete.')
        )
        return parser

    def take_action(self, parsed_args):
        client = self.app.client_manager.modelarts
        client.delete_model(model=parsed_args.model_id, ignore_missing=False)


class CreateModel(command.ShowOne):
    _description = _('Create a ModelArts model')

    def get_parser(self, prog_name):
        parser = super(CreateModel, self).get_parser(prog_name)
        parser.add_argument(
            '--model_name',
            metavar='<model_name>',
            required=True,
            help=_('Model name.'
                   'Model name. The value can contain 1 to 64 visible characters, ',
                   'including Chinese characters. Only letters, Chinese characters, digits, '
                   'hyphens (-), and underscores (_) are allowed.')
        )
        parser.add_argument(
            '--model_version',
            metavar='<model_version>',
            required=True,
            help=_('Model version in the format of Digit.Digit.Digit. ',
                   'The value range of the digits is [1, 99]. ',
                   'Note that no part of the version number can start with 0. ',
                   'For example, 01.01.01 is not allowed.')

        )
        parser.add_argument(
            '--source_location',
            metavar='<source_location>',
            required=True,
            help=_('OBS path where the model is located or the template address of the SWR image')
        )
        parser.add_argument(
            '--source_job_id',
            metavar='<source_job_id>',
            help=_('ID of the source training job. If the model is generated from a training job, ',
                   'input this parameter for source tracing.',
                   ' If the model is imported from a third-party meta model, leave this parameter blank. ',
                   'By default, this parameter is left blank. ')
        )
        parser.add_argument(
            '--source_job_version',
            metavar='<source_job_version>',
            help=_('Version of the source training job. If the model is generated from a training job, '
                   'input this parameter for source tracing. '
                   'If the model is imported from a third-party meta model, leave this parameter blank. '
                   'By default, this parameter is left blank.')
        )

        parser.add_argument(
            '--source_type',
            metavar='<source_type>',
            help=_('Model source type. Currently, the value can only be auto, ',
                   'which indicates ExeML models (model download is not supported). ',
                   'If the model is deployed by a training job, leave this parameter blank.',
                   ' By default, this parameter is left blank.')
        )
        parser.add_argument(
            '--model_type',
            metavar='<model_type>',
            required=True,
            help=_('Model type. The value can be TensorFlow, MXNet, Caffe, Spark_MLlib, ',
                   'Scikit_Learn, XGBoost, Image, or PyTorch, which is read from the configuration file.')
        )
        parser.add_argument(
            '--runtime',
            metavar='<runtime>',
            help=_('Model running environment. The possible values of runtime are related to model_type.')
        )
        parser.add_argument(
            '--description',
            metavar='<description>',
            help=_('Model remarks. The value contains a maximum of 100 characters and ',
                   'cannot contain the following special characters and more: &!\'\"<>= ')
        )
        parser.add_argument(
            '--execution_code',
            metavar='<execution_code>',
            help=_('OBS path for storing the execution code. By default, this parameter is left blank.'
                   ' The name of the execution code file is fixed to customize_service.py. ')
        )
        parser.add_argument(
            '--input_params',
            metavar='<input_params>',
            help=_('Collection of input parameters of a model. By default, this parameter is left blank.')
        )
        parser.add_argument(
            '--output_params',
            metavar='<output_params>',
            help=_('Collection of output parameters of a model. By default, this parameter is left blank.')
        )
        parser.add_argument(
            '--dependencies',
            metavar='<dependencies>',
            help=_('Package required for inference code and model. By default, this parameter is left blank.')
        )
        parser.add_argument(
            '--model_algorithm',
            metavar='<model_algorithm>',
            help=_('Model algorithm. If the algorithm is read from the configuration file, '
                   'this parameter can be left blank. For example, the value can be predict_analysis,'
                   ' object_detection, or image_classification. ')
        )
        parser.add_argument(
            '--model_metrics',
            metavar='<model_metrics>',
            help=_('Model precision, which is read from the configuration file ')
        )
        parser.add_argument(
            '--apis',
            metavar='<apis>',
            help=_('All apis input and output parameters of the model. '
                   'If the parameters are read from the configuration file, this parameter can be left blank.')
        )
        parser.add_argument(
            '--initial_config',
            metavar='<initial_config>',
            help=_('Character string converted from the final model configuration file. '
                   'It is recommended that the initial_config file be used to provide information'
                   ' about the fields such as apis, dependencies, input_params, and output_params.')
        )
        parser.add_argument(
            '--workspace_id',
            metavar='<workspace_id>',
            help=_('Workspace ID. Default value: 0')
        )
        parser.add_argument(
            '--model_docs',
            metavar='<model_docs>',
            help=_('List of model description documents. A maximum of three documents are supported.')
        )
        parser.add_argument(
            '--install_type',
            metavar='<install_type>',
            help=_('Deployment type. Only lowercase letters are supported. '
                   'The value can be real-time, or batch. Default value: ["real-time","batch"]')
        )
        return parser

    def take_action(self, parsed_args):

        client = self.app.client_manager.modelarts

        attrs = {}

        if parsed_args.model_name:
            attrs['model_name'] = parsed_args.model_name
        if parsed_args.model_version:
            attrs['model_version'] = parsed_args.model_version
        if parsed_args.source_location:
            attrs['source_location'] = parsed_args.source_location
        if parsed_args.source_job_id:
            attrs['source_job_id'] = parsed_args.source_job_id
        if parsed_args.source_job_version:
            attrs['source_job_version'] = parsed_args.source_job_version
        if parsed_args.source_type:
            attrs['source_type'] = parsed_args.source_type
        if parsed_args.model_type:
            attrs['model_type'] = parsed_args.model_type
        if parsed_args.runtime:
            attrs['runtime'] = parsed_args.runtime
        if parsed_args.description:
            attrs['description'] = parsed_args.description
        if parsed_args.execution_code:
            attrs['execution_code'] = parsed_args.execution_code
        if parsed_args.input_params:
            attrs['input_params'] = parsed_args.input_params
        if parsed_args.output_params:
            attrs['output_params'] = parsed_args.output_params
        if parsed_args.dependencies:
            attrs['dependencies'] = parsed_args.dependencies
        if parsed_args.model_algorithm:
            attrs['model_algorithm'] = parsed_args.model_algorithm
        if parsed_args.model_metrics:
            attrs['model_metrics'] = parsed_args.model_metrics
        if parsed_args.apis:
            attrs['apis'] = parsed_args.apis
        if parsed_args.initial_config:
            attrs['initial_config'] = parsed_args.initial_config
        if parsed_args.workspace_id:
            attrs['workspace_id'] = parsed_args.workspace_id
        if parsed_args.model_docs:
            attrs['model_docs'] = parsed_args.model_docs
        if parsed_args.install_type:
            attrs['install_type'] = parsed_args.install_type
        obj = client.create_model(**attrs)

        display_columns, columns = _get_columns(obj)
        data = utils.get_item_properties(obj, columns)

        return (display_columns, data)


class ShowModel(command.ShowOne):
    _description = _('Show details of a modelarts model')

    def get_parser(self, prog_name):
        parser = super(ShowModel, self).get_parser(prog_name)
        parser.add_argument(
            'model_id',
            metavar='<model_id>',
            help=_('Enter model id')
        )
        return parser

    def take_action(self, parsed_args):
        client = self.app.client_manager.modelarts

        data = client.show_model(
            model=parsed_args.model_id,
        )

        display_columns, columns = _get_columns(data)
        data = utils.get_item_properties(data, columns)

        return (display_columns, data)


class Models(command.Lister):
    _description = _('Get properties of a model')
    columns = (
        'model_id',
        'model_name',
        'model_version',
        'model_size',
        'description',
        'dimensions',
        'metric_name',
        'unit',
    )

    table_columns = (
        'model_name',
        'dimensions.name',
        'dimensions.value',
        'metric_name',
        'unit',
    )

    def get_parser(self, prog_name):
        parser = super(Models, self).get_parser(prog_name)
        return parser

    def take_action(self, parsed_args):
        client = self.app.client_manager.modelarts

        query = {}

        data = client.models(**query)

        table = (self.columns,
                 (utils.get_dict_properties(
                     _flatten_output(s), self.columns
                 ) for s in data))
        return table

#   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. # ModelArts model v2 action implementations