import asyncio
from contextlib import asynccontextmanager
from pathlib import Path

import pytest

from clarabot_nano.common.core.message import MSG_TYPE_INDEXER_SYNC
from clarabot_nano.indexer.config import IndexerConfig
from clarabot_nano.utility import parse_environment_flag
from clarabot_nano.utility.path.validate import normalize_absolute_path
from clarabot_nano_tests.indexer.scheduler.utility import PathStructureBuilder, AbstractDocsCollector
from clarabot_nano_tests.testing.core.instance import ConfigTestingNanoInstanceHandler
from clarabot_nano_tests.testing.core.manager import DevCoreProcessManager
from clarabot_nano_tests.testing.file import temp_path
from clarabot_nano_tests.testing.server.manager import DevServerManager
from clarabot_nano_tests.testing.solr import mutate_remote_config_indexed_languages


class _TestingDocsCollector(AbstractDocsCollector):
    def __init__(self, session, dbg_config, languages, roots):
        AbstractDocsCollector.__init__(self, session)
        self.ec = IndexerConfig(dbg_config)
        self.languages = languages
        self.roots = roots

    def get_solr_query_url(self):
        return self.ec.solr_query_url

    def get_solr_update_url(self):
        return self.ec.solr_update_url

    def get_solr_auth_header(self):
        return self.ec.solr_auth_header

    def get_languages(self):
        return self.languages

    def assert_accessor_in_roots(self, accessor_path: str):
        assert accessor_path.rpartition("//")[0] in self.roots


@asynccontextmanager
async def _prepare(tmpdir, testing_server_manager, reset_user_app_dirs, dev_nano_root_dir, core_args=()):
    r0 = Path(normalize_absolute_path(temp_path(tmpdir))).as_posix()
    Path(r0).mkdir()
    r1 = Path(normalize_absolute_path(temp_path(tmpdir))).as_posix()
    Path(r1).mkdir()
    psb = PathStructureBuilder([r0, r1])
    psb.create_files_in_dirs(r0, {"": 4, "a/a1": 3, "b/b1/b2": 2, "b/b3": 2})
    psb.create_files_in_dirs(r1, {"": 2, "c": 2, "d/d1": 2, "d/d2": 1})

    def _mut_rem_conf(c):
        mutate_remote_config_indexed_languages("all", c)
        c["drives"] = [{"path": str(r0)}, {"path": str(r1)}]

    async with ConfigTestingNanoInstanceHandler(
        reset_user_app_dirs,
        dev_nano_root_dir,
        testing_server_manager,
        _mut_rem_conf,
    ).run(core_args) as (
        pm,
        debug_info,
        session,
        dbg_config,
    ):
        pm: DevCoreProcessManager
        rs = [r0, r1]
        tdc = _TestingDocsCollector(session, dbg_config, ["en", "hu"], rs)
        yield pm, debug_info, session, dbg_config, rs, psb, tdc


@pytest.mark.skipif(parse_environment_flag("CBNANO_TEST_WITHOUT_SERVER"), reason="server-dependant tests disabled")
@pytest.mark.asyncio
async def test_indexer_process_poll_and_observe(
    tmpdir, testing_server_manager: DevServerManager, reset_user_app_dirs, dev_nano_root_dir
):
    async with _prepare(tmpdir, testing_server_manager, reset_user_app_dirs, dev_nano_root_dir) as (
        pm,
        debug_info,
        session,
        dbg_config,
        rs,
        psb,
        tdc,
    ):
        await tdc.assert_from_paths_until(psb.generate_paths_structure(), timeout=8.0)

        psb.create_files_in_dirs(rs[0], {"": 1, "a/a2": 1, "b/b4": 2})
        psb.create_files_in_dirs(rs[1], {"": 1, "d/d2/d3": 3})
        await tdc.assert_from_paths_until(psb.generate_paths_structure())


@pytest.mark.skipif(parse_environment_flag("CBNANO_TEST_WITHOUT_SERVER"), reason="server-dependant tests disabled")
@pytest.mark.asyncio
async def test_indexer_process_internal_events(
    tmpdir, testing_server_manager: DevServerManager, reset_user_app_dirs, dev_nano_root_dir
):
    async with _prepare(
        tmpdir,
        testing_server_manager,
        reset_user_app_dirs,
        dev_nano_root_dir,
        ["--indexer_args=--skip_poll", "--indexer_args=--skip_observe"],
    ) as (
        pm,
        debug_info,
        session,
        dbg_config,
        rs,
        psb,
        tdc,
    ):
        pm: DevCoreProcessManager
        await asyncio.sleep(6.0)
        fs0 = psb.create_files_in_dirs(rs[0], {"": 1, "a/a2": 1, "b/b4": 2})
        fs1 = psb.create_files_in_dirs(rs[1], {"": 1, "d/d2/d3": 3})
        with pytest.raises(AssertionError):
            await tdc.assert_from_paths_until(psb.generate_paths_structure(), timeout=3.0)
        await tdc.assert_from_paths({})

        await pm.assert_core_request(
            session, debug_info["port"], MSG_TYPE_INDEXER_SYNC, {"path": str(Path(rs[0], fs0[""][0]))}
        )
        await pm.assert_core_request(
            session, debug_info["port"], MSG_TYPE_INDEXER_SYNC, {"path": str(Path(rs[0], "b/b4", fs0["b/b4"][0]))}
        )
        await pm.assert_core_request(
            session, debug_info["port"], MSG_TYPE_INDEXER_SYNC, {"path": str(Path(rs[1], "d/d2/d3", fs1["d/d2/d3"][0]))}
        )

        ps = psb.generate_filtered_paths_structure(
            {
                rs[0]: [
                    ("", fs0[""][0]),
                    ("b/b4", fs0["b/b4"][0]),
                ],
                rs[1]: [
                    ("d/d2/d3", fs1["d/d2/d3"][0]),
                ],
            }
        )
        await tdc.assert_from_paths_until(ps)
