#!/usr/bin/env python3
import os
import re
import subprocess
import webbrowser
import tempfile
import requests
import shutil
from pathlib import Path
from platformdirs import user_cache_dir
import shlex  # Added import shlex for parsing command strings for script execution placeholder
import sys


def ask_openai(user_prompt, reasoning_effort):
    """
    Sends the user_prompt to the OpenAI API and returns the answer.
    This function centralizes the API request logic to avoid duplication.
    """
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {os.environ.get('OPENAI_API_KEY')}",
    }
    payload = {
        "model": "o3-mini",
        "reasoning_effort": reasoning_effort,
        "messages": [{"role": "user", "content": user_prompt}],
    }
    try:
        response = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers=headers,
            json=payload,
            timeout=180,
        )
        response.raise_for_status()
    except requests.exceptions.RequestException as req_err:
        print(f"Request to OpenAI failed:\n{req_err}")
        return None

    response_json = response.json()
    if "choices" not in response_json or not response_json["choices"]:
        print("Error: Unexpected response from OpenAI:", response_json)
        return None

    return response_json["choices"][0]["message"]["content"]


def unroll_prompt_from_file(filename, dir=None, unroll=False):
    """
    Reads the file content from a directory specified by the
    ASSISTANTS_DIR environment variable.
    """
    base_dir = dir if dir else os.environ.get("ASSISTANTS_DIR", "")
    filepath = os.path.join(base_dir, filename)
    with open(filepath, "r", encoding="utf-8") as f:
        content = f.read()
    return unroll_prompt(content) if unroll else content


def get_repo_name(git_url):
    """
    Extracts the repository name from a git URL.
    For example, given "git@github.com:username/repo.git" or
    "https://github.com/username/repo.git" it returns "repo".
    """
    # Handle SSH-style URL (with colon)
    if "@" in git_url and ":" in git_url:
        part = git_url.split(":")[-1]  # e.g., "username/repo.git"
    else:
        # Handle HTTPS-style URL.
        part = git_url.rstrip("/").split("/")[-1]
    if part.endswith(".git"):
        part = part[:-4]
    return part


def unroll_prompt_from_git(git_url, file_location, branch):
    """
    Clones (or updates) a repository in a user-specific cache folder,
    then retrieves the content of a file from the specified branch.
    """
    repo_name = get_repo_name(git_url)

    # 1) Determine the user cache directory for your project:
    cache_dir = user_cache_dir("jprcfai")
    repos_dir = os.path.join(cache_dir, "repos")
    os.makedirs(repos_dir, exist_ok=True)

    # 2) Clone or update the repo inside the cache directory.
    repo_path = os.path.join(repos_dir, repo_name)
    git_cmd = shutil.which("git")
    if not git_cmd:
        raise EnvironmentError("Git is not installed or not found in PATH.")

    if not os.path.exists(repo_path):
        subprocess.run([git_cmd, "clone", git_url, repo_path], check=True)
    else:
        subprocess.run([git_cmd, "-C", repo_path, "fetch"], check=True)

    # 3) Use 'git show' to grab the file contents.
    result = subprocess.run(
        [git_cmd, "-C", repo_path, "show", f"{branch}:{file_location}"],
        capture_output=True,
        text=True,
        check=True,
    )
    return result.stdout


def unroll_prompt(prompt, visited=None):
    """
    Recursively replaces placeholders in the prompt with their loaded content.

    There are three placeholder types:

    1. [#PLACEHOLDER_LOAD_FROM_FILE (<prompt_label>)]
       -> Loads content from a local file.

    2. [#PLACEHOLDER_LOAD_FILE_FROM_GIT (<git_url_ssh>, <file_location>, <branch>)]
       -> Clones or updates a git repository and loads content from a file in that repo.

    3. [#PLACEHOLDER_LOAD_CONTENT_FROM_SCRIPT_EXECUTION ([<full_command>], <is_custom_dir>)]
       -> Executes the provided script command, captures its output, and recursively
          processes any placeholders within that output.
          If <is_custom_dir> is False, the script is assumed to be internal to the package
          (jprcfai) and its path is resolved relative to this file.
    """
    if visited is None:
        visited = set()

    # Regular expression for file-based placeholders:
    file_pattern = re.compile(r"\[#PLACEHOLDER_LOAD_FROM_FILE\s*\(\s*([^)]+?)\s*\)\]")
    # Regular expression for git-based placeholders:
    git_pattern = re.compile(
        r"\[#PLACEHOLDER_LOAD_FILE_FROM_GIT\s*\(\s*([^,]+?)\s*,\s*([^,]+?)\s*,\s*([^)]+?)\s*\)\]"
    )
    # Regular expression for script execution placeholders:
    script_pattern = re.compile(
        r"\[#PLACEHOLDER_LOAD_CONTENT_FROM_SCRIPT_EXECUTION\s*\(\s*\[\s*([^\]]+?)\s*\]\s*,\s*([^)]+?)\s*\)\]"
    )

    def file_repl(match):
        filename = match.group(1).strip()
        key = ("LOAD_FROM_FILE", filename)
        if key in visited:
            # Already processed this file; avoid reprocessing.
            return match.group(0)
        visited.add(key)
        try:
            content = unroll_prompt_from_file(filename)
        except Exception as e:
            content = f"[Error loading file '{filename}': {e}]"
        # Process any placeholders within the loaded content recursively.
        return unroll_prompt(content, visited)

    def git_repl(match):
        git_url = match.group(1).strip()
        file_location = match.group(2).strip()
        branch = match.group(3).strip()
        key = ("LOAD_FROM_GIT", git_url, file_location, branch)
        if key in visited:
            return match.group(0)
        visited.add(key)
        try:
            content = unroll_prompt_from_git(git_url, file_location, branch)
        except Exception as e:
            content = (
                f"[Error loading from git ({git_url}, {file_location}, {branch}): {e}]"
            )
        # Recursively process the loaded content.
        return unroll_prompt(content, visited)

    def script_repl(match):
        full_command_str = match.group(1).strip()
        is_custom_dir_str = match.group(2).strip()
        # Convert the is_custom_dir string to a boolean.
        is_custom_dir = is_custom_dir_str.lower() == "true"
        key = ("LOAD_FROM_SCRIPT", full_command_str, is_custom_dir)
        if key in visited:
            return match.group(0)
        visited.add(key)
        try:
            posix_mode = sys.platform != "win32"
            tokens = shlex.split(full_command_str, posix=posix_mode)
            # If not a custom directory, assume the script is internal to the package,
            # and resolve its path relative to this file using pathlib.
            if not is_custom_dir and len(tokens) > 1:
                # tokens[1] is assumed to be a relative script path.
                script_path = Path(__file__).parent / tokens[1]
                tokens[1] = str(script_path.resolve())
            result = subprocess.run(tokens, capture_output=True, text=True, check=True)
            content = result.stdout
        except Exception as e:
            content = (
                f"[Error executing script command ('{full_command_str}', "
                f"{is_custom_dir_str}): {e}]"
            )
        # Recursively process any placeholders within the output.
        return unroll_prompt(content, visited)

    # First, replace any file-based placeholders.
    prompt = file_pattern.sub(file_repl, prompt)
    # Then, replace any git-based placeholders.
    prompt = git_pattern.sub(git_repl, prompt)
    # Finally, replace any script execution placeholders.
    prompt = script_pattern.sub(script_repl, prompt)

    return prompt


def execute_local_script_with_browser(code, execution_command, port):
    """
    Executes the provided code by writing it to a temporary file and launching it
    using the given command. Immediately launches the default web browser to the specified port.

    Parameters:
      code: The code to execute.
      execution_command: Command to execute the code (e.g., "node").
      port: Port number for launching the browser.
    """
    try:
        with tempfile.NamedTemporaryFile(
            mode="w", delete=False, suffix=".js", encoding="utf-8"
        ) as tmp_file:
            tmp_file.write(code)
            tmp_filepath = tmp_file.name
    except Exception as exc:
        print("Failed to write to temporary file:", exc)
        return None

    # Ensure the execution command is available
    exec_cmd = shutil.which(execution_command)
    if not exec_cmd:
        print(f"Error: {execution_command} is not installed or not found in PATH.")
        return None

    print("\nExecuting final code from temporary file:", tmp_filepath)
    process = subprocess.Popen(
        [exec_cmd, tmp_filepath],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
    )

    webbrowser.open(f"http://localhost:{port}")
    print(f"\nBrowser launched to http://localhost:{port}.")

    process.wait()

    try:
        os.unlink(tmp_filepath)
    except Exception as e:
        print(f"Warning: Could not delete temporary file {tmp_filepath}: {e}")
