import os
import glob
import subprocess
import sys
import datetime

sys.path.append("Input_Your_Path")
from code_gen.llm import llm_response

def print_validation_lc_result(success: bool):
    line_width = 60
    title = "Validation LC"

    PURPLE = "\033[95m"
    GREEN = "\033[92m"
    RED = "\033[91m"
    RESET = "\033[0m"

    print(PURPLE + "=" * line_width + RESET)
    print(title.center(line_width))
    print(PURPLE + "=" * line_width + RESET)

    result_msg = "Result: PASS" if success else "Result: FAIL"
    color = GREEN if success else RED
    print((color + result_msg + RESET).center(line_width))

    print(PURPLE + "=" * line_width + RESET)

def generate_problem_pddl(init_observation, domain_pddl, target_skill_def_code, target_skill_call_code):
    prompt = f'''
You are a PDDL expert tasked with generating a complete problem.pddl file based on the provided initial observation. The file should include the :objects, :init, and :goal sections, and follow standard PDDL syntax. Here are the instructions:
1.	Objects and Initial State:
Use the exact objects and initial state (:init) from the provided initial observation. Do not add or modify any objects or predicates unless explicitly stated in the observation.
2.	Goal State:
The :goal section should represent the expected state after executing the target skill on the current observation.
•	Analyze the initial observation and the domain PDDL to understand the current state and the effects of the target skill.
•	Utilize the domain PDDL, which specifies how predicates or conditions change after using the skill, to predict what conditions will be present after executing the skill.
•	Set the goal as a minimal set of predicates that reflect this expected state, using only the predicate symbols already defined in the domain PDDL.
•	Do not invent any new predicates beyond those the domain PDDL declares, even if the observation suggests certain states. If a needed predicate does not exist in the domain, omit it from the goal.
3.	Format:
Provide the complete problem.pddl file as plain text (not in a code block), ensuring it is syntactically correct and complete.

Important
•	You should not change the initial observation. You can only generate the :goal section (and include the standard (:problem …), (:domain …), (:objects …), (:init …)) as required.
•	Do not include any code comments in the response. You should only provide the problem PDDL content.
•	Ensure that the generated PDDL is syntactically correct and follows the standard PDDL format.
•	Do not use a code block. Provide the response as plain text.
•	You must only use the predicates, objects, and types that appear in both the domain PDDL and the initial observation.

Initial Observation:
{init_observation}

Domain PDDL:
{domain_pddl}

Skill Definition Code:
{target_skill_def_code}

Skill Call Code:

{target_skill_call_code}
'''
    try:
        response = llm_response("openai", prompt)
        if response is None:
            raise ValueError("LLM response is None")
        gnenrated_pddl = response.strip()
        temp_dir = "Input_Your_Path"
        os.makedirs(temp_dir, exist_ok=True)
        filename = os.path.join(temp_dir, f"problem_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.pddl")
        with open(filename, 'w') as f:
            f.write(gnenrated_pddl)
        return gnenrated_pddl
    except Exception as e:
        print(f"Error generating problem PDDL: {str(e)}")
        return None

def analyze_pddl_failure(domain_pddl, problem_pddl):
    prompt = f"""You are a PDDL expert. Analyze the following domain and problem PDDL files and identify the missing predicate in the initial state that prevents successful planning. Focus only on identifying the missing predicate required for the goal state.

Domain PDDL:
{domain_pddl}

Problem PDDL:
{problem_pddl}

Output format: Only provide the name of the missing predicate without any explanation.

Your analysis:"""

    try:
        response = llm_response("openai", prompt)
        if response is None:
            raise ValueError("LLM response is None")
        return response.strip()
    except Exception as e:
        print(f"Error analyzing PDDL failure: {str(e)}")
        return f"LLM analysis failed: {str(e)}"

def check_skill_pddl(target_skill_def_code, target_skill_call_code, domain_pddl, base_path, init_observation, skill_name):
    try:
        problem_pddl = generate_problem_pddl(
            init_observation, domain_pddl,
            target_skill_def_code, target_skill_call_code
        )
        if problem_pddl is None:
            return 0, "Failed to generate problem PDDL", None

        debug_dir = "Input_Your_Path"
        os.makedirs(debug_dir, exist_ok=True)

        domain_file = os.path.join(debug_dir, "domain.pddl")
        problem_file = os.path.join(debug_dir, "problem.pddl")
        plan_file = os.path.join(debug_dir, "sas_plan")

        old_plan_files = glob.glob(os.path.join(debug_dir, "sas_plan*"))
        for opf in old_plan_files:
            os.remove(opf)

        with open(domain_file, 'w') as f:
            f.write(domain_pddl)
        with open(problem_file, 'w') as f:
            f.write(problem_pddl)

        fast_downward_path = "Input_Your_Path"
        command = (
            f"{fast_downward_path} --alias seq-sat-lama-2011 "
            f"--plan-file {plan_file} {domain_file} {problem_file}"
        )
        result = subprocess.run(
            command, shell=True,
            capture_output=True, text=True,
            timeout=10
        )

        plan_files = glob.glob(os.path.join(debug_dir, "sas_plan*"))
        if not plan_files:
            missing_predicate = analyze_pddl_failure(domain_pddl, problem_pddl)
            return 0, missing_predicate if missing_predicate else "No plan generated", None

        skill_found = False
        for pf in plan_files:
            with open(pf, "r") as f:
                plan_content = f.read()
            if skill_name in plan_content:
                skill_found = True
                break

        if skill_found:
            return 1, None, problem_pddl
        else:
            missing_predicate = analyze_pddl_failure(domain_pddl, problem_pddl)
            return 0, missing_predicate if missing_predicate else "Skill not in plan", None

    except Exception as e:
        print(f"Error during PDDL solving: {str(e)}")
        return 0, str(e), None