import tempfile
from . import tarskilite as tl

from pathlib import Path 
# from forbiditerative import planners as fi
from kstar_planner import planners as kp


def get_tarski_problem(domain, problem):
    with tempfile.NamedTemporaryFile() as domain_temp, \
         tempfile.NamedTemporaryFile() as problem_temp:

        with open(str(domain_temp.name), 'w', encoding='utf8') as file:
            file.write(domain.lower())
        with open(str(problem_temp.name), 'w', encoding='utf8') as file:
            file.write(problem.lower())

        try:
            P = tl.STRIPS(str(domain_temp.name), str(problem_temp.name))
            return P
        except Exception as e:
            print(f"||{e}||")
            return None


def create_tmp_dom_prob_replace_init(P, state, result_domain_file, result_problem_file):
    d, p = P.PDDL_replace_init_pddl_parser(state)
    with open(str(result_domain_file.name), 'w', encoding='utf8') as file:
        file.write(str(d))
    with open(str(result_problem_file.name), 'w', encoding='utf8') as file:
        file.write(str(p))

    return d, p




# Used in next action
def is_on_optimal_plan(domain, problem, action, opt):
    with tempfile.NamedTemporaryFile() as domain_temp, \
         tempfile.NamedTemporaryFile() as problem_temp:

        with open(str(domain_temp.name), 'w', encoding='utf8') as file:
            file.write(domain.lower())
        with open(str(problem_temp.name), 'w', encoding='utf8') as file:
            file.write(problem.lower())

        # Here, we need to keep the temp files live until the end of the function
        try:
            P = tl.STRIPS(str(domain_temp.name), str(problem_temp.name))
        except Exception as e:
            # Unsolvable
            return False

        a = P.get_action_or_none(action[1:-1])
        if a is None:
            return False
        state = P.init
        next_state = tl.progress(state, a)
        if opt is None:
            # Get an optimal plan cost
            plans = generate_optimal_plans_for_problem_state(P, state, num_plans=1, timeout=5)
            opt = len(plans[0]["actions"])
        else:
            opt = int(opt)    

        # Getting an optimal plan for the next state
        next_plans = generate_optimal_plans_for_problem_state(P, next_state, num_plans=1, timeout=5)
        if next_plans is None:
            return False
        next_opt = len(next_plans[0]["actions"])
        return next_opt + 1 == opt

# Used in justification
def is_plan(domain, problem, new_plan):
    P = get_tarski_problem(domain, problem)
    if P is None:
        # Unsolvable
        return False
    
    # Check if new_plan is a plan
    current_state = P.init
    for action in new_plan:
        applicable_actions = P.get_applicable_actions(current_state)
        app_actions_list = [f'({a.name.lower()})' for a in applicable_actions]
        if action.lower() not in app_actions_list:
            return False
        a = applicable_actions[app_actions_list.index(action.lower())]
        current_state = tl.progress(current_state, a)
    return tl.entails(current_state, P.goal)

# Used in action reachability
def get_action_preconditions(domain, problem, action):
    P = get_tarski_problem(domain, problem)

    assert P is not None, f"Domain\n{domain}\nProblem\n{problem}\nAction: {action}"
    a = P.get_action_or_none(action[1:-1])
    if a is None:
        return a
        
    return [f'({f})' for f in a.pres]


def generate_top_q_plans(domain, problem, num_plans=10, quality_bound=1.0, timeout=30):
    # print("Running K* planner")
    plans = kp.plan_unordered_topq(domain_file=Path(domain), problem_file=Path(problem), number_of_plans_bound=num_plans, quality_bound=quality_bound, timeout=timeout)
    return plans

def generate_optimal_plans_for_problem_state(P, state, num_plans, timeout):
    import tempfile
    with tempfile.NamedTemporaryFile() as domain_temp, \
         tempfile.NamedTemporaryFile() as problem_temp:
        
        create_tmp_dom_prob_replace_init(P, state, domain_temp, problem_temp)
        plans = generate_top_q_plans(domain=str(domain_temp.name), problem=str(problem_temp.name), num_plans=num_plans, quality_bound=1.0, timeout=timeout)
        # print(plans)
        if plans is None or len(plans['plans']) == 0:
            return None
        return plans['plans']