from unittest.mock import patch, Mock
from pynonymizer.__main__ import create_parser
import pytest


def test_legacy_positional_args():
    """
    When called with the positional args, argparse should return a working namespace with the correct values set
    :return:
    """
    args = create_parser().parse_args(["input.sql", "strategyfile.yml", "output.sql"])
    assert args.legacy_input == "input.sql"
    assert args.legacy_strategyfile == "strategyfile.yml"
    assert args.legacy_output == "output.sql"


def mock_getenv_old(name):
    """
    a mock for getenv to return the OLD environment variable set with pre-defined values
    """
    if name == "DB_TYPE":
        return "OLDENV_DB_TYPE"
    elif name == "DB_HOST":
        return "OLDENV_DB_HOST"
    elif name == "DB_NAME":
        return "OLDENV_DB_NAME"
    elif name == "DB_USER":
        return "OLDENV_DB_USER"
    elif name == "DB_PASS":
        return "OLDENV_DB_PASSWORD"
    elif name == "FAKE_LOCALE":
        return "OLDENV_FAKE_LOCALE"


def mock_getenv_none():
    """
    Simulates an empty envrionment variable set
    """
    return None


def mock_getenv_new(name):
    """
    a mock for getenv to return the NEW environment variable set with pre-defined values
    """
    if name == "PYNONYMIZER_DB_TYPE":
        return "ENV_DB_TYPE"
    elif name == "PYNONYMIZER_DB_HOST":
        return "ENV_DB_HOST"
    elif name == "PYNONYMIZER_DB_NAME":
        return "ENV_DB_NAME"
    elif name == "PYNONYMIZER_DB_USER":
        return "ENV_DB_USER"
    elif name == "PYNONYMIZER_DB_PASSWORD":
        return "ENV_DB_PASSWORD"
    elif name == "PYNONYMIZER_FAKE_LOCALE":
        return "ENV_FAKE_LOCALE"
    elif name == "PYNONYMIZER_INPUT":
        return "ENV_INPUT"
    elif name == "PYNONYMIZER_STRATEGY":
        return "ENV_STRATEGY"
    elif name == "PYNONYMIZER_OUTPUT":
        return "ENV_OUTPUT"
    elif name == "PYNONYMIZER_START_AT":
        return "ANONYMIZE_DB"
    elif name == "PYNONYMIZER_SKIP_STEPS":
        return "ANONYMIZE_DB DUMP_DB"
    elif name == "PYNONYMIZER_STOP_AT":
        return "ANONYMIZE_DB"
    elif name == "PYNONYMIZER_VERBOSE":
        return "VERBOSE"


def mock_getenv_old_new_combined(name):
    """
    combine the outputs from the two mocking function sets
    """
    return mock_getenv_new(name) or mock_getenv_old(name)


@patch("os.getenv", Mock(side_effect=mock_getenv_old))
def test_old_environmental_defaults():
    """
    When not specified, arguments should fall back to OLD env vars (if specified)
    :return:
    """
    # Use positionals, since the old vars dont cover these
    args = create_parser().parse_args(["input.sql", "strategyfile.yml", "output.sql"])
    assert args.legacy_input        == "input.sql"
    assert args.legacy_strategyfile == "strategyfile.yml"
    assert args.legacy_output       == "output.sql"
    assert args.db_type      == "OLDENV_DB_TYPE"
    assert args.db_name      == "OLDENV_DB_NAME"
    assert args.db_host      == "OLDENV_DB_HOST"
    assert args.db_user      == "OLDENV_DB_USER"
    assert args.db_password  == "OLDENV_DB_PASSWORD"
    assert args.fake_locale  == "OLDENV_FAKE_LOCALE"


@patch("os.getenv", Mock(side_effect=mock_getenv_old_new_combined))
def test_environmental_defaults():
    """
    When not specified, arguments should fall back to NEW environmental defaults (PYNONYMIZER_*)
    old envs should take the lowest precedence and be ignored
    :return:
    """
    args = create_parser().parse_args([])
    assert args.input         == "ENV_INPUT"
    assert args.strategyfile  == "ENV_STRATEGY"
    assert args.output        == "ENV_OUTPUT"
    assert args.db_type       == "ENV_DB_TYPE"
    assert args.db_name       == "ENV_DB_NAME"
    assert args.db_host       == "ENV_DB_HOST"
    assert args.db_user       == "ENV_DB_USER"
    assert args.db_password   == "ENV_DB_PASSWORD"
    assert args.fake_locale   == "ENV_FAKE_LOCALE"
    assert args.start_at_step == "ANONYMIZE_DB"
    assert args.skip_steps    == ["ANONYMIZE_DB", "DUMP_DB"]
    assert args.stop_at_step  == "ANONYMIZE_DB"


@patch("os.getenv", Mock(side_effect=mock_getenv_new))
def test_all_legacy_new_mutex():
    """
    legacy posisitonals and new options should result in an error if specified together
    """
    with pytest.raises(SystemExit) as e_info:
        create_parser().parse_args(["input.sql", "strategyfile.yml", "output.sql",  "--input", "input2.sql"])

    with pytest.raises(SystemExit) as e_info:
        create_parser().parse_args(["input.sql", "strategyfile.yml", "output.sql",  "--strategy", "strategyfile2.yml"])

    with pytest.raises(SystemExit) as e_info:
        create_parser().parse_args(["input.sql", "strategyfile.yml", "output.sql",  "--output", "output2.sql"])


@patch("os.getenv", Mock(side_effect=mock_getenv_new))
def test_all_args_precedence():
    """
    All arguments should be overridden by their argv counterparts
    :return:
    """
    args = create_parser().parse_args([
        "--input",  "input.sql",
        "--strategy",  "strategyfile.yml",
        "--output", "output.sql",
        "--db-type", "ARG_DB_TYPE",
        "--db-host", "ARG_DB_HOST",
        "--db-name", "ARG_DB_NAME",
        "--db-user", "ARG_DB_USER",
        "--db-password", "ARG_DB_PASSWORD",
        "--fake-locale", "ARG_FAKE_LOCALE",
        "--start-at", "START",
        "--skip-steps", "ANONYMIZE_DB",
        "--stop-at", "END",
        "--verbose"
    ])
    assert args.input          == "input.sql"
    assert args.strategyfile   == "strategyfile.yml"
    assert args.output         == "output.sql"
    assert args.db_type        == "ARG_DB_TYPE"
    assert args.db_name        == "ARG_DB_NAME"
    assert args.db_host        == "ARG_DB_HOST"
    assert args.db_user        == "ARG_DB_USER"
    assert args.db_password    == "ARG_DB_PASSWORD"
    assert args.fake_locale    == "ARG_FAKE_LOCALE"
    assert args.start_at_step  == "START"
    assert args.skip_steps     == ["ANONYMIZE_DB"]
    assert args.stop_at_step   == "END"
    assert args.verbose is True


@patch("os.getenv", Mock(side_effect=mock_getenv_new))
def test_all_short_args_precedence():
    """
    All arguments should be overridden by their argv counterparts (using short options!)
    :return:
    """
    args = create_parser().parse_args([
        "-i", "input.sql",
        "-s", "strategyfile.yml",
        "-o", "output.sql",
        "-t", "ARG_DB_TYPE",
        "-d", "ARG_DB_HOST",
        "-n", "ARG_DB_NAME",
        "-u", "ARG_DB_USER",
        "-p", "ARG_DB_PASSWORD",
        "-l", "ARG_FAKE_LOCALE"
    ])
    assert args.input        == "input.sql"
    assert args.strategyfile == "strategyfile.yml"
    assert args.output       == "output.sql"
    assert args.db_type      == "ARG_DB_TYPE"
    assert args.db_name      == "ARG_DB_NAME"
    assert args.db_host      == "ARG_DB_HOST"
    assert args.db_user      == "ARG_DB_USER"
    assert args.db_password  == "ARG_DB_PASSWORD"
    assert args.fake_locale  == "ARG_FAKE_LOCALE"
    assert args.verbose is False
