"""

"""
import abc
import re
import requests
import termcolor
from requests import RequestException
import six
from six.moves.urllib import parse
from jiracli.cli import colorfunc
from jiracli.utils import COLOR


@six.add_metaclass(abc.ABCMeta)
class JiraBridge(object):

    def __init__(self, base_url, config, persist=True):
        self.base_url = self._check_redirect(base_url)
        self.config = config
        self.persist = persist

    def _check_redirect(self, url):
        try:
            resp = requests.get( url, allow_redirects = False )
            if resp.status_code in [301,302]:
                return resp.headers['location']
        except RequestException:
            return None
        finally:
            return url

    def __repr__(self):
        return "%s(%s)" % (self.__class__.__name__, self.base_url)

    def format_issue(self, issue, mode=0, formatter=None, comments_only=False):
        fields = {}
        status_color = "blue"
        status_from_id = JiraBridge.object_from_key(
            issue.setdefault('status', '1'),
            self.get_statuses
        )
        if not status_from_id:
            status_from_id = self.get_status(issue.setdefault('status', '1'))

        status_string = (status_from_id and status_from_id['name']) or 'unknown'

        status_category = status_from_id.get('statusCategory', {})
        if status_category:
            status_color = status_category.get('colorName')
            if status_color:
                status_color = status_color.split("-")[0]
            if status_color not in termcolor.COLORS:
                status_color = None
        if not status_color:
            if status_string.lower() in ["resolved", "closed", "done"]:
                status_color = "green"
            elif status_string.lower() in ["open", "unassigned", "reopened", "to do"]:
                status_color = "red"
        list_fields = set(['versions', 'fixversions'])

        special_fields = {
            "status": self.get_statuses,
            "priority": self.get_priorities,
            "type": lambda: dict(list(self.get_issue_types().items()) + list(self.get_subtask_issue_types().items()))
        }
        special_field_fallback = {
            "status": self.get_status,
            "type": self.get_issue_type
        }
        if formatter:
            groups = re.compile("(%([\w]+))").findall(formatter)
            ret_str = formatter
            for k, v in groups:
                if v.lower() in special_fields.keys():
                    key = issue[v.lower()]
                    data = "" or JiraBridge.object_from_key(key, special_fields[v.lower()]).get("name")
                    if not data and v.lower() in special_field_fallback:
                        data = special_field_fallback[v.lower()](key).get("name")
                    ret_str = ret_str.replace(k, data)
                elif v.lower() in list_fields:
                    fix_versions = ", ".join(v.name for v in issue.get('fixVersions', []))
                    ret_str = ret_str.replace(k, fix_versions)
                else:
                    ret_str = ret_str.replace(k, str(issue.setdefault(v.lower(), "")))
            return ret_str

        if mode >= 0:
            # minimal
            fields["issue"] = issue["key"]
            fields["status"] = colorfunc(
                JiraBridge.object_from_key(
                    issue["status"],
                    self.get_statuses
                ).get("name", self.get_status(issue["status"])['name'])
                , status_color
            )
            fields["reporter"] = issue.setdefault("reporter","")
            fields["assignee"] = issue.setdefault("assignee","")
            fields["summary"] = issue.setdefault("summary","")
            fields["link"] = colorfunc(
                "%s/browse/%s" % (
                    self.base_url, issue["key"]
                ), "white", attrs=["underline"]
            )
        if mode == 1 or comments_only:
            fields["description"] = issue.setdefault("description","") or ""
            if not issue.get("priority", ""):
                fields["priority"] = ""
            else:
                fields["priority"] = JiraBridge.object_from_key(issue["priority"], self.get_priorities)["name"]
            fields["type"] = JiraBridge.object_from_key(
                issue["type"],
                self.get_issue_types if 'parent' not in issue else self.get_subtask_issue_types
            ).get("name",  self.get_issue_type(issue["type"]))
            fields["comments"] = "\n"
            comments = self.get_issue_comments(issue["key"])
            for comment in comments:
                comment_str =  comment["body"].strip()
                fields["comments"] += "%s %s : %s\n" % ( colorfunc(comment["created"], "blue"), colorfunc(comment["author"], "green"), comment_str )
        children_string = ""
        if mode > 1:
            description = (issue.setdefault("description", "") or "").split("\n")
            fields["description"] = "\n".join([description[0]] + [" "*23 + k for k in description[1:]])

            for child in self.search_issues_jql("parent=%s" % issue["key"]):
                child_type = JiraBridge.object_from_key(child["type"], self.get_subtask_issue_types)["name"].lower()
                key = ("%s" % child_type).ljust(20)
                value = "%s (%s) %s" % (
                    child["key"], child["summary"], colorfunc("%s/browse/%s" % (self.base_url, child["key"]), "white", attrs=['underline'])
                )
                children_string += "%s : %s\n" % (key, value)
        if comments_only:
            return fields["comments"].strip()
        elif mode < 0:
            url_str = colorfunc(parse.urljoin(self.base_url, "/browse/%s" % (issue["key"])), "white", attrs=["underline"])
            ret_str = colorfunc(issue["key"], status_color) + " " + issue.setdefault("summary", "") + " " + url_str
            if not COLOR:
                ret_str += " [%s] " % status_string
            return ret_str
        for k, v in fields.items():
            if not v:
                fields[k] = ""
        formatted = ""
        for k, v in fields.items():
            if not k == 'comments':
                formatted += ' : '.join((k.ljust(20), str(v))) + '\n'
        formatted += children_string
        if "comments" in fields:
            formatted += fields["comments"]
        return formatted

    @staticmethod
    def object_from_key(value, callable, key='id'):
        mapping = callable()
        for k, v in mapping.items():
            if v[key] == value:
                return v
        return {}

    @abc.abstractmethod
    def login(self, username, password):
        raise NotImplementedError

    @abc.abstractmethod
    def ping(self):
        raise NotImplementedError

    @abc.abstractmethod
    def get_filters(self):
        raise NotImplementedError

    @abc.abstractmethod
    def get_projects(self):
        raise NotImplementedError

    @abc.abstractmethod
    def get_components(self, project):
        raise NotImplementedError

    @abc.abstractmethod
    def list_versions(self, project):
        raise NotImplementedError

    @abc.abstractmethod
    def get_priorities(self):
        raise NotImplementedError

    @abc.abstractmethod
    def get_issue_types(self):
        raise NotImplementedError

    @abc.abstractmethod
    def get_issue_type(self, issue_type_id):
        raise NotImplementedError

    @abc.abstractmethod
    def get_subtask_issue_types(self):
        raise NotImplementedError

    @abc.abstractmethod
    def get_statuses(self):
        raise NotImplementedError

    @abc.abstractmethod
    def get_status(self, status_id):
        raise NotImplementedError

    @abc.abstractmethod
    def get_resolutions(self):
        raise NotImplementedError

    @abc.abstractmethod
    def get_issue(self, issue_id):
        raise NotImplementedError

    @abc.abstractmethod
    def update_issue(self, issue_id, **kwargs):
        raise NotImplementedError

    @abc.abstractmethod
    def search_issues(self, free_text, project=None, limit=100):
        raise NotImplementedError

    @abc.abstractmethod
    def search_issues_jql(self, query, limit=100, project=None):
        raise NotImplementedError

    @abc.abstractmethod
    def get_issues_by_filter(self, *filters):
        raise NotImplementedError

    @abc.abstractmethod
    def get_issue_comments(self, issue):
        raise NotImplementedError

    @abc.abstractmethod
    def add_comment(self, issue, comment):
        raise NotImplementedError

    @abc.abstractmethod
    def create_issue(
        self, project, type=0, summary="", description="", priority="minor", parent=None, components={}, **extras
    ):
        raise NotImplementedError

    @abc.abstractmethod
    def get_available_transitions(self, issue):
        raise NotImplementedError

    @abc.abstractmethod
    def transition_issue(self, issue, transition, comment=""):
        raise NotImplementedError

    @abc.abstractmethod
    def assign_issue(self, issue, assignee):
        raise NotImplementedError

    @abc.abstractmethod
    def change_reporter(self, issue, reporter):
        raise NotImplementedError

    @abc.abstractmethod
    def add_versions(self, issue, versions, type):
        raise NotImplementedError

    @abc.abstractmethod
    def remove_versions(self, issue, versions, type):
        raise NotImplementedError

from .rest import JiraRestBridge
from .soap import JiraSoapBridge

def get_bridge(protocol):
    """
    simple factory to get the jira bridge based on the protocol
    """
    return {
        'soap': JiraSoapBridge,
        'rest': JiraRestBridge
    }[protocol]
