import asyncio
import json
import re

import aiohttp
import pytest

from clarabot_nano.common.solr.api import SolrApiAuthError, unload_solr_core
from clarabot_nano.common.solr.rest import create_solr_auth_header, NANO_CORE_NAME_PREFIX
from clarabot_nano.indexer.config import IndexerConfig
from clarabot_nano.indexer.idxstatedb import IndexingStateDatabase
from clarabot_nano.indexer.initializer import IndexInitializationError
from clarabot_nano.indexer.process import IndexerProcess
from clarabot_nano.indexer.utility import get_index_supported_languages
from clarabot_nano.utility.config.cache import CacheMarkByDict
from clarabot_nano_tests.testing.core.instance import CoreTestingNanoInstanceHandler
from clarabot_nano_tests.testing.solr import wait_solr_startup, SOLR_LOCALE_LANGS, SOLR_ALL_LANGS


class _MockedInitialisationIndexerProcess(IndexerProcess):
    def __init__(self):
        # NOTE: purposefully not calling super inits
        self._supported_languages = get_index_supported_languages()

    async def test(self, session, config):
        ec = IndexerConfig(config)
        solr_port = ec.solr_port
        solr_auth_header = ec.solr_auth_header
        async with IndexingStateDatabase.create(
            asyncio.get_event_loop(), str(ec.data_dir / "idxr_mock.db"), sqlite_timeout=10.0
        ) as index_state_db:
            assert ec.indexed_languages == []
            core_name, core_languages, core_config = await self._initialise(session, ec, index_state_db)
            assert core_name.startswith(NANO_CORE_NAME_PREFIX)
            assert core_languages == SOLR_LOCALE_LANGS
            assert core_config["updateHandler"]["autoSoftCommit"]["maxTime"] >= 15000
            assert await self._initialise_validate(session, ec, index_state_db) == (
                core_name,
                core_languages,
                core_config,
            )
            # test lost or invalid bind
            for bind_value in [None, "invalid"]:
                await index_state_db.set_config_str(self.BIND_CONFIG_KEY, bind_value)
                with pytest.raises(IndexInitializationError, match="No active bind for solr core in state db"):
                    await self._initialise_validate(session, ec, index_state_db)
            await index_state_db.set_config_str(self.BIND_CONFIG_KEY, NANO_CORE_NAME_PREFIX + "111")
            with pytest.raises(IndexInitializationError, match="Bound solr core does not exist"):
                await self._initialise_validate(session, ec, index_state_db)
            await index_state_db.set_config_str(self.BIND_CONFIG_KEY, core_name)
            # test broken but JSON parsable mark by effective_languages
            mark = CacheMarkByDict.read_mark(ec.solr_data_dir, self.SCHEMA_BIND_MARK_NAME)
            mark["effective_languages"].append(None)
            CacheMarkByDict(mark).write_mark(ec.solr_data_dir, self.SCHEMA_BIND_MARK_NAME)
            with pytest.raises(
                IndexInitializationError, match="effective_languages in old cache mark has invalid value"
            ):
                await self._initialise_validate(session, ec, index_state_db)
            mark["effective_languages"] = None
            CacheMarkByDict(mark).write_mark(ec.solr_data_dir, self.SCHEMA_BIND_MARK_NAME)
            with pytest.raises(
                IndexInitializationError, match="effective_languages in old cache mark is invalid or missing"
            ):
                await self._initialise_validate(session, ec, index_state_db)
            # test removed language support by effective_languages
            mark["effective_languages"] = ["lang"]
            CacheMarkByDict(mark).write_mark(ec.solr_data_dir, self.SCHEMA_BIND_MARK_NAME)
            with pytest.raises(
                IndexInitializationError, match="schema compilation failed with old effective_languages"
            ):
                await self._initialise_validate(session, ec, index_state_db)
            mark_path = CacheMarkByDict.get_mark_path(ec.solr_data_dir, self.SCHEMA_BIND_MARK_NAME)
            mark_path.write_text("broken")
            with pytest.raises(IndexInitializationError, match="Old cache mark is invalid or missing"):
                await self._initialise_validate(session, ec, index_state_db)
            mark_path.unlink()
            with pytest.raises(IndexInitializationError, match="Old cache mark is invalid or missing"):
                await self._initialise_validate(session, ec, index_state_db)
            mark_path.write_text(json.dumps(mark), encoding="utf-8")
            # test changed lang config
            mark["config_languages"] = "all"
            mark["effective_languages"] = SOLR_LOCALE_LANGS
            CacheMarkByDict(mark).write_mark(ec.solr_data_dir, self.SCHEMA_BIND_MARK_NAME)
            with pytest.raises(IndexInitializationError, match="Cache mark is stale"):
                await self._initialise_validate(session, ec, index_state_db)
            mark["config_languages"] = []
            CacheMarkByDict(mark).write_mark(ec.solr_data_dir, self.SCHEMA_BIND_MARK_NAME)
            assert await self._initialise_validate(session, ec, index_state_db) == (
                core_name,
                core_languages,
                core_config,
            )
            # test unreachable Solr raises (not IndexInitializationError)
            for port in range(81, 83):
                ec.solr_port = port
                with pytest.raises(aiohttp.ClientError):
                    await self._initialise_validate(session, ec, index_state_db)
            ec.solr_port = solr_port
            ec.solr_auth_header = ""
            with pytest.raises(SolrApiAuthError):
                await self._initialise_validate(session, ec, index_state_db)
            ec.solr_auth_header = solr_auth_header

            # # reset to all langs
            ec.indexed_languages = "all"
            with pytest.raises(IndexInitializationError, match="Cache mark is stale"):
                await self._initialise_validate(session, ec, index_state_db)
            core_name2, core_languages2, core_config2 = await self._initialise(session, ec, index_state_db)
            assert core_name != core_name2
            assert core_name2.startswith(NANO_CORE_NAME_PREFIX)
            assert core_languages2 == SOLR_ALL_LANGS
            assert core_config2["updateHandler"]["autoSoftCommit"]["maxTime"] >= 15000
            assert await self._initialise_validate(session, ec, index_state_db) == (
                core_name2,
                core_languages2,
                core_config2,
            )


@pytest.mark.asyncio
async def test_indexer_initialization(reset_user_app_dirs, dev_nano_root_dir):
    p = _MockedInitialisationIndexerProcess()
    async with CoreTestingNanoInstanceHandler(reset_user_app_dirs, dev_nano_root_dir).run() as (
        pm,
        session,
        children_spawns,
        dbg_config,
    ):
        solr_port = dbg_config["state"]["solr_port"]
        auth_h = create_solr_auth_header(dbg_config["state"]["solr_key"])
        await wait_solr_startup(session, solr_port, auth_h)

        await p.test(session, dbg_config)
