import base64
import os
import random
from functools import partial
from pathlib import Path

import aiohttp
import pytest
import safe_radix32
from clarabot_nano_common.common.protocol.errors import NANO_REQUEST_ERROR_HANDLER_BAD_REQUEST
from clarabot_nano_common.common.solr.rest import create_nano_update_url, create_solr_auth_header
from clarabot_nano_common.utility.path.common import normalize_absolute_path

from clarabot_nano.utility import parse_environment_flag
from clarabot_nano_tests.testing.client.manager import GraphQLClientManager
from clarabot_nano_tests.testing.config import room_blocks_from_room_data
from clarabot_nano_tests.testing.core.instance import RequestTestingNanoInstanceHandler
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 run_check_update, mutate_remote_config_indexed_languages


@pytest.mark.skipif(parse_environment_flag("CBNANO_TEST_WITHOUT_SERVER"), reason="server-dependant tests disabled")
@pytest.mark.asyncio
async def test_server_request_process_search(
    tmpdir,
    reset_user_app_dirs,
    dev_nano_root_dir,
    testing_server_manager: DevServerManager,
    testing_server_user0,
    testing_client_manager: GraphQLClientManager,
):
    u0_kr = testing_server_user0["_acc_kr"]
    r0 = Path(normalize_absolute_path(temp_path(tmpdir)))
    r0.mkdir()
    r1 = Path(normalize_absolute_path(temp_path(tmpdir)))
    r1.mkdir()

    rooms = testing_server_user0["rooms"][:4]
    drives = [
        {
            "path": str(r0),
            "rooms": [safe_radix32.encode_fw(rooms[0]["id"]), safe_radix32.encode_fw(rooms[2]["id"])],
        },
        {
            "path": str(r1),
            "rooms": [safe_radix32.encode_fw(rooms[1]["id"])],
        },
    ]
    room_blocks = {
        safe_radix32.encode_fw(rooms[0]["id"]): room_blocks_from_room_data(rooms[0]),
        safe_radix32.encode_fw(rooms[1]["id"]): room_blocks_from_room_data(rooms[1]),
        safe_radix32.encode_fw(rooms[2]["id"]): room_blocks_from_room_data(rooms[2]),
    }
    async with aiohttp.ClientSession() as session:
        async with RequestTestingNanoInstanceHandler(
            reset_user_app_dirs,
            dev_nano_root_dir,
            testing_server_manager,
            testing_server_user0,
            testing_client_manager,
            drives,
            room_blocks,
            remote_config_mut=partial(mutate_remote_config_indexed_languages, "all"),
        ).run() as (pm, debug_info, gateway_port, gateway_key, session_id, remote_yaml, nano_id, u0wsc):
            dbg_config = await pm.get_children_config(session, debug_info["port"])
            solr_port = dbg_config["state"]["solr_port"]
            auth_h = create_solr_auth_header(dbg_config["state"]["solr_key"])
            u_url = create_nano_update_url(solr_port, dbg_config["state"]["solr_core"])

            # errors
            await testing_client_manager.search_room(
                54, rooms[0]["id"], u0wsc, u0_kr, u0_kr, error=NANO_REQUEST_ERROR_HANDLER_BAD_REQUEST
            )
            await testing_client_manager.search_room(
                "", rooms[0]["id"], u0wsc, u0_kr, u0_kr, error=NANO_REQUEST_ERROR_HANDLER_BAD_REQUEST
            )
            await testing_client_manager.search_room(
                "large", rooms[0]["id"], u0wsc, u0_kr, u0_kr, error=NANO_REQUEST_ERROR_HANDLER_BAD_REQUEST, lang=55
            )

            # SEARCH
            r = await testing_client_manager.search_room("large", rooms[0]["id"], u0wsc, u0_kr, u0_kr)
            assert r == {"docs": []}

            # generate docs to test against
            docs = []
            doc_extra = [50, 13456013000]

            def _create_doc(root, path, lang, title, text):
                docs.append(
                    {
                        "path": f"{root.as_posix()}/{path}",
                        "file": True,
                        "accessors": {"add-distinct": f"{root.as_posix()}//{path}"},
                        "size": doc_extra[0],
                        "mtime": doc_extra[1],
                        f"summary_{lang}": title,
                        f"text_{lang}": text,
                        "img_data": (
                            img_data := (
                                None
                                if random.random() < 0.1
                                else base64.b64encode(os.urandom(100) * random.randint(7, 33)).decode("ascii")
                            )
                        ),
                        # NOTE: For testing the search results, this is correct. However, later (if actual bucketing
                        # is implemented) this will only be a gross simplification.
                        "img_bucket": int(bool(img_data)),
                    }
                )
                doc_extra[0] += 1
                doc_extra[1] += 1000

            def _link_doc(root, path, acc_root, acc_path):
                docs.append(
                    {
                        "path": f"{root.as_posix()}/{path}",
                        "file": True,
                        "accessors": {"add-distinct": f"{acc_root.as_posix()}//{acc_path}"},
                    }
                )

            def _create_dir(root, path):
                return {
                    "path": f"{root.as_posix()}/{path}",
                    "file": False,
                    "accessors": {"add-distinct": f"{root.as_posix()}//{path}"},
                }

            text = (
                "It is a long story of a person who would not want to grasp reality... "
                "The wise man ponders: why does the table climb on top of the chair? "
                "Will the chair let the table down? "
                "During the great war of the home decor masters, "
                "the benches came together and overthrew the tyranny of the fences."
            )
            _create_doc(r0, "asd/singleward.txt", lang := "faba", f"singleward.txt\nEnglish text in {lang}", text)
            _create_doc(r0, "asd/singleword.txt", lang := "en", f"singleword.txt\nEnglish text in {lang}", text)
            _create_doc(r0, "asd/singlewyrd.txt", lang := "hu", f"singlewyrd.txt\nEnglish text in {lang}", text)

            text = (
                "A kerti törpék otthona veszélybe került amikor a szomszédnál csőtörés volt... "
                "A bölcs törpe tűnődott: miért nincsen lába a vasalónak? "
                "Írigy-e a lóra, akinek bár nincs vasa, négy lába van? "
                "A házi díszítő mesterek viszálya közben esett meg, "
                "hogy a padok összefogtak és megdöntötték a kerítések uralmát."
            )
            _create_doc(r0, "asd/y0 kétszó.txt", lang := "faba", f"y0 kétszó.txt\nHungarian text in {lang}", text)
            _create_doc(r0, "asd/y1 kétszú.txt", lang := "en", f"y1 kétszú.txt\nHungarian text in {lang}", text)
            _create_doc(r0, "asd/y2 kétszí.txt", lang := "hu", f"y2 kétszí.txt\nHungarian text in {lang}", text)

            text = (
                "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor "
                "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud "
                "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute "
                "irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
                "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia "
                "deserunt mollit anim id est laborum."
            )
            _create_doc(r0, "asd/z0-dashward.txt", lang := "faba", f"z0-dashward.txt\nIpsum text in {lang}", text)
            _create_doc(r0, "asd/z1-dashword.txt", lang := "en", f"z1-dashword.txt\nIpsum text in {lang}", text)
            _create_doc(r0, "asd/z2-dashwyrd.txt", lang := "hu", f"z2-dashwyrd.txt\nIpsum text in {lang}", text)

            _create_doc(r0, "asd/low_dash_file.dat", "hu", f"low_dash_file.dat", "xysemmiség")
            _create_doc(r0, "asd/normal-dash-file.dat", "hu", f"normal-dash-file.dat", "xysemmiség")
            _create_doc(r0, "asd/normal-rádash-file.dat", "hu", f"normal-rádash-file.dat", "xysemmiség")

            _link_doc(r0, "asd/singlewyrd.txt", r1, "sym/singlewyrd.txt")
            _link_doc(r0, "asd/y2 kétszí.txt", r1, "sym/y2 kétszí.txt")
            _link_doc(r0, "asd/z2-dashwyrd.txt", r1, "sym/z2-dashwyrd.txt")

            await run_check_update(session, u_url, auth_h, docs + [_create_dir(r0, "asd"), _create_dir(r1, "sym")])

            def _add_img_data_to_docs(docs_):
                for doc in docs_:
                    acc_path_ = f"//{doc['path']}"
                    paths_ = set(d["path"] for d in docs if d["accessors"]["add-distinct"].endswith(acc_path_))
                    real_docs = [d for d in docs if d["path"] in paths_ and d.get("img_data")]
                    if real_docs:
                        assert len(real_docs) == 1, f"{doc}\n{real_docs}"
                        doc["img0"] = base64.b64decode(real_docs[0]["img_data"])
                return docs_

            # queries!
            r = await testing_client_manager.search_room("table", rooms[0]["id"], u0wsc, u0_kr, u0_kr)
            assert r == {
                "docs": _add_img_data_to_docs(
                    [
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/singlewyrd.txt",
                            "size": 52,
                            "mtime": 13.456015,
                            "score": 1.3914173,
                            "snippets": [
                                " wise man ponders: why does the <u>table</u> climb on top of the chair? "
                                "Will the chair let the <u>table</u> down? During"
                            ],
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/singleword.txt",
                            "size": 51,
                            "mtime": 13.456014,
                            "score": 1.1520759,
                            "snippets": [
                                " wise man ponders: why does the <u>table</u> climb on top of the chair? "
                                "Will the chair let the <u>table</u> down? During"
                            ],
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/singleward.txt",
                            "size": 50,
                            "mtime": 13.456013,
                            "score": 0.433577,
                            "snippets": [
                                " wise man ponders: why does the <u>table</u> climb on top of the chair? "
                                "Will the chair let the <u>table</u> down? During"
                            ],
                        },
                    ]
                )
            }, f"{r}"
            r = await testing_client_manager.search_room("table", rooms[1]["id"], u0wsc, u0_kr, u0_kr)
            assert r == {
                "docs": _add_img_data_to_docs(
                    [
                        {
                            "rid": safe_radix32.encode_fw(rooms[1]["id"]),
                            "path": "sym/singlewyrd.txt",
                            "size": 52,
                            "mtime": 13.456015,
                            "score": 1.3914173,
                            "snippets": [
                                " wise man ponders: why does the <u>table</u> climb on top of the chair? "
                                "Will the chair let the <u>table</u> down? During"
                            ],
                        }
                    ]
                )
            }, f"{r}"

            r = await testing_client_manager.search_room("table", rooms[0]["id"], u0wsc, u0_kr, u0_kr, lang="en")
            assert r == {
                "docs": _add_img_data_to_docs(
                    [
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/singleword.txt",
                            "size": 51,
                            "mtime": 13.456014,
                            "score": 2.422457,
                            "snippets": [
                                " wise man ponders: why does the <u>table</u> climb on top of the chair? "
                                "Will the chair let the <u>table</u> down? During"
                            ],
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/singlewyrd.txt",
                            "size": 52,
                            "mtime": 13.456015,
                            "score": 1.3914173,
                            "snippets": [
                                " wise man ponders: why does the <u>table</u> climb on top of the chair? "
                                "Will the chair let the <u>table</u> down? During"
                            ],
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/singleward.txt",
                            "size": 50,
                            "mtime": 13.456013,
                            "score": 0.433577,
                            "snippets": [
                                " wise man ponders: why does the <u>table</u> climb on top of the chair? "
                                "Will the chair let the <u>table</u> down? During"
                            ],
                        },
                    ]
                )
            }, f"{r}"

            # check file name matching in title (tokenization by period)
            r = await testing_client_manager.search_room("singleward.txt", rooms[0]["id"], u0wsc, u0_kr, u0_kr)
            assert r == {
                "docs": _add_img_data_to_docs(
                    [
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/singleward.txt",
                            "size": 50,
                            "mtime": 13.456013,
                            "score": 0.48613048,
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/singlewyrd.txt",
                            "size": 52,
                            "mtime": 13.456015,
                            "score": 0.46325773,
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/y2 kétszí.txt",
                            "size": 55,
                            "mtime": 13.456018,
                            "score": 0.4258147,
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/z2-dashwyrd.txt",
                            "size": 58,
                            "mtime": 13.456021,
                            "score": 0.4258147,
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/singleword.txt",
                            "size": 51,
                            "mtime": 13.456014,
                            "score": 0.15446368,
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/y1 kétszú.txt",
                            "size": 54,
                            "mtime": 13.456017,
                            "score": 0.14276084,
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/z1-dashword.txt",
                            "size": 57,
                            "mtime": 13.45602,
                            "score": 0.14276084,
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/y0 kétszó.txt",
                            "size": 53,
                            "mtime": 13.456016,
                            "score": 0.05383853,
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/z0-dashward.txt",
                            "size": 56,
                            "mtime": 13.456019,
                            "score": 0.05383853,
                        },
                    ]
                )
            }, f"{r}"
            r = await testing_client_manager.search_room("singleward", rooms[0]["id"], u0wsc, u0_kr, u0_kr)
            assert r == {
                "docs": _add_img_data_to_docs(
                    [
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/singleward.txt",
                            "size": 50,
                            "mtime": 13.456013,
                            "score": 0.42787853,
                        }
                    ]
                )
            }, f"{r}"

            # check file name matching in title (tokenization by underscore/low-dash and dash)
            r = await testing_client_manager.search_room("dash", rooms[0]["id"], u0wsc, u0_kr, u0_kr)
            assert r == {
                "docs": _add_img_data_to_docs(
                    [
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/low_dash_file.dat",
                            "size": 59,
                            "mtime": 13.456022,
                            "score": 1.8089294,
                        },
                        {
                            "rid": safe_radix32.encode_fw(rooms[0]["id"]),
                            "path": "asd/normal-dash-file.dat",
                            "size": 60,
                            "mtime": 13.456023,
                            "score": 1.8089294,
                        },
                    ]
                )
            }, f"{r}"
