# Copyright (c) 2015 The New Mexico Consortium
#
# {{{NMC-LICENSE
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
# }}}

# Flask imports
from flask import Flask, jsonify
from flask.ext.restful import Api, Resource, reqparse

# nmc_probe imports
from nmc_probe.lun_clone_job import LUNCloneJob
from nmc_probe.log import Log

# other imports
import traceback


class LUNCloneTest(Resource):
    def get(self):
        return {'status': 'ok',
                'message': 'test'}


class LUNClone(Resource):
    def create_task(self, job_type_name):
        try:
            args = self.array_parser.parse_args()
            details = LUNCloneJob.with_args(args, job_type_name)
            Log.info('%s job %s added to queue' % (job_type_name, details.id))
            return {'status': 'ok', 'job_id': details.id}, 201
        except (Exception), e:
            Log.error('%s:\n%s' % (str(e), traceback.format_exc()))
            return {'status': str(e),
                    'stacktrace': traceback.format_exc()}, 501

    @property
    def array_parser(self):
        if getattr(self, '_array_parser', None) is None:
            parser = reqparse.RequestParser()
            parser.add_argument('clones', type=list, location='json')
            self._array_parser = parser
        return self._array_parser

    def post(self):
        return self.create_task(u'create')

    def delete(self):
        return self.create_task(u'delete')

    # The next two properties are kept for historical reasons. Ideally,
    # reqparse would validate each entry in the array. However, tutorials for
    # reqparse only show simple examples and the reqparse docs are designed for
    # somebody who already knows to how use reqparse.
    #
    # These two properties are here in the off chance a nested parser is
    # created
    @property
    def delete_parser(self):
        if getattr(self, '_delete_parser', None) is None:
            self._delete_parser = reqparse.RequestParser()
            self._delete_parser.add_argument('dst', type=str, location='json')
            self._delete_parser.add_argument('target_wwn', type=str,
                                             location='json')
            self._delete_parser.add_argument('deleteClones', type=str,
                                             location='json')
        return self._delete_parser

    @property
    def create_parser(self):
        if getattr(self, '_create_parser', None) is None:
            parser = reqparse.RequestParser()
            parser.add_argument('src', type=str, location='json')
            parser.add_argument('dst', type=str, location='json')
            parser.add_argument('properties', type=dict, location='json')
            parser.add_argument('createParent', type=bool, location='json')
            parser.add_argument('wwn', type=str, location='json')
            parser.add_argument('initiators', type=list, location='json')
            self._create_parser = parser

        return self._create_parser


class LUNCloneStatus(Resource):
    def get(self, id):
        try:
            results = LUNCloneJob.get_job(id)

            if results.count() == 0:
                return {'status': 'error', 'reason': 'unknown job id'}, 501

            if results.count() > 1:
                return {'status': 'error',
                        'reason': 'too many jobs match this id'}, 501

            job = results.one()

            retval = {
                'status':   '%s' % job.status.code,
                'job_type': '%s' % job.job_type.code,
                'num_requested': job.num_requested,
                'num_clones': job.num_clones,
                'num_targets': job.num_targets,
                'num_completed': job.num_completed,
                'percent_completed': job.percent_completed,
                'job_time': job.job_time,
                'clone_time': job.clone_time,
                'target_time': job.target_time,
                'udevd_time': job.udevd_time,
                'lag_time': job.lag_time,
            }

            if job.status == 'failed':
                if job.error is not None:
                    if job.error != '':
                        retval['error'] = job.error

            return retval, 201

        except (Exception), e:
            Log.error('%s:\n%s' % (str(e), traceback.format_exc()))
            return {'status': str(e),
                    'stacktrace': traceback.format_exc()}, 501


class LUNCloneRepeat(Resource):
    def post(self):
        args = self.post_parser.parse_args()
        results = LUNCloneJob.get_job(args['id'])

        if results.count() == 0:
            return {'status': 'error', 'reason': 'unknown job id'}, 501

        if results.count() > 1:
            return {'status': 'error',
                    'reason': 'too many jobs match this id'}, 501

        orig = results.one()
        details = LUNCloneJob.repeat(orig)

        if details is not None:
            return {'status': 'ok', 'job_id': details.id}, 201

        return {'status': 'error creating duplicate job'}, 501

    @property
    def post_parser(self):
        if getattr(self, '_post_parser', None) is None:
            parser = reqparse.RequestParser()
            parser.add_argument('id', type=str, location='json',
                                required=True, help='id is required')
            self._post_parser = parser
        return self._post_parser
