from xmlrpc.client import Fault

import pytest

from utils import make_basic, Commit, to_pr, seen


@pytest.fixture
def repo(env, config, make_repo):
    repo, _ = make_basic(env, config, make_repo, statuses="default")
    return repo

@pytest.fixture
def pr_id(env, repo, config):
    with repo:
        repo.make_commits('c', Commit("c", tree={'x': '1'}), ref='heads/aref')
        pr = repo.make_pr(target='c', head='aref')
        repo.post_status('aref', 'success')
        pr.post_comment('hansen r+', config['role_reviewer']['token'])
    env.run_crons()
    with repo:
        repo.post_status('staging.c', 'success')
    env.run_crons()
    pr_id = to_pr(env, pr)
    assert pr_id.merge_date
    return pr_id

@pytest.fixture
def backport_id(env, pr_id):
    action = pr_id.backport()
    backport_id = env[action['res_model']].browse([action['res_id']])
    assert backport_id._name == 'runbot_merge.pull_requests.backport'
    assert backport_id
    return backport_id

def test_golden_path(env, repo, config, pr_id, backport_id, users):
    branch_a, branch_b, _branch_c = env['runbot_merge.branch'].search([], order='name')
    backport_id.target = branch_a.id
    act2 = backport_id.action_apply()
    env.run_crons()  # run cron to update labels

    _, bp_id = env['runbot_merge.pull_requests'].search([], order='number')
    assert bp_id.limit_id == branch_b
    assert bp_id._name == act2['res_model']
    assert bp_id.id == act2['res_id']
    bp_head = repo.commit(bp_id.head)
    assert repo.read_tree(bp_head) == {
        'f': 'e',
        'x': '1',
    }
    assert bp_head.message == f"""c

X-original-commit: {pr_id.head}\
"""
    assert bp_id.message == f"[Backport] c\n\nBackport of {pr_id.display_name}"
    assert repo.get_pr(bp_id.number).labels == {"backport"}

    # check that the backport can actually be merged and forward-ports successfully...
    with repo:
        repo.post_status(bp_id.head, 'success')
        repo.get_pr(bp_id.number).post_comment("hansen r+", config['role_reviewer']['token'])
    env.run_crons()
    with repo:
        repo.post_status('staging.a', 'success')
    env.run_crons()
    _pr, _backport, fw_id = env['runbot_merge.pull_requests'].search([], order='number')
    fw_pr = repo.get_pr(fw_id.number)
    assert fw_pr.comments == [
        seen(env, fw_pr, users),
        (users['user'], '''\
@{user} @{reviewer} this PR targets b and is the last of the forward-port chain.

To merge the full chain, use
> @hansen r+

More info at https://github.com/odoo/odoo/wiki/Mergebot#forward-port
'''.format_map(users)),
    ]

def test_conflict(env, repo, config, backport_id):
    with repo:
        repo.make_commits('a', Commit('conflict', tree={'x': '-1'}), ref='heads/a', make=False)

    branch_a, _branch_b, _branch_c = env['runbot_merge.branch'].search([], order='name')
    backport_id.target = branch_a.id
    with pytest.raises(Fault) as exc:
        backport_id.action_apply()
    assert exc.value.faultString == """\
backport conflict:

Auto-merging x
CONFLICT (add/add): Merge conflict in x
"""

def test_target_error(env, config, backport_id):
    branch_a, _branch_b, branch_c = env['runbot_merge.branch'].search([], order='name')
    with pytest.raises(Fault) as exc:
        backport_id.action_apply()
    assert exc.value.faultString == "A backport needs a backport target"

    backport_id.target = branch_c.id
    with pytest.raises(Fault) as exc:
        backport_id.action_apply()
    assert exc.value.faultString == "The backport branch needs to be before the source's branch (got 'c' and 'c')"

    backport_id.target = branch_a.id
    backport_id.action_apply()

@pytest.mark.skip(
    reason="Currently no way to make just the PR creation fail, swapping the "
           "fp_github_token for an invalid one breaks git itself"
)
def test_pr_fail(env, config, repo, pr_id, backport_id):
    backport_id.target = env['runbot_merge.branch'].search([], order='name', limit=1).id
    with pytest.raises(Fault) as exc:
        backport_id.action_apply()
    assert exc.value.faultString == 'Backport PR creation failure: '
