import re

import requests
from django.conf import settings
from django.core.cache import cache
from django.utils.encoding import force_str

CACHE_DURATION = 3600


def decorate_commit_line(line):
    escaped_line = line.replace('<', '&lt;').replace('>', '&gt;')
    if line.startswith('<'):
        return '<span class="in">%s</span>' % escaped_line
    elif line.startswith('>'):
        return '<span class="out">%s</span>' % escaped_line
    return '<span class="other">%s</span>' % escaped_line


class Issue:
    def __init__(self, issue_id):
        self.id = issue_id
        self.commits = []

    def update_content(self):
        response = requests.get(
            '%s/issues/%s.json' % (settings.REDMINE_URL, self.id),
            headers={'X-Redmine-API-Key': settings.REDMINE_API_KEY},
            timeout=(10, 10),
        )
        response.raise_for_status()
        content = response.json()
        cache.set('issue-%s' % self.id, content, CACHE_DURATION)

    def get_attribute(self, attr, default=None):
        content = cache.get('issue-%s' % self.id)
        if content:
            return content.get('issue').get(attr, default)
        try:
            self.update_content()
        except requests.RequestException:
            return '---'
        return self.get_attribute(attr, default)

    @property
    def subject(self):
        return self.get_attribute('subject')

    @property
    def closed_on(self):
        if self.get_attribute('closed_on'):
            return self.get_attribute('closed_on')
        # fallback on updated_on, as "resolved (to deploy)" is not considered
        # "closed"
        return self.get_attribute('updated_on')

    @property
    def url(self):
        return '%s/issues/%s' % (settings.REDMINE_URL, self.id)

    @property
    def priority_class(self):
        return 'priority-%s' % self.get_attribute('priority')['name'].lower()

    def add_commit(self, commit):
        self.commits.append(commit)


class CommitAndIssues:
    def __init__(self, oneline):
        self.issues = []
        self.commit = oneline.strip()
        self.commit_html = decorate_commit_line(self.commit)

    @classmethod
    def get_for_commits(cls, module, v1, v2):
        difflog = module.get_diff_log(v1, v2, format='full', grep=r'\#[0-9]\+')
        commits = re.findall(r'^commit.*?\n\n.*?\n', difflog, re.DOTALL | re.MULTILINE)
        oneline_commits = module.get_diff_log(v1, v2, grep=r'\#[0-9]\+').splitlines()

        objects = []
        for commit, oneline in zip(commits, oneline_commits):
            obj = cls(oneline.strip())
            for issue_id in re.findall(r'#(\d+)', commit):
                obj.add_issue(issue_id)
            objects.append(obj)

        return objects

    def add_issue(self, issue_id):
        self.issues.append(Issue(issue_id))


def get_issue_deployment_status(issue_id):
    from .models import InstalledService, Module

    data = {}
    for module in Module.objects.all():
        try:
            git_log = module.get_git_log()
        except OSError:
            continue
        git_log_hashes = [x[0][:7] for x in git_log]
        for line in git_log:
            if not force_str(line[1], 'utf-8', 'ignore').endswith('#%s)' % issue_id):
                continue
            commit_hash = line[0][:7]
            fix_index = git_log_hashes.index(commit_hash)
            data = {'platforms': {}}
            for service in InstalledService.objects.all():
                installed_version = module.get_installed_version(service.platform, service.service)
                if not installed_version or not installed_version.version:
                    continue
                version_number = installed_version.version.version
                if not version_number:
                    continue
                service_name = '%s / %s / %s' % (
                    installed_version.service.platform.project.title,
                    installed_version.service.platform.title,
                    installed_version.service.service.title,
                )
                version_hash = module.get_version_hash(version_number)
                if version_hash not in git_log_hashes:
                    # maybe a hotfix, try with base version number
                    version_hash = module.get_version_hash(version_number.split('.post')[0])
                    if version_hash not in git_log_hashes:
                        # still not there
                        continue
                version_index = git_log_hashes.index(version_hash)
                data['platforms'][service_name] = {
                    'version': version_number,
                    'status': 'ok' if version_index <= fix_index else 'nok',
                }
        if data:
            break
    return data
