from __future__ import annotations

import os.path

import pytest

from pre_commit import envcontext
from pre_commit.languages import r
from pre_commit.util import win_exe
from testing.fixtures import make_config_from_repo
from testing.fixtures import make_repo
from tests.repository_test import _get_hook_no_install


def _test_r_parsing(
    tempdir_factory,
    store,
    hook_id,
    expected_hook_expr={},
    expected_args={},
    config={},
    expect_path_prefix=True,
):
    repo_path = 'r_hooks_repo'
    path = make_repo(tempdir_factory, repo_path)
    config = config or make_config_from_repo(path)
    hook = _get_hook_no_install(config, store, hook_id)
    ret = r._cmd_from_hook(hook)
    expected_cmd = 'Rscript'
    expected_opts = (
        '--no-save', '--no-restore', '--no-site-file', '--no-environ',
    )
    expected_path = os.path.join(
        hook.prefix.prefix_dir if expect_path_prefix else '',
        f'{hook_id}.R',
    )
    expected = (
        expected_cmd,
        *expected_opts,
        *(expected_hook_expr or (expected_path,)),
        *expected_args,
    )
    assert ret == expected


def test_r_parsing_file_no_opts_no_args(tempdir_factory, store):
    hook_id = 'parse-file-no-opts-no-args'
    _test_r_parsing(tempdir_factory, store, hook_id)


def test_r_parsing_file_opts_no_args(tempdir_factory, store):
    with pytest.raises(ValueError) as excinfo:
        r._entry_validate(['Rscript', '--no-init', '/path/to/file'])

    msg = excinfo.value.args
    assert msg == (
        'The only valid syntax is `Rscript -e {expr}`',
        'or `Rscript path/to/hook/script`',
    )


def test_r_parsing_file_no_opts_args(tempdir_factory, store):
    hook_id = 'parse-file-no-opts-args'
    expected_args = ['--no-cache']
    _test_r_parsing(
        tempdir_factory, store, hook_id, expected_args=expected_args,
    )


def test_r_parsing_expr_no_opts_no_args1(tempdir_factory, store):
    hook_id = 'parse-expr-no-opts-no-args-1'
    _test_r_parsing(
        tempdir_factory, store, hook_id, expected_hook_expr=('-e', '1+1'),
    )


def test_r_parsing_expr_no_opts_no_args2(tempdir_factory, store):
    with pytest.raises(ValueError) as execinfo:
        r._entry_validate(['Rscript', '-e', '1+1', '-e', 'letters'])
    msg = execinfo.value.args
    assert msg == ('You can supply at most one expression.',)


def test_r_parsing_expr_opts_no_args2(tempdir_factory, store):
    with pytest.raises(ValueError) as execinfo:
        r._entry_validate(
            [
                'Rscript', '--vanilla', '-e', '1+1', '-e', 'letters',
            ],
        )
    msg = execinfo.value.args
    assert msg == (
        'The only valid syntax is `Rscript -e {expr}`',
        'or `Rscript path/to/hook/script`',
    )


def test_r_parsing_expr_args_in_entry2(tempdir_factory, store):
    with pytest.raises(ValueError) as execinfo:
        r._entry_validate(['Rscript', '-e', 'expr1', '--another-arg'])

    msg = execinfo.value.args
    assert msg == ('You can supply at most one expression.',)


def test_r_parsing_expr_non_Rscirpt(tempdir_factory, store):
    with pytest.raises(ValueError) as execinfo:
        r._entry_validate(['AnotherScript', '-e', '{{}}'])

    msg = execinfo.value.args
    assert msg == ('entry must start with `Rscript`.',)


def test_r_parsing_file_local(tempdir_factory, store):
    path = 'path/to/script.R'
    hook_id = 'local-r'
    config = {
        'repo': 'local',
        'hooks': [{
            'id': hook_id,
            'name': 'local-r',
            'entry': f'Rscript {path}',
            'language': 'r',
        }],
    }
    _test_r_parsing(
        tempdir_factory,
        store,
        hook_id=hook_id,
        expected_hook_expr=(path,),
        config=config,
        expect_path_prefix=False,
    )


def test_rscript_exec_relative_to_r_home():
    expected = os.path.join('r_home_dir', 'bin', win_exe('Rscript'))
    with envcontext.envcontext((('R_HOME', 'r_home_dir'),)):
        assert r._rscript_exec() == expected


def test_path_rscript_exec_no_r_home_set():
    with envcontext.envcontext((('R_HOME', envcontext.UNSET),)):
        assert r._rscript_exec() == 'Rscript'
