# Copyright (C) 2014 Linaro Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""The RequestHandler for /job URLs."""

import json
import tornado

from functools import partial
from tornado.web import asynchronous

from handlers.base import BaseHandler
from models.job import JOB_COLLECTION
from models.defconfig import DEFCONFIG_COLLECTION
from models.subscription import SUBSCRIPTION_COLLECTION
from utils.db import (
    delete,
    find_one,
)
from taskqueue.tasks import (
    send_emails,
    import_job,
)


class JobHandler(BaseHandler):
    """Handle the /job URLs."""

    @property
    def collection(self):
        return self.db[JOB_COLLECTION]

    def _valid_keys(self, method):
        valid_keys = {
            'POST': ['job', 'kernel'],
            'DELETE': ['job'],
        }

        return valid_keys.get(method, None)

    @asynchronous
    def post(self, *args, **kwargs):

        self._check_content_type()

        json_obj = json.loads(self.request.body.decode('utf8'))
        if self._has_valid_keys(json_obj, self._valid_keys('POST')):
            import_job.apply_async([json_obj], link=send_emails.s())
            self._create_valid_response(200)
        else:
            self.send_error(status_code=400)

    @asynchronous
    def delete(self, *args, **kwargs):

        self._check_content_type()

        json_obj = json.loads(self.request.body.decode('utf8'))
        if self._has_valid_keys(json_obj, self._valid_keys('DELETE')):
            self.executor.submit(
                partial(self._delete_job, json_obj['job'])
            ).add_done_callback(
                lambda future: tornado.ioloop.IOLoop.instance().add_callback(
                    partial(self._create_valid_response, future.result())
                )
            )
        else:
            self.send_error(status_code=400)

    def _delete_job(self, job_id):
        """Delete a job from the database.

        Use with care since documents cannot be retrieved after!

        Removing a job from the collection means to remove also all the
        other documents associated with the it: defconfig and subscription.

        :param job_id: The ID of the job to remove.
        :return Whatever is returned by the `delete` function.
        """
        # TODO: maybe look into two-phase commits in mongodb
        # http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/

        ret_val = 200

        if find_one(self.collection, job_id):
            delete(
                self.db[DEFCONFIG_COLLECTION],
                {'job_id': {'$in': [job_id]}}
            )

            delete(
                self.db[SUBSCRIPTION_COLLECTION],
                {'job_id': {'$in': [job_id]}}
            )

            ret_val = delete(self.collection, job_id)
        else:
            ret_val = 404

        return ret_val

@@ -729,20 +729,8 @@
 %22%22%0A%0A
-import json%0A
 impo
@@ -774,45 +774,8 @@
 tial
-%0Afrom tornado.web import asynchronous
 %0A%0Afr
@@ -1404,34 +1404,16 @@
 e)%0A%0A
-    @asynchronous%0A
     def 
 post
@@ -1408,16 +1408,17 @@
     def 
+_
 post(sel
@@ -1424,200 +1424,19 @@
 lf, 
-*args, **kwargs):%0A%0A        self._check_content_type()%0A%0A        json_obj = json.loads(self.request.body.decode('utf8'))%0A        if self._has_valid_keys(json_obj, self._valid_keys('POST')):%0A    
+json_obj):%0A
     
@@ -1500,28 +1500,24 @@
 ())%0A        
-    
 self._create
@@ -1541,85 +1541,8 @@
 00)%0A
-        else:%0A            self.send_error(status_code=400)%0A%0A    @asynchronous
 %0A   
@@ -1546,16 +1546,17 @@
     def 
+_
 delete(s
@@ -1564,202 +1564,19 @@
 lf, 
-*args, **kwargs):%0A%0A        self._check_content_type()%0A%0A        json_obj = json.loads(self.request.body.decode('utf8'))%0A        if self._has_valid_keys(json_obj, self._valid_keys('DELETE')):%0A    
+json_obj):%0A
     
@@ -1605,36 +1605,32 @@
 it(%0A            
-  
-  
 partial(self._de
@@ -1655,29 +1655,16 @@
 %5B'job'%5D)
-%0A            
 ).add_do
@@ -1706,16 +1706,32 @@
  future:
+%0A               
  tornado
@@ -1843,16 +1843,17 @@
 esult())
+)
 %0A       
@@ -1861,85 +1861,8 @@
     
-    )%0A            )%0A        else:%0A            self.send_error(status_code=400
 )%0A%0A 

Implement _post and _delete.