import logging
from langchain_experimental.graph_transformers import LLMGraphTransformer
from langchain_openai import ChatOpenAI
from django.conf import settings
from langchain_community.graphs import Neo4jGraph
from langchain.schema import Document
from .models import Neo4jProfile
from datetime import datetime
from langchain.prompts import ChatPromptTemplate
from langchain_community.chains.graph_qa.cypher import GraphCypherQAChain


logger = logging.getLogger(__name__)

DEFAULT_PROFILE_NAME = "DefaultNeo4jProfile"  # Define a default profile

class Neo4jDatabase:
    """
    Handles connection to Neo4j using different profiles for various use cases.
    """

    def __init__(self, profile_name=None):
        try:
            # Load the specified Neo4j profile, or fallback to the default
            if profile_name:
                self.profile = Neo4jProfile.objects.get(name=profile_name)
            else:
                logger.warning("No profile specified. Using default Neo4j profile.")
                self.profile = Neo4jProfile.objects.get(name=DEFAULT_PROFILE_NAME)

            # Connect to the assigned Neo4j instance
            self.graph = Neo4jGraph(
                url=self.profile.uri,
                username=self.profile.username,
                password=self.profile.password
            )

            # Load the correct AI model
            self.llm = ChatOpenAI(
                api_key=self.profile.openai_api_key,
                model_name=self.profile.model_name,
                temperature=0
            )

            # Add the custom prompt to enforce English
            english_prompt = ChatPromptTemplate.from_template(
                "Transform the following text into a graph structure. All nodes, relationships, and properties should be in English, regardless of the original language."
            )

            # Apply the custom English prompt here
            self.graph_transformer = LLMGraphTransformer(
                llm=self.llm,
                prompt=english_prompt
            )

            logger.info(f"Neo4jDatabase initialized with profile: {self.profile.name}")

        except Neo4jProfile.DoesNotExist:
            logger.error(f"Neo4j profile '{profile_name}' not found.")
            raise
        except Exception as e:
            logger.error(f"Failed to initialize Neo4jDatabase: {str(e)}")
            raise

    def store_interaction(self, user_id, bot_id, user_message, bot_response, platform):
        """
        Stores a chatbot interaction as a structured graph in Neo4j.
        Converts messages into `Document` objects to work with `LLMGraphTransformer`.
        """
        try:

            timestamp = datetime.utcnow().isoformat()

            # Convert messages into `Document` objects
            documents = [
                Document(page_content=user_message, metadata={"role": "user", "user_id": user_id, "platform": platform, "created_at": timestamp}),
                Document(page_content=bot_response, metadata={"role": "bot", "bot_id": bot_id, "platform": platform, "created_at": timestamp}),
            ]

            # Convert text into structured graph documents
            graph_docs = self.graph_transformer.convert_to_graph_documents(documents)

            # Store the structured graph data into Neo4j
            self.graph.add_graph_documents(graph_docs, include_source=True)

            logger.info(f"Stored interaction in Neo4j (Profile: {self.profile.name})")
        except Exception as e:
            logger.error(f"Failed to store interaction in Neo4j: {str(e)}")
            raise

    def query_graph(self, user_query):
        """
        Queries the graph using GraphCypherQAChain and returns a structured response.
        """
        logger.info(f"Calling Neo4j {user_query}")
        try:
            # Use AI model to generate Cypher query
            qa_chain = GraphCypherQAChain.from_llm(
                llm=self.llm,
                graph=self.graph,
                verbose=True,
                allow_dangerous_requests=True
            )

            result = qa_chain.invoke({"query": user_query})
            logger.info(f"Resulting Neo4j {result}")
            return result['result']

        except Exception as e:
            logger.error(f"Graph query failed: {e}")
            return None
