import json
import os
import time
import uuid
from typing import Any, Callable, List

from pydantic import (
    BaseModel,
    Field,
    constr,
)
from pydantic.v1 import validator

from swarms.telemetry.capture_sys_data import (
    capture_system_data,
    log_agent_data,
)
from swarms.tools.base_tool import BaseTool
from swarms.utils.loguru_logger import initialize_logger

logger = initialize_logger("prompt")


class Prompt(BaseModel):
    """
    A class representing a prompt with content, edit history, and version control.
    This version is enhanced for production use, with thread-safety, logging, and additional functionality.
    Autosaving is now added to save the prompt to a specified folder within the WORKSPACE_DIR.

    Attributes:
        id (UUID): A unique identifier for the prompt.
        content (str): The main content of the prompt.
        created_at (datetime): The timestamp when the prompt was created.
        last_modified_at (datetime): The timestamp when the prompt was last modified.
        edit_count (int): The number of times the prompt has been edited.
        edit_history (List[str]): A list of all versions of the prompt, including current and previous versions.
        autosave (bool): Flag to enable or disable autosaving.
        autosave_folder (str): The folder path within WORKSPACE_DIR where the prompt will be autosaved.
    """

    id: str = Field(
        default=uuid.uuid4().hex,
        description="Unique identifier for the prompt",
    )
    name: str = Field(
        default="prompt", description="Name of your prompt"
    )
    description: str = Field(
        default="Simple Prompt",
        description="The description of the prompt",
    )
    content: constr(min_length=1, strip_whitespace=True) = Field(
        ..., description="The main content of the prompt"
    )
    created_at: str = Field(
        default_factory=lambda: time.strftime("%Y-%m-%d %H:%M:%S"),
        description="Time when the prompt was created",
    )
    last_modified_at: str = Field(
        default_factory=lambda: time.strftime("%Y-%m-%d %H:%M:%S"),
        description="Time when the prompt was last modified",
    )
    edit_count: int = Field(
        default=0,
        description="The number of times the prompt has been edited",
    )
    edit_history: List[str] = Field(
        default_factory=list,
        description="The history of edits, storing all prompt versions",
    )
    autosave: bool = Field(
        default=False,
        description="Flag to enable or disable autosaving",
    )
    autosave_folder: str = Field(
        default="prompts",
        description="The folder path within WORKSPACE_DIR where the prompt will be autosaved",
    )
    auto_generate_prompt: bool = Field(
        default=False,
        description="Flag to enable or disable auto-generating the prompt",
    )
    parent_folder: str = Field(
        default=os.getenv("WORKSPACE_DIR"),
        description="The folder where the autosave folder is in",
    )
    llm: Any = None

    @validator("edit_history", pre=True, always=True)
    def initialize_history(cls, v, values):
        """
        Initializes the edit history by storing the first version of the prompt.
        """
        if not v:
            return [
                values["content"]
            ]  # Store initial version in history
        return v

    def __init__(self, **data):
        super().__init__(**data)

        if self.autosave:
            self._autosave()

        if self.auto_generate_prompt and self.llm:
            self.auto_generate_prompt()

    def edit_prompt(self, new_content: str) -> None:
        """
        Edits the prompt content and updates the version control.
        This method is thread-safe to prevent concurrent access issues.
        If autosave is enabled, it saves the prompt to the specified folder.

        Args:
            new_content (str): The updated content of the prompt.

        Raises:
            ValueError: If the new content is identical to the current content.
        """
        if new_content == self.content:
            logger.warning(
                f"Edit attempt failed: new content is identical to current content for prompt {self.id}"
            )
            raise ValueError(
                "New content must be different from the current content."
            )

        # logger.info(
        #     f"Editing prompt {self.id}. Current content: '{self.content}'"
        # )
        self.edit_history.append(new_content)
        self.content = new_content
        self.edit_count += 1
        self.last_modified_at = time.strftime("%Y-%m-%d %H:%M:%S")

        # logger.debug(
        #     f"Prompt {self.id} updated. Edit count: {self.edit_count}. New content: '{self.content}'"
        # )

        if self.autosave:
            self._autosave()

    def log_telemetry(self):
        system_data = capture_system_data()
        merged_data = {**system_data, **self.model_dump()}
        log_agent_data(merged_data)

    def rollback(self, version: int) -> None:
        """
        Rolls back the prompt to a previous version based on the version index.
        This method is thread-safe to prevent concurrent access issues.
        If autosave is enabled, it saves the prompt to the specified folder after rollback.

        Args:
            version (int): The version index to roll back to (0 is the first version).

        Raises:
            IndexError: If the version number is out of range.
        """
        if version < 0 or version >= len(self.edit_history):
            logger.error(
                f"Rollback failed: invalid version {version} for prompt {self.id}"
            )
            raise IndexError("Invalid version number for rollback.")

        # logger.info(
        #     f"Rolling back prompt {self.id} to version {version}."
        # )
        self.content = self.edit_history[version]
        self.edit_count = version
        self.last_modified_at = time.strftime("%Y-%m-%d %H:%M:%S")
        # logger.debug(
        #     f"Prompt {self.id} rolled back to version {version}. Current content: '{self.content}'"
        # )

        self.log_telemetry()

        if self.autosave:
            self._autosave()

    def return_json(self):
        return self.model_dump_json(indent=4)

    def get_prompt(self) -> str:
        """
        Returns the current prompt content as a string.

        Returns:
            str: The current prompt content.
        """
        # logger.debug(f"Returning prompt {self.id} as a string.")
        self.log_telemetry()

        return self.content

    def save_to_storage(self) -> None:
        """
        Placeholder method for saving the prompt to persistent storage.
        In a production environment, this would integrate with a database or file system.

        Raises:
            NotImplementedError: This method is a placeholder for storage integration.
        """
        # logger.info(f"Saving prompt {self.id} to persistent storage.")
        raise NotImplementedError(
            "Persistent storage integration is required."
        )

    def load_from_storage(
        self, prompt_id: str = uuid.uuid4().hex
    ) -> None:
        """
        Placeholder method for loading the prompt from persistent storage by its ID.
        In a production environment, this would integrate with a database or file system.

        Args:
            prompt_id (UUID): The unique identifier of the prompt to load.

        Raises:
            NotImplementedError: This method is a placeholder for storage integration.
        """
        # logger.info(
        #     f"Loading prompt {prompt_id} from persistent storage."
        # )
        raise NotImplementedError(
            "Persistent storage integration is required."
        )

    def add_tools(self, tools: List[Callable]) -> str:
        tools_prompt = BaseTool(
            tools=tools, tool_system_prompt=None
        ).convert_tool_into_openai_schema()
        self.content += "\n"
        self.content += "\n"
        self.content += tools_prompt

    def _autosave(self) -> None:
        """
        Autosaves the prompt to a specified folder within WORKSPACE_DIR.
        """
        workspace_dir = os.getenv("WORKSPACE_DIR")
        if not workspace_dir:
            logger.error(
                "WORKSPACE_DIR environment variable is not set."
            )
            return

        autosave_path = os.path.join(
            workspace_dir, self.autosave_folder
        )
        if not os.path.exists(autosave_path):
            os.makedirs(autosave_path)

        file_path = os.path.join(
            autosave_path, f"prompt-id-{self.id}.json"
        )
        with open(file_path, "w") as file:
            json.dump(self.model_dump(), file)
        # logger.info(f"Autosaved prompt {self.id} to {file_path}.")

        # return "Prompt autosaved successfully."

    # def auto_generate_prompt(self):
    #     logger.info(f"Auto-generating prompt for {self.name}")
    #     task = self.name + " " + self.description + " " + self.content
    #     prompt = auto_generate_prompt(task, llm=self.llm, max_tokens=4000, use_second_sys_prompt=True)
    #     logger.info("Generated prompt successfully, updating content")
    #     self.edit_prompt(prompt)
    #     logger.info("Prompt content updated")

    #     return "Prompt auto-generated successfully."

    class Config:
        """Pydantic configuration for better JSON serialization."""

        use_enum_values = True
        arbitrary_types_allowed = True
