from pathlib import Path
import json
import time
import re
import traceback
from typing import List, Optional

def remove_newlines(text: str, replace_with: str = " ") -> str:
    return re.sub(r'[\r\n]+', replace_with, text)

def remove_newlines_and_special_characters(text: str, replace_with: str = " ") -> str:
    # First remove newlines and carriage returns
    text = re.sub(r'[\r\n]+', replace_with, text)
    # Then remove special characters (keeping alphanumeric, spaces, and basic punctuation)
    text = re.sub(r'[^a-zA-Z0-9\s\.\,\!\?\-\']', replace_with, text)
    # Clean up multiple spaces that may have been created
    text = re.sub(r'\s+', ' ', text)
    return text.strip()

def normalize_last_char_into_punctuation(text: str, replace_with: str = ".") -> str:
    text = text or ""
    text = text.rstrip()
    text = re.sub(r'[^\w\s]*$', replace_with, text)
    return text

def is_bash_error_code(return_code: int) -> bool:
    """
    Check if a bash return code indicates an error.

    Args:
        return_code: The integer return code from a bash command

    Returns:
        True if the return code indicates an error (non-zero), False otherwise
    """
    clean_exit_codes = [0, -15]
    return return_code not in clean_exit_codes

def render_agent_error_message(stderr_output: str, last_assistant_message_content: Optional[str] = None) -> str:
    try:
        # get 3 lines from top and 3 lines from bottom of stderr_output
        stderr_output_lines = stderr_output.split("\n")
        stderr_output_lines = stderr_output_lines[:3] + stderr_output_lines[-3:]
        stderr_output_snippet = "...".join(stderr_output_lines).strip()
        last_assistant_message_content = (last_assistant_message_content or "").strip()

        last_error_message = f" Error details: {stderr_output_snippet}" if stderr_output_snippet else None

        if last_assistant_message_content:
            last_assistant_message_content = f"{last_assistant_message_content}\n> There was an error running the agent, please try again.{last_error_message}".strip()
        else:
            last_assistant_message_content = f"> There was an error running the agent, please try again.{last_error_message}".strip()

        return last_assistant_message_content
    except Exception as e:
        traceback.print_exc()
        print(f"Error rendering agent error message: {e}")
        return "> There was an error running the agent. Please try again." # intentially different message to distinguish case

def is_port_open(host="127.0.0.1", port=8000):
    import socket
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.settimeout(2)
        try:
            s.connect((host, port))
            print("Server is up.")
            return True
        except (socket.timeout, ConnectionRefusedError):
            print("Server is down.")
            return False

def poll_for_port_open(host="127.0.0.1", port=8000):
    while not is_port_open(host, port):
        print("Waiting for server to be up...")
        time.sleep(0.1)
    print("Server is up.")

def get_file_path(filename: str) -> Path:
    absolute_path = Path(filename).absolute()
    return Path(absolute_path)

def write_to_file(filename: str, content: str) -> Path:
    file_path = get_file_path(filename)
    file_path.write_text(content)
    return file_path

def deep_merge_dict(existing: dict, new: dict) -> dict:
    """
    Recursively merge two dictionaries, with new values taking precedence.
    """
    result = existing.copy()
    for key, value in new.items():
        if key in result and isinstance(result[key], dict) and isinstance(value, dict):
            result[key] = deep_merge_dict(result[key], value)
        else:
            result[key] = value
    return result

def remove_messages(text, messages_to_remove):
    """
    Remove specified messages from text and clean up whitespace.
    
    Args:
        text: The original text string
        messages_to_remove: List of message strings to remove
    
    Returns:
        Cleaned text with messages removed and extra spaces cleaned up
    """
    result = text
    
    # Remove each message from the text
    for message in messages_to_remove:
        # Remove the message
        result = result.replace(message, "")
    
    # Clean up multiple spaces (replace 2+ spaces with single space)
    import re
    result = re.sub(r' {2,}', ' ', result)
    
    # Remove leading and trailing whitespace
    result = result.strip()
    
    return result