#!/usr/bin/env python

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from collections import namedtuple

import pytest

import merge_arrow_pr


FakeIssue = namedtuple('issue', ['fields'])
FakeFields = namedtuple('fields', ['status', 'summary', 'assignee',
                                   'components', 'fixVersions'])
FakeAssignee = namedtuple('assignee', ['displayName'])
FakeStatus = namedtuple('status', ['name'])
FakeComponent = namedtuple('component', ['name'])
FakeVersion = namedtuple('version', ['name', 'raw'])

RAW_VERSION_JSON = [
    {'name': 'JS-0.4.0', 'released': False},
    {'name': '0.11.0', 'released': False},
    {'name': '0.12.0', 'released': False},
    {'name': '0.10.0', 'released': True},
    {'name': '0.9.0', 'released': True}
]


SOURCE_VERSIONS = [FakeVersion(raw['name'], raw)
                   for raw in RAW_VERSION_JSON]

TRANSITIONS = [{'name': 'Resolve Issue', 'id': 1}]

jira_id = 'ARROW-1234'
status = FakeStatus('In Progress')
fields = FakeFields(status, 'issue summary', FakeAssignee('groundhog'),
                    [FakeComponent('C++'), FakeComponent('Format')],
                    [])
FAKE_ISSUE_1 = FakeIssue(fields)


class FakeJIRA:

    def __init__(self, issue=None, project_versions=None, transitions=None,
                 current_fix_versions=None):
        self._issue = issue
        self._project_versions = project_versions
        self._transitions = transitions

    def issue(self, jira_id):
        return self._issue

    def transitions(self, jira_id):
        return self._transitions

    def transition_issue(self, jira_id, transition_id, comment=None,
                         fixVersions=None):
        self.captured_transition = {
            'jira_id': jira_id,
            'transition_id': transition_id,
            'comment': comment,
            'fixVersions': fixVersions
        }

    def get_candidate_fix_versions(self):
        return SOURCE_VERSIONS, ['0.12.0']

    def project_versions(self, project):
        return self._project_versions


class FakeCLI:

    def __init__(self, responses=()):
        self.responses = responses
        self.position = 0

    def prompt(self, prompt):
        response = self.responses[self.position]
        self.position += 1
        return response

    def fail(self, msg):
        raise Exception(msg)


def test_jira_fix_versions():
    jira = FakeJIRA(project_versions=SOURCE_VERSIONS,
                    transitions=TRANSITIONS)

    issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())
    all_versions, default_versions = issue.get_candidate_fix_versions()
    assert all_versions == SOURCE_VERSIONS
    assert default_versions == ['0.11.0']


def test_jira_no_suggest_patch_release():
    versions_json = [
        {'name': '0.11.1', 'released': False},
        {'name': '0.12.0', 'released': False},
    ]

    versions = [FakeVersion(raw['name'], raw) for raw in versions_json]

    jira = FakeJIRA(project_versions=versions, transitions=TRANSITIONS)
    issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())
    all_versions, default_versions = issue.get_candidate_fix_versions()
    assert all_versions == versions
    assert default_versions == ['0.12.0']


def test_jira_parquet_no_suggest_non_cpp():
    # ARROW-7351
    versions_json = [
        {'name': 'cpp-1.5.0', 'released': True},
        {'name': 'cpp-1.6.0', 'released': False},
        {'name': 'cpp-1.7.0', 'released': False},
        {'name': '1.11.0', 'released': False},
        {'name': '1.12.0', 'released': False}
    ]

    versions = [FakeVersion(raw['name'], raw)
                for raw in versions_json]

    jira = FakeJIRA(project_versions=versions, transitions=TRANSITIONS)
    issue = merge_arrow_pr.JiraIssue(jira, 'PARQUET-1713', 'PARQUET',
                                     FakeCLI())
    all_versions, default_versions = issue.get_candidate_fix_versions()
    assert all_versions == versions
    assert default_versions == ['cpp-1.6.0']


def test_jira_invalid_issue():
    class Mock:

        def issue(self, jira_id):
            raise Exception("not found")

    with pytest.raises(Exception):
        merge_arrow_pr.JiraIssue(Mock(), 'ARROW-1234', 'ARROW', FakeCLI())


def test_jira_resolve():
    jira = FakeJIRA(issue=FAKE_ISSUE_1,
                    project_versions=SOURCE_VERSIONS,
                    transitions=TRANSITIONS)

    my_comment = 'my comment'
    fix_versions = [SOURCE_VERSIONS[1].raw]

    issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())
    issue.resolve(fix_versions, my_comment)

    assert jira.captured_transition == {
        'jira_id': 'ARROW-1234',
        'transition_id': 1,
        'comment': my_comment,
        'fixVersions': fix_versions
    }


def test_jira_resolve_non_mainline():
    jira = FakeJIRA(issue=FAKE_ISSUE_1,
                    project_versions=SOURCE_VERSIONS,
                    transitions=TRANSITIONS)

    my_comment = 'my comment'
    fix_versions = [SOURCE_VERSIONS[0].raw]

    issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())
    issue.resolve(fix_versions, my_comment)

    assert jira.captured_transition == {
        'jira_id': 'ARROW-1234',
        'transition_id': 1,
        'comment': my_comment,
        'fixVersions': fix_versions
    }


def test_jira_resolve_released_fix_version():
    # ARROW-5083
    jira = FakeJIRA(issue=FAKE_ISSUE_1,
                    project_versions=SOURCE_VERSIONS,
                    transitions=TRANSITIONS)

    cmd = FakeCLI(responses=['0.9.0'])
    fix_versions_json = merge_arrow_pr.prompt_for_fix_version(cmd, jira)
    assert fix_versions_json == [RAW_VERSION_JSON[-1]]


def test_multiple_authors_bad_input():
    a0 = 'Jimbob Crawfish <jimbob.crawfish@gmail.com>'
    a1 = 'Jarvis McCratchett <jarvis.mccratchett@hotmail.com>'
    a2 = 'Hank Miller <hank.miller@protonmail.com>'
    distinct_authors = [a0, a1]

    cmd = FakeCLI(responses=[''])
    primary_author, new_distinct_authors = merge_arrow_pr.get_primary_author(
        cmd, distinct_authors)
    assert primary_author == a0
    assert new_distinct_authors == [a0, a1]

    cmd = FakeCLI(responses=['oops', a1])
    primary_author, new_distinct_authors = merge_arrow_pr.get_primary_author(
        cmd, distinct_authors)
    assert primary_author == a1
    assert new_distinct_authors == [a1, a0]

    cmd = FakeCLI(responses=[a2])
    primary_author, new_distinct_authors = merge_arrow_pr.get_primary_author(
        cmd, distinct_authors)
    assert primary_author == a2
    assert new_distinct_authors == [a2, a0, a1]


def test_jira_already_resolved():
    status = FakeStatus('Resolved')
    fields = FakeFields(status, 'issue summary', FakeAssignee('groundhog'),
                        [FakeComponent('Java')], [])
    issue = FakeIssue(fields)

    jira = FakeJIRA(issue=issue,
                    project_versions=SOURCE_VERSIONS,
                    transitions=TRANSITIONS)

    fix_versions = [SOURCE_VERSIONS[0].raw]
    issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())

    with pytest.raises(Exception,
                       match="ARROW-1234 already has status 'Resolved'"):
        issue.resolve(fix_versions, "")


def test_no_unset_point_release_fix_version():
    # ARROW-6915: We have had the problem of issues marked with a point release
    # having their fix versions overwritten by the merge tool. This verifies
    # that existing patch release versions are carried over
    status = FakeStatus('In Progress')

    versions_json = {
        '0.14.2': {'name': '0.14.2', 'id': 1},
        '0.15.1': {'name': '0.15.1', 'id': 2},
        '0.16.0': {'name': '0.16.0', 'id': 3},
        '0.17.0': {'name': '0.17.0', 'id': 4}
    }

    fields = FakeFields(status, 'summary', FakeAssignee('someone'),
                        [FakeComponent('Java')],
                        [FakeVersion(v, versions_json[v])
                         for v in ['0.17.0', '0.15.1', '0.14.2']])
    issue = FakeIssue(fields)

    jira = FakeJIRA(issue=issue, project_versions=SOURCE_VERSIONS,
                    transitions=TRANSITIONS)

    issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())
    issue.resolve([versions_json['0.16.0']], "a comment")

    assert jira.captured_transition == {
        'jira_id': 'ARROW-1234',
        'transition_id': 1,
        'comment': 'a comment',
        'fixVersions': [versions_json[v]
                        for v in ['0.16.0', '0.15.1', '0.14.2']]
    }

    issue.resolve([versions_json['0.15.1']], "a comment")

    assert jira.captured_transition == {
        'jira_id': 'ARROW-1234',
        'transition_id': 1,
        'comment': 'a comment',
        'fixVersions': [versions_json[v] for v in ['0.15.1', '0.14.2']]
    }


def test_jira_output_no_components():
    # ARROW-5472
    status = 'Interesting work'
    components = []
    output = merge_arrow_pr.format_jira_output(
        'ARROW-1234', 'Resolved', status, FakeAssignee('Foo Bar'),
        components)

    assert output == """=== JIRA ARROW-1234 ===
Summary\t\tInteresting work
Assignee\tFoo Bar
Components\tNO COMPONENTS!!!
Status\t\tResolved
URL\t\thttps://issues.apache.org/jira/browse/ARROW-1234"""

    output = merge_arrow_pr.format_jira_output(
        'ARROW-1234', 'Resolved', status, FakeAssignee('Foo Bar'),
        [FakeComponent('C++'), FakeComponent('Python')])

    assert output == """=== JIRA ARROW-1234 ===
Summary\t\tInteresting work
Assignee\tFoo Bar
Components\tC++, Python
Status\t\tResolved
URL\t\thttps://issues.apache.org/jira/browse/ARROW-1234"""
