import json
from argparse import ArgumentParser

import networkx
import pytest

from cooperative_cuisine.argument_parser import (
    game_server_arguments,
    study_server_arguments,
    disable_websocket_logging_arguments,
    add_list_of_manager_ids_arguments,
    add_gui_arguments,
    add_study_arguments,
    ssl_argument,
    create_study_server_parser,
    create_game_server_parser,
    create_gui_parser,
    create_replay_parser,
    create_normal_parser,
    create_server_parser,
)
from cooperative_cuisine.environment import Environment
from cooperative_cuisine.pygame_2d_vis.video_replay import (
    GRID_SIZE_DEFAULT,
    STEP_DURATION_DEFAULT,
    FPS_DEFAULT,
    NUMBER_PLAYERS_DEFAULT,
)
from cooperative_cuisine.state_representation import (
    create_movement_graph,
    restrict_movement_graph,
    astar_heuristic,
)
from cooperative_cuisine.utils import (
    create_layout_with_counters,
    setup_logging,
)
from tests.test_start import env_config_no_validation
from tests.test_start import layout_empty_config, item_info


def test_arguments():
    parser = ArgumentParser()
    game_server_arguments(parser)
    study_server_arguments(parser)
    disable_websocket_logging_arguments(parser)
    add_list_of_manager_ids_arguments(parser)
    add_gui_arguments(parser)
    add_study_arguments(parser)
    ssl_argument(parser)
    parser.parse_args(
        [
            "-s",
            "localhost",
            "-sp",
            "8000",
            "-g",
            "localhost",
            "-gp",
            "8080",
            "--manager-ids",
            "123",
            "123123",
            "--do-study",
        ]
    )


def test_parser_creation():
    parser = ArgumentParser()
    create_game_server_parser(parser)
    parser = ArgumentParser()
    create_study_server_parser(parser)
    parser = ArgumentParser()
    create_gui_parser(parser)
    parser = ArgumentParser()
    create_replay_parser(parser)
    print(parser)
    grid_size_action = [
        a for a in parser._optionals._actions if "--grid-size" in a.option_strings
    ][0]
    assert grid_size_action.default == GRID_SIZE_DEFAULT

    number_player_action = [
        a for a in parser._optionals._actions if "--number-player" in a.option_strings
    ][0]
    assert number_player_action.default == NUMBER_PLAYERS_DEFAULT
    fps_action = [a for a in parser._optionals._actions if "--fps" in a.option_strings][
        0
    ]
    assert fps_action.default == FPS_DEFAULT
    step_duration_action = [
        a for a in parser._optionals._actions if "--step-duration" in a.option_strings
    ][0]
    assert step_duration_action.default == 1 / STEP_DURATION_DEFAULT

    parser = ArgumentParser()
    create_server_parser(parser)

    parser = ArgumentParser()
    create_normal_parser(parser)


def test_layout_creation():
    assert """###\n#_#\n###\n""" == create_layout_with_counters(3, 3)
    assert """###\n#_#\n#_#\n###\n""" == create_layout_with_counters(3, 4)


def test_setup_logging():
    setup_logging()


def test_movement_graph(env_config_no_validation, layout_empty_config, item_info):
    env = Environment(
        env_config_no_validation, layout_empty_config, item_info, as_files=False
    )
    player_name = "0"
    env.add_player(player_name)

    state_string = env.get_json_state(player_id=player_name)
    state = json.loads(state_string)
    graph_diag = create_movement_graph(state, diagonal=True)

    graph = create_movement_graph(
        json.loads(env.get_json_state(player_id=player_name)), diagonal=False
    )
    path = networkx.astar_path(
        graph,
        source=(0, 0),
        target=(3, 3),
        heuristic=astar_heuristic,
    )
    assert len(path) != 0, "No path found, but should have."

    graph_restricted = restrict_movement_graph(graph_diag, [(1, 0), (0, 1), (1, 1)])
    with pytest.raises(networkx.exception.NetworkXNoPath) as e_info:
        path = networkx.astar_path(
            graph_restricted,
            source=(0, 0),
            target=(3, 3),
            heuristic=astar_heuristic,
        )
    with pytest.raises(networkx.exception.NodeNotFound) as e_info:
        path = networkx.astar_path(
            graph_restricted,
            source=(20, 20),
            target=(40, 40),
            heuristic=astar_heuristic,
        )

    path = networkx.astar_path(
        restrict_movement_graph(
            graph=graph_diag,
            player_positions=[],
        ),
        source=(0, 0),
        target=(5, 5),
        heuristic=astar_heuristic,
    )
    assert len(path) != 0, "No path found, but should have."

    # now with diagonal movement
    graph = create_movement_graph(
        json.loads(env.get_json_state(player_id=player_name)), diagonal=True
    )
    path = networkx.astar_path(
        graph,
        source=(0, 0),
        target=(3, 3),
        heuristic=astar_heuristic,
    )
    assert len(path) != 0, "No path found, but should have."

    graph_restricted = restrict_movement_graph(graph_diag, [(1, 0), (0, 1), (1, 1)])
    with pytest.raises(networkx.exception.NetworkXNoPath) as e_info:
        path = networkx.astar_path(
            graph_restricted,
            source=(0, 0),
            target=(3, 3),
            heuristic=astar_heuristic,
        )
    with pytest.raises(networkx.exception.NodeNotFound) as e_info:
        path = networkx.astar_path(
            graph_restricted,
            source=(20, 20),
            target=(40, 40),
            heuristic=astar_heuristic,
        )

    path = networkx.astar_path(
        restrict_movement_graph(
            graph=graph_diag,
            player_positions=[],
        ),
        source=(0, 0),
        target=(5, 5),
        heuristic=astar_heuristic,
    )
    assert len(path) != 0, "No path found, but should have."