import asyncio
import logging
import os
import shutil
from pathlib import Path

import aiohttp
import pytest
from clarabot_nano_common.common.config.constants import CommonConfigConstants
from clarabot_nano_common.common.log import LoggingBootstrap
from clarabot_nano_common.utility.dirs import Directories

from clarabot_nano_tests.testing.client.manager import GraphQLClientManager
from clarabot_nano_tests.testing.privy.manager import DevPrivyManager
from clarabot_nano_tests.testing.server.manager import DevServerManager
from clarabot_nano_tests.testing.utility import alt_temp_dir, clear_nano_app_config_data_dir
from dev_tools.build.deps.script import build_deps
from dev_tools.build.utility import dev_build_dir, dev_cache_dir
from dev_tools.picoture.script import build_picoture

# The user's temp directory may be sensitive on some platforms. To provide similar testing rules across platforms we
# explicitly override it, even if it is unnecessary.
os.environ["PYTEST_DEBUG_TEMPROOT"] = Path(alt_temp_dir(), "clarabot-nano-dev-test").as_posix()
os.makedirs(os.environ["PYTEST_DEBUG_TEMPROOT"], exist_ok=True)


def _create_dev_src_manager():
    return DevServerManager.create(os.environ.get("CLARABOT_TESTING_SERVER_CONFIG_PATH"))


@pytest.fixture(scope="session")
def testing_server_reset_data():
    async def _reset_testing_server():
        tsm = _create_dev_src_manager()
        try:
            return await tsm.call_debug_reset("forty", use_last=False)
        finally:
            await tsm.close()

    return asyncio.run(_reset_testing_server())


@pytest.fixture(scope="session")
def testing_server_user0(testing_server_reset_data):
    return testing_server_reset_data["user_accounts"][0]


@pytest.fixture(scope="session")
def testing_server_user_1(testing_server_reset_data, testing_server_user0):
    last_user_data = testing_server_reset_data["user_accounts"][-1]
    assert testing_server_user0["_p_kr_version"] != last_user_data["_p_kr_version"]
    return last_user_data


@pytest.fixture(scope="session")
def testing_server_user_norm_pass(testing_server_reset_data):
    for ud in testing_server_reset_data["user_accounts"]:
        if ud.get("norm_alt_passes"):
            break
    else:
        assert False, "user missing with norm_alt_passes"
    return ud


@pytest.fixture(scope="session")
def testing_server_user_not_norm_pass(testing_server_reset_data):
    for ud in testing_server_reset_data["user_accounts"]:
        if ud.get("not_norm_alt_passes"):
            break
    else:
        assert False, "user missing with not_norm_alt_passes"
    return ud


@pytest.fixture
async def testing_server_manager():
    try:
        tsm = _create_dev_src_manager()
        await tsm.csrf_by_session(await tsm.session())  # check if server listens
    except Exception as e:
        raise Exception("Debug server may not be available") from e
    try:
        yield tsm
    finally:
        await tsm.close()


@pytest.fixture
async def testing_client_manager(testing_server_manager):
    tcm = GraphQLClientManager(testing_server_manager)
    yield tcm


@pytest.fixture
async def testing_privy_manager():
    async with aiohttp.ClientSession() as session:
        yield DevPrivyManager(session)


@pytest.fixture(scope="session", autouse=True)
def dev_build_nano_picoture():
    # Picoture is built by setuptools normally, for development and testing we auto-build it.
    return build_picoture(force=False)


@pytest.fixture(scope="session")
def dev_nano_build_dir():
    return dev_build_dir()


@pytest.fixture(scope="session")
def dev_nano_root_dir(dev_nano_build_dir, dev_build_nano_picoture):
    ds = Directories.instance()
    root_dir = ds.get_user_app_data_dir("devtest", "root")
    if root_dir.exists():
        shutil.rmtree(root_dir)
    build_deps(dev_cache_dir(), dev_nano_build_dir / "deps", dev_build_nano_picoture)
    shutil.copytree(dev_nano_build_dir, root_dir, dirs_exist_ok=True)
    # for base, dirs, files in os.walk(root_dir):
    #     os.chmod()
    return root_dir


@pytest.fixture(scope="session")
def monkeypatch_session():
    from _pytest.monkeypatch import MonkeyPatch

    m = MonkeyPatch()
    yield m
    m.undo()


@pytest.fixture(scope="session", autouse=True)
def monkey_patch_testing_contants(monkeypatch_session):
    # set some dev settings in the tester process as well for utilities to work as expected
    tsm = _create_dev_src_manager()
    with monkeypatch_session.context() as m:
        m.setattr(CommonConfigConstants, "SERVER_DOMAIN", tsm.server_frontend_domain)
        m.setattr(CommonConfigConstants, "SERVER_PATH", tsm.server_frontend_path)
        m.setattr(CommonConfigConstants, "DEVELOPMENT", True)
        yield


@pytest.fixture(scope="session", autouse=True)
def monkey_patch_logging_bootstrap(monkeypatch_session):
    def _setup_logging_from_config(*args, **kwargs):
        print("NOOP setup_logging_from_config", *args, **kwargs)

    def _setup_logging(*args, **kwargs):
        print("NOOP setup_logging", *args, **kwargs)

    def _setup_custom_sigint_sigterm_handler(*args, **kwargs):
        print("NOOP setup_custom_sigint_sigterm_handler", *args, **kwargs)

    with monkeypatch_session.context() as m:
        m.setattr(LoggingBootstrap, "setup_logging_from_config", _setup_logging_from_config)
        m.setattr(LoggingBootstrap, "setup_logging", _setup_logging)
        m.setattr(LoggingBootstrap, "setup_custom_sigint_sigterm_handler", _setup_custom_sigint_sigterm_handler)
        yield


@pytest.fixture(autouse=True)
def ensure_test_logging_is_independent(monkeypatch):
    logger = logging.getLogger()
    assert str(logger.handlers).lower().find("concurrent") == -1, str(logger.handlers)
    logger.addHandler(logging.StreamHandler())  # allows logging during tests


@pytest.fixture(autouse=True)
def ensure_testing_environment_setup(monkeypatch):
    assert (
        CommonConfigConstants.USER_DATA_PATH_PARTS[0] == "devtest"
    ), "Set CBNANO_USER_DIR_OVERRIDE to devtest for testing!"


@pytest.fixture
def user_app_dirs(ensure_testing_environment_setup):
    ds = Directories.instance()
    return ds.get_user_app_config_dir(*CommonConfigConstants.USER_DATA_PATH_PARTS), ds.get_user_app_data_dir(
        *CommonConfigConstants.USER_DATA_PATH_PARTS
    )


@pytest.fixture
def user_app_config_dir(user_app_dirs):
    return user_app_dirs[0]


@pytest.fixture
def user_app_data_dir(user_app_dirs):
    return user_app_dirs[1]


@pytest.fixture
def reset_user_app_dirs(user_app_dirs):
    config_dir, data_dir = user_app_dirs
    clear_nano_app_config_data_dir(config_dir, data_dir)
    return user_app_dirs


@pytest.fixture
def reset_user_app_config_dir(reset_user_app_dirs):
    return reset_user_app_dirs[0]


@pytest.fixture
def reset_user_app_data_dir(reset_user_app_dirs):
    return reset_user_app_dirs[1]


@pytest.fixture
def reset_user_app_dirs_override2():
    override = "devtest2"
    ds = Directories.instance()
    config_dir = ds.get_user_app_config_dir(override, *CommonConfigConstants.USER_DATA_PATH_PARTS[1:])
    data_dir = ds.get_user_app_data_dir(override, *CommonConfigConstants.USER_DATA_PATH_PARTS[1:])
    clear_nano_app_config_data_dir(config_dir, data_dir)
    return override, config_dir, data_dir
