import copy
import datetime
import json
import math
import pickle
import random
from typing import Any, Dict, List

import numpy as np
from omegaconf import DictConfig

from Adam.skill_loader import convert_action_to_mineflayer


PICKAXE_TO_INT = {
    'wooden_pickaxe': 1,
    'stone_pickaxe': 2,
    'iron_pickaxe': 3,
}

INT_TO_PICKAXE = {
    1: 'wooden_pickaxe',
    2: 'stone_pickaxe',
    3: 'iron_pickaxe',
}

def max_pickaxe_level(inventory: Dict[str, int]) -> int:
    pickaxes_levels = [PICKAXE_TO_INT[item] for item in inventory if item in PICKAXE_TO_INT]
    pickaxes_levels.append(0)

    return max(pickaxes_levels)


def save_bin(data: Any, path: str):
    with open(path, "wb") as fp:
        pickle.dump(data, fp)


def read_bin(path: str) -> Any:
    with open(path, "rb") as fp:
        data = pickle.load(fp, encoding="bytes")
    return data


def get_time() -> str:
    now = datetime.datetime.now()
    return now.strftime("%Y_%m_%d_%H_%M_%S")


def get_evaluate_task_and_goal(cfg: DictConfig) -> List[str]:
    id_map = {task["id"]: task for task in cfg["all_task"]}
    evaluate_id = cfg["benchmark_evaluate_id"]

    task = id_map[evaluate_id]["instruction"]
    goal = id_map[evaluate_id]["goal"]

    return task, goal


def convert_subgoal_to_mineflayer_code_action(subgoal):
        high_level_action = copy.deepcopy(subgoal["task"]) # "mine cobblestone"
        high_level_action_subgoal = copy.deepcopy(subgoal["goal"]) # ["cobblestone", 11]

        high_level_action_subgoal_item = high_level_action_subgoal[0]
        if "log" in high_level_action_subgoal_item:
            high_level_action_subgoal_item = "log"
        elif "plank" in high_level_action_subgoal_item:
            high_level_action_subgoal_item = "planks"
        elif "stick" in high_level_action_subgoal_item:
            high_level_action_subgoal_item = "stick"
        elif "charcoal" in high_level_action_subgoal_item:
            high_level_action_subgoal_item = "charcoal"
        elif "coal" in high_level_action_subgoal_item:
            high_level_action_subgoal_item = "coal"

        high_level_action_subgoal_item_num = high_level_action_subgoal[1]
        if high_level_action_subgoal_item == "planks":
            high_level_action_subgoal_item_num = math.ceil(high_level_action_subgoal_item_num / 4)
        elif high_level_action_subgoal_item == "stick":
            high_level_action_subgoal_item_num = math.ceil(high_level_action_subgoal_item_num / 4)

        high_level_action_subgoal = [high_level_action_subgoal_item, high_level_action_subgoal_item_num]

        mineflayer_js_code_action = convert_action_to_mineflayer(
            high_level_action,
            high_level_action_subgoal,
        )
        return mineflayer_js_code_action


def check_original_goal_finish(original_final_goal, inventory):
    as_it_is = _check_original_goal_finish(original_final_goal, inventory)
    with_underbar = _check_original_goal_finish(original_final_goal.replace(" ", "_"), inventory)
    without_ore = _check_original_goal_finish(original_final_goal.replace("_ore", ""), inventory)
    without_dust = _check_original_goal_finish(original_final_goal.replace("_dust", ""), inventory)
    return as_it_is or with_underbar or without_ore or without_dust


def _check_original_goal_finish(original_final_goal, inventory):
    item_list = _expand_item(original_final_goal)

    total = 0
    for item in item_list:
        total += inventory[item] if item in inventory else 0
    
    if total >= 1:
        return True
    return False


def check_waypoint_item_obtained(waypoint, inventory_before_the_action, inventory_after_the_action):
    as_it_is = _check_waypoint_item_obtained(waypoint, inventory_before_the_action, inventory_after_the_action)
    with_underbar = _check_waypoint_item_obtained(waypoint.replace(" ", "_"), inventory_before_the_action, inventory_after_the_action)
    # without_ore = _check_waypoint_item_obtained(waypoint.replace("_ore", ""), inventory_before_the_action, inventory_after_the_action)
    # without_dust = _check_waypoint_item_obtained(waypoint.replace("_dust", ""), inventory_before_the_action, inventory_after_the_action)
    return as_it_is or with_underbar


def _check_waypoint_item_obtained(waypoint, inventory_before_the_action, inventory_after_the_action):
    item_list = _expand_item(waypoint)

    after = 0
    before = 0
    for item in item_list:
        before += inventory_before_the_action[item] if item in inventory_before_the_action else 0
        after += inventory_after_the_action[item] if item in inventory_after_the_action else 0

    if after >= before + 1:
        return True

    return False


def _expand_item(item: str) -> List[str]:
    if "charcoal" in item:
        return ["charcoal"]
    elif "log" in item or "logs" in item:
        return [
            "acacia_log",
            "birch_log",
            "dark_oak_log",
            "jungle_log",
            "oak_log",
            "spruce_log",
        ]
    elif "plank" in item or "planks" in item:
        return [
            "acacia_planks",
            "birch_planks",
            "dark_oak_planks",
            "jungle_planks",
            "oak_planks",
            "dark_oak_planks",
            "spruce_planks",
        ]
    # elif "redstone" in item:
    #     return ["redstone"]
    # elif "stone" in item:
    #     return ["cobblestone"]
    elif "coal" in item:
        return ["coal"]
    else:
        return [item]
