from pymilvus import Collection, CollectionSchema, FieldSchema, DataType, connections, utility
from sentence_transformers import SentenceTransformer

class DB:
    def __init__(self, collection_name="demo", host="localhost", port="19530", model_name='sentence-transformers/all-MiniLM-L6-v2'):
        # Connect to Milvus server
        connections.connect("default", host=host, port=port)
        
        self.collection_name = collection_name
        self.model = SentenceTransformer(model_name)
        self.dimension = self.model.get_sentence_embedding_dimension()

        # Check if collection exists, and create it if it doesn't
        if not utility.has_collection(self.collection_name):
            self._create_collection()
        else:
            self.collection = Collection(self.collection_name)
            self.collection.load()

    def _create_collection(self):
        # Define the fields for the collection schema
        fields = [
            FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=False),  # Set auto_id to False for custom ids
            FieldSchema(name="vector", dtype=DataType.FLOAT_VECTOR, dim=self.dimension),
            FieldSchema(name="content", dtype=DataType.VARCHAR, max_length=500)  # Content storage field
        ]
        schema = CollectionSchema(fields=fields, enable_dynamic_field=True)
        self.collection = Collection(self.collection_name, schema=schema)

        # Create index on the vector field
        index_params = {
            "index_type": "IVF_FLAT",
            "metric_type": "COSINE",
            "params": {"nlist": 128}
        }
        self.collection.create_index(field_name="vector", index_params=index_params)
        print(f"Collection '{self.collection_name}' created successfully.")

    def add_documents(self, documents):
        # Prepare data for insertion
        ids = []  # Optional: You can let Milvus auto-generate ids if needed.
        vectors = []
        contents = []

        for i, doc in enumerate(documents):
            if isinstance(doc, str):
                text_content = doc  # Use string content directly
            else:
                text_content = doc.page_content  # Assuming doc has a 'page_content' attribute

            # Encode document into a vector
            embeddings = self.model.encode(text_content)
            vector = embeddings.tolist()
            
            # Add the id, vector, and content to the lists
            ids.append(i)  # Add unique id for each document
            vectors.append(vector)
            contents.append(text_content)

        # Insert data into the collection
        data = [
            ids,       # List of ids
            vectors,   # List of vectors
            contents   # List of content strings
        ]
        self.collection.insert(data)
        print(f"Added {len(documents)} documents to collection '{self.collection_name}'.")

    def search_documents(self, query, limit=10):
        # Encode the query into a vector
        query_embedding = self.model.encode(query).tolist()

        # Define search parameters
        search_params = {"metric_type": "COSINE", "params": {"nprobe": 10}}

        # Perform search in the collection
        results = self.collection.search(
            [query_embedding],
            "vector",
            param=search_params,
            limit=limit,
            output_fields=["id", "content"]
        )

        # Parse and return search results
        search_results = []
        if results:
            for result in results[0]:
                search_results.append({"id": result.id, "content": result.entity.get("content")})
        
        return search_results

    def get_document_by_id(self, doc_id):
        # Retrieve a document by its ID
        results = self.collection.query(expr=f"id == {doc_id}", output_fields=["id", "content"])
        if results:
            return results[0]
        else:
            print(f"No document found with ID {doc_id}.")
            return None

    def delete_document_by_id(self, doc_id):
        # Delete a document by its ID
        try:
            self.collection.delete(expr=f"id == {doc_id}")
            print(f"Document with ID {doc_id} deleted successfully.")
        except Exception as e:
            print(f"Error deleting document with ID {doc_id}: {str(e)}")

    def drop_collection(self):
        # Drop the entire collection
        if utility.has_collection(self.collection_name):
            utility.drop_collection(self.collection_name)
            print(f"Collection '{self.collection_name}' dropped successfully.")

    def list_collections(self):
        # List all collections in the database
        return utility.list_collections()

    def list_documents(self):
        # List all documents in the collection
        results = self.collection.query(expr="id >= 0", output_fields=["id", "content"])
        return results

    def count_documents(self):
        # Count the number of documents in the collection
        return self.collection.num_entities

    def create_index(self):
        # Create index on the vector field
        index_params = {
            "index_type": "IVF_FLAT",
            "metric_type": "COSINE",
            "params": {"nlist": 128}
        }
        self.collection.create_index(field_name="vector", index_params=index_params)
        print(f"Index created successfully for collection '{self.collection_name}'.")

    def load_collection(self):
        # Load the collection into memory
        self.collection.load()

    def release_collection(self):
        # Release the collection from memory
        self.collection.release()

"implement knowledge graph in this code"