import itertools
import os

import pytest
from clarabot_crypto.group_config.block import GroupConfigBlockFlags
from clarabot_nano_common.utility.serialize import msgpack_dumps

from clarabot_nano.utility import parse_environment_flag
from clarabot_nano_tests.testing.client.manager import GraphQLClientManager
from clarabot_nano_tests.testing.client.response import get_initial_context_truth_for_dbg_handler
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.server.manager import DevServerManager


@pytest.mark.skipif(parse_environment_flag("CBNANO_TEST_WITHOUT_SERVER"), reason="server-dependant tests disabled")
@pytest.mark.asyncio
async def test_gateway_request_process(
    reset_user_app_dirs,
    dev_nano_root_dir,
    testing_server_manager: DevServerManager,
    testing_server_user0,
    testing_client_manager: GraphQLClientManager,
):
    """
    Test request processing of the gateway.
    """
    room_ids = [testing_server_user0["rooms"][i]["id"] for i in range(4)]
    packed_room_ids = [await testing_server_manager.safe_radix32(room_id) for room_id in room_ids]
    drives = [
        {
            "path": "test0",
            "rooms": [
                packed_room_ids[0],
                packed_room_ids[2],  # with share_anonymous
            ],
        },
        {
            "path": "test1",
            "rooms": [packed_room_ids[1]],
        },
    ]
    room_blocks = {
        packed_room_ids[0]: room_blocks_from_room_data(testing_server_user0["rooms"][0]),
        packed_room_ids[1]: room_blocks_from_room_data(testing_server_user0["rooms"][1]),
        packed_room_ids[2]: room_blocks_from_room_data(testing_server_user0["rooms"][2]),
    }
    room0 = testing_server_user0["rooms"][0]
    async with RequestTestingNanoInstanceHandler(
        reset_user_app_dirs,
        dev_nano_root_dir,
        testing_server_manager,
        testing_server_user0,
        testing_client_manager,
        drives,
        room_blocks,
    ).run() as (pm, debug_info, gateway_port, gateway_key, session_id, remote_yaml, nano_id, u0wsc):
        for cacheable, count in [(True, 8), (False, 7)]:
            req, resps = await testing_client_manager.nano_request_responses_raw(
                nano_req := msgpack_dumps(
                    {
                        "type": "debug-echo-c",
                        "room_id": packed_room_ids[0],
                        "hello": os.urandom(8),
                        "cacheable": cacheable,
                        "count": count,
                    }
                ),
                room_ids[0],
                None,
                u0wsc,
                testing_server_user0["_acc_kr"],
                testing_server_user0["_acc_kr"],
                single_response=False,
                # testing_server_user0["rooms"][0]["_room_kr"],
            )
            parts = [r["part"] for r in resps]
            assert parts == list(range(count)), f"{cacheable} {parts}"
            # allow anonymous may be passively or actively unset depending on running previous test
            resp = resps[0]
            assert isinstance(tmp := resp["response"]["context"].pop("remote_ip"), (bytes, str)), f"{tmp}"
            target_ctxs = (
                get_initial_context_truth_for_dbg_handler(
                    req,
                    testing_server_user0["_acc_kr"],
                    testing_server_user0["account_id"],
                    room_data=room0,
                    remote_yaml=remote_yaml,
                    member=member,
                    group=group,
                )
                for member, group in itertools.product([1, 3], [0, GroupConfigBlockFlags.ALLOW_ANONYMOUS_NEGATE])
            )
            resp_ctx = resp["response"]["context"]
            assert resp_ctx in target_ctxs, f"{resp_ctx}\n{target_ctxs}"
            # test continuing the transfer using the same request
            c_req, c_resps = await testing_client_manager.nano_request_responses_raw(
                req,
                room_ids[0],
                None,
                u0wsc,
                testing_server_user0["_acc_kr"],
                testing_server_user0["_acc_kr"],
                single_response=False,
                # testing_server_user0["rooms"][0]["_room_kr"],
                cache_key=resp.get("cacheKey"),
                transfer_key=resp.get("transferKey"),
                part=4,
                stream_processor_kwargs=dict(cache_key=resp.get("cacheKey"), transfer_key=resp.get("transferKey")),
                request_payload_encrypted=True,
            )
            parts = [r["part"] for r in c_resps]
            assert parts == [0] + list(range(4, count)), f"{cacheable} {parts}"
            # test continuing the transfer using the a new request
            c_req, c_resps = await testing_client_manager.nano_request_responses_raw(
                nano_req,
                room_ids[0],
                None,
                u0wsc,
                testing_server_user0["_acc_kr"],
                testing_server_user0["_acc_kr"],
                single_response=False,
                # testing_server_user0["rooms"][0]["_room_kr"],
                cache_key=resp.get("cacheKey"),
                transfer_key=resp.get("transferKey"),
                part=4,
                stream_processor_kwargs=dict(cache_key=resp.get("cacheKey"), transfer_key=resp.get("transferKey")),
                continues_from_part=cacheable,  # transfer_key may only continue the original request
            )
            parts = [r["part"] for r in c_resps]
            assert parts == ([0] + list(range(4, count)) if cacheable else list(range(count))), f"{cacheable} {parts}"
