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

import datetime
import itertools
import logging
import os
import re
import urllib.parse

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


class Backlog(Kanbanara):

    @cherrypy.expose
    def backlogsorter(self):
        """Allows the cards in the backlog state to be sorted by priority and severity"""
        content = []
        Kanbanara.check_authentication(f'/{self.component}/backlogsorter')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content.append(Kanbanara.header(self, 'backlogsorter', "Backlog Sorter"))
        content.append(Kanbanara.filter_bar(self, 'backlogsorter'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'backlogsorter',
                                                              'Backlog Sorter'))
        content.append('<div id="kanbanboardcontainer"><table id="kanbanboard"><thead><tr>')
        for priority in reversed(self.priorities):
            content.append('<th id="backlogsorter'+priority+'" colspan="4" title="'+priority.capitalize()+' Priority">'+
                           priority.capitalize()+'</th>')

        content.append('</tr><tr><tr>')
        for priority, severity in itertools.product(reversed(self.priorities), reversed(self.severities)):
            content.append('<th id="backlogcolumn'+priority+severity+'" title="'+severity.capitalize()+' Severity">'+
                           severity.capitalize()+'</th>')

        content.append('</tr></thead><tbody><tr>')
        for priority, severity in itertools.product(reversed(self.priorities), reversed(self.severities)):
            content.append('<td id="backlog'+priority+severity+'container">')
            content.append('<div id="backlog'+priority+severity+'">')
            content.append('<!-- This section is automatically populated by JQuery and Ajax -->')
            content.append('<p>Please enable Javascript!</p></div></td>')

        content.append('</tr></tbody></table></div></div>')
        for script_file in ['kanban.js', 'backlogsorter.js']:
            content.append('<script type="text/javascript" src="/scripts/'+script_file+'"></script>')

        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def dropped_on_backlogcriticalcritical(self, doc_id=""):
        """Called when card dropped on critical priority critical severity section on backlogsorter
        """
        self.dropped_on_backlog_priority_severity('critical', 'critical', doc_id)
        return self.populate_backlog_kanban_column('critical', 'critical')

    @cherrypy.expose
    def dropped_on_backlogcriticalhigh(self, doc_id=""):
        """Called when card dropped on critical priority high severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('critical', 'high', doc_id)
        return self.populate_backlog_kanban_column('critical', 'high')

    @cherrypy.expose
    def dropped_on_backlogcriticallow(self, doc_id=""):
        """Called when card dropped on critical priority low severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('critical', 'low', doc_id)
        return self.populate_backlog_kanban_column('critical', 'low')

    @cherrypy.expose
    def dropped_on_backlogcriticalmedium(self, doc_id=""):
        """Called when card dropped on critical priority medium severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('critical', 'medium', doc_id)
        return self.populate_backlog_kanban_column('critical', 'medium')

    @cherrypy.expose
    def dropped_on_backloghighcritical(self, doc_id=""):
        """Called when card dropped on high priority critical severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('high', 'critical', doc_id)
        return self.populate_backlog_kanban_column('high', 'critical')

    @cherrypy.expose
    def dropped_on_backloghighhigh(self, doc_id=""):
        """Called when card dropped on high priority high severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('high', 'high', doc_id)
        return self.populate_backlog_kanban_column('high', 'high')

    @cherrypy.expose
    def dropped_on_backloghighlow(self, doc_id=""):
        """Called when card dropped on high priority low severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('high', 'low', doc_id)
        return self.populate_backlog_kanban_column('high', 'low')

    @cherrypy.expose
    def dropped_on_backloghighmedium(self, doc_id=""):
        """Called when card dropped on high priority medium severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('high', 'medium', doc_id)
        return self.populate_backlog_kanban_column('high', 'medium')

    @cherrypy.expose
    def dropped_on_backloglowcritical(self, doc_id=""):
        """Called when card dropped on low priority critical severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('low', 'critical', doc_id)
        return self.populate_backlog_kanban_column('low', 'critical')

    @cherrypy.expose
    def dropped_on_backloglowhigh(self, doc_id=""):
        """Called when card dropped on low priority high severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('low', 'high', doc_id)
        return self.populate_backlog_kanban_column('low', 'high')

    @cherrypy.expose
    def dropped_on_backloglowlow(self, doc_id=""):
        """Called when card dropped on low priority low severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('low', 'low', doc_id)
        return self.populate_backlog_kanban_column('low', 'low')

    @cherrypy.expose
    def dropped_on_backloglowmedium(self, doc_id=""):
        """Called when card dropped on low priority medium severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('low', 'medium', doc_id)
        return self.populate_backlog_kanban_column('low', 'medium')

    @cherrypy.expose
    def dropped_on_backlogmediumcritical(self, doc_id=""):
        """Called when card dropped on medium priority critical severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('medium', 'critical', doc_id)
        return self.populate_backlog_kanban_column('medium', 'critical')

    @cherrypy.expose
    def dropped_on_backlogmediumhigh(self, doc_id=""):
        """Called when card dropped on medium priority high severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('medium', 'high', doc_id)
        return self.populate_backlog_kanban_column('medium', 'high')

    @cherrypy.expose
    def dropped_on_backlogmediumlow(self, doc_id=""):
        """Called when card dropped on medium priority low severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('medium', 'low', doc_id)
        return self.populate_backlog_kanban_column('medium', 'low')

    @cherrypy.expose
    def dropped_on_backlogmediummedium(self, doc_id=""):
        """Called when card dropped on medium priority medium severity section on backlogsorter"""
        self.dropped_on_backlog_priority_severity('medium', 'medium', doc_id)
        return self.populate_backlog_kanban_column('medium', 'medium')

    @cherrypy.expose
    def dropped_on_step_expedite(self, step_no, step_role, doc_id):
        """Called when card dropped on a column's expedite section on kanban board"""
        username = Kanbanara.check_authentication(f'/{self.component}')
        if step_no and doc_id:
            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)
            project = member_document.get('project', '')
            project_document = self.projects_collection.find_one({"project": project})
            workflow = project_document.get('workflow', [])
            step_document = workflow[int(step_no)]
            main_or_counterpart_column = step_document[step_role+'column']
            state = main_or_counterpart_column['state']
            for card_document in self.cards_collection.find({"_id": ObjectId(doc_id)}):
                card_document['statehistory'] = self.append_state_history(card_document['statehistory'],
                                                                          state, username)
                card_document['state'] = state
                self.cards_collection.save(card_document)
                self.add_recent_activity_entry((datetime.datetime.utcnow(), username, doc_id, 'moved to '+state+' expedite'))
                break

        return self.step_main_expedite(-1, step_no)

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

        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'
                           )

        # 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']

    @cherrypy.expose
    def dropped_on_backlog_priority_severity(self, priority="", severity="", doc_id=""):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}')
        if doc_id:
            card_document = self.cards_collection.find_one({"_id": ObjectId(doc_id)})
            if card_document:
                card_document['statehistory'] = self.append_state_history(card_document['statehistory'],
                                                                          'backlog', username)
                if priority:
                    card_document['priority'] = priority
                else:
                    priority = card_document['priority']

                if severity:
                    card_document['severity'] = severity
                else:
                    severity = card_document['severity']

                self.cards_collection.save(card_document)
                self.add_recent_activity_entry((datetime.datetime.utcnow(), username, doc_id,
                                                'moved to backlog '+priority+' '+severity))

    @cherrypy.expose
    def populate_backlog_kanban_column(self, priority, severity):
        """comment"""
        content = []
        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)
        states = self.get_custom_states_mapped_onto_metastates(['backlog'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        if member_document and 'card' in member_document and member_document['card']:
            hierarchical_ids = self.find_all_hierarchical_ids(member_document['card'],
                                                              [member_document['card']])
            owner_reviewer_search_criteria['id'] = {"$in": hierarchical_ids}

        owner_reviewer_search_criteria['priority'] = priority
        owner_reviewer_search_criteria['severity'] = severity
        if self.cards_collection.find(owner_reviewer_search_criteria).count():
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria).sort("lastchanged",
                                                                                                pymongo.DESCENDING):
                content.append(self.assemble_kanban_card(['owner', 'coowner',
                                                          'reviewer', 'coreviewer'],
                                                         ['updateable'], -1, card_document['_id'],
                                                         False, 0))

            content.append('<script type="text/javascript" src="/scripts/kanban.js"></script>')

        else:
            content.append('<span class="ui-icon ui-icon-info" title="There are no '+priority+' priority and '+severity+' severity cards!" />')

        content.append('<script type="text/javascript" src="/scripts/backlog'+priority+severity+'.js"></script>')
        return "".join(content)

    @cherrypy.expose
    def index(self):
        """Redirects you to the kanban board"""
        raise cherrypy.HTTPRedirect("/kanban", 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(Backlog(), '/backlog', config=conf)
