# Kanbanara Sync Component
# Written by Rebecca Shalfield between 2013 and 2017
# Copyright (c) Rebecca Shalfield 2013-2017
# Released under the GNU AGPL v3

'''Kanbanara's Sync Component'''

import copy
import datetime
import logging
import os
import re
from socket import timeout
import urllib
import urllib.parse
import urllib.request

from bson import ObjectId
import cherrypy
from kanbanara import Kanbanara
from pymongo import MongoClient


class Sync(Kanbanara):
    '''Kanbanara's Sync Component'''

    def __init__(self):
        """Initialisation"""
        self.component = 'kanban'

        Kanbanara.__init__(self)

        self.current_dir = os.path.dirname(os.path.abspath(__file__))

        logging.basicConfig(level=logging.DEBUG,
                            format='%(asctime)s - %(levelname)s - %(message)s',
                            filename=os.path.join(self.current_dir, '..', 'logs', 'kanbanara.log'),
                            filemode='w'
                           )

        self.housekeeping_last_done = 0

        # Initialisation Settings
        self.mongodb_host, self.mongodb_port, self.mongodb_username, self.mongodb_password, self.mongodb_bindir = Kanbanara.read_mongodb_ini_file(self)

        # Connect to MongoDB on given host and port
        if self.mongodb_username and self.mongodb_password:
            modified_username = urllib.parse.quote_plus(self.mongodb_username)
            modified_password = urllib.parse.quote_plus(self.mongodb_password)
            connection = MongoClient('mongodb://' + modified_username + ':' + modified_password +
                                     '@' + self.mongodb_host + ':' + str(self.mongodb_port))
        else:
            connection = MongoClient(self.mongodb_host, self.mongodb_port)

        # Connect to 'kanbanara' database, creating if not already exists
        kanbanara_db = connection['kanbanara']

        # Connect to 'projects' collection
        self.projects_collection = kanbanara_db['projects']
        for attribute in ['project']:
            self.projects_collection.create_index(attribute, unique=True, background=True)

        # Connect to 'sessions' collection
        self.sessions_collection = kanbanara_db['sessions']
        for attribute in ['session_id']:
            self.sessions_collection.create_index(attribute, unique=True, background=True)

        for attribute in ['lastaccess']:
            self.sessions_collection.create_index(attribute, unique=False, background=True)

        # Connect to 'members' collection
        self.members_collection = kanbanara_db['members']
        for attribute in ['username']:
            # TODO - username attribute should be unique but get error when unique=true set
            self.members_collection.create_index(attribute, unique=False, background=True)

        # Connect to 'cards' collection
        self.cards_collection = kanbanara_db['cards']

        self.recent_activities = []

    @cherrypy.expose
    def index(self):
        """Redirects you to the kanban board"""
        raise cherrypy.HTTPRedirect("/kanban", 302)

    def synchronise_slave_from_master(self, project_document, master_host, master_port):
        """Synchronise the slave from the master"""
        # TODO - Need to get card, member and project documents to slave from master
        True

    def synchronise_projects(self, projects=[]):
        """Synchronise all slave projects that have reached their next synchronisation time"""
        datetime_now = datetime.datetime.utcnow()
        if not projects:
            projects = self.projects_collection.find().distinct('project')

        for project_document in self.projects_collection.find(
                {'project': {'$in': projects},
                 'role': 'slave',
                 'next_synchronisation': {'$lte': datetime_now}
                }):
            master_host = project_document.get('master_host', '')
            master_port = project_document.get('master_port', '')
            if master_host and master_port:
                self.synchronise_project(project_document, master_host, master_port)

    def synchronise_project(self, project_document, master_host, master_port):
        """Synchronise an individual slave project that has reached its next synchronisation time"""
        #self.synchronise_slave_from_master(project_document, master_host, master_port)
        self.synchronise_master_from_slave(project_document, master_host, master_port)
        synchronisation_period = project_document.get('synchronisation_period', self.potential_synchronisation_periods[-1])
        project_document['next_synchronisation'] = datetime.datetime.utcnow() + self.synchronisation_periods[synchronisation_period]
        self.projects_collection.save(project_document)

    def synchronise_master_from_slave(self, project_document, master_host, master_port):
        """Synchronise the master from the slave"""
        project = project_document.get('project', '')
        if project:
            synchronised_upto = project_document.get('synchronised_upto', 0)
            for doc_id in self.cards_collection.find(
                    {"project": project,
                     "lastchanged": {'$gt': synchronised_upto}
                    }).distinct('_id'):
                #self.synchronise_card(doc_id, '')
                True

    @cherrypy.expose
    def synchronise_card(self, doc_id, destination=""):
        """Called from synchronise_master_from_slave() to synchronise each card in a given project
           or called from the Synchronise option on a card's menu to synchronise an individual card
        """
        for card_document in self.cards_collection.find({"_id": ObjectId(doc_id),
                                                         "project": {'$exists': True}}):
            copy_of_card_document = copy.copy(card_document)
            for project_document in self.projects_collection.find({'project': card_document['project']}):
                master_host = project_document.get('master_host', '')
                master_port = project_document.get('master_port', '')
                url = 'http://'+master_host+':'+master_port+'/kanban/api_synchronise_card'
                headers = {'User-Agent': 'Kanbanara'}
                del copy_of_card_document['_id']
                card_document_as_json = self.dictionary_as_json('file', copy_of_card_document, 0)
                values = {'data': card_document_as_json}
                data = urllib.parse.urlencode(values)
                # TODO - This needs to use POST instead of GET
                try:
                    with urllib.request.urlopen(url+'?'+data, timeout=10) as response:
                        assigned_id = response.read().decode("utf-8")
                        if response.status == 200 and response.reason == 'OK':
                            if not self.cards_collection.find({"id": assigned_id}).count():
                                if 'hierarchy' in card_document:
                                    card_document['hierarchy'] = card_document['hierarchy'].replace(card_document['id'], assigned_id)

                                card_document['id'] = assigned_id
                                #try:
                                self.cards_collection.save(card_document)
                                #except DuplicateKeyError:
                                #    # TODO Handle this better!
                                #    pass

                except urllib.error.HTTPError as error:
                    logging.debug(error.code)
                    logging.debug(error.read())
                except urllib.error.URLError as error:
                    print(error.reason)
                except timeout:
                    pass
                else:
                    pass

                break

            break

        if destination:
            raise cherrypy.HTTPRedirect("/kanban/"+destination, 302)

    @cherrypy.expose
    def synchronise_member_projects(self):
        """called by 'Synchronise' main menu bar option"""
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        member_document = Kanbanara.get_member_document(self, session_document)
        if 'project' in member_document and member_document['project']:
            projects = [member_document['project']]
        else:
            projects = self.get_member_projects(member_document)

        self.synchronise_projects(projects)
        raise cherrypy.HTTPRedirect("/kanban/index", 302)

CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
conf = {'/': {'tools.staticdir.root':   CURRENT_DIR,
              'tools.sessions.on':      True,
              'tools.sessions.locking': 'explicit'
             }}
for directory in ['css', 'images']:
    if os.path.exists(CURRENT_DIR+os.sep+directory):
        conf['/'+directory] = {'tools.staticdir.on':  True,
                               'tools.staticdir.dir': directory}

cherrypy.tree.mount(Sync(), '/sync', config=conf)
