# patent_7_hallucination_detection.py

import nltk
import spacy
import requests
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

# Download necessary NLTK resources (run this once)
# nltk.download('punkt')
# nltk.download('averaged_perceptron_tagger')
# nltk.download('maxent_ne_chunker')
# nltk.download('words')


# Load a suitable spaCy model (e.g., en_core_web_sm, en_core_web_lg)
# You may need to download it first: python -m spacy download en_core_web_sm
nlp = spacy.load("en_core_web_sm")  # Consider a larger model for better accuracy


class HallucinationDetector:
    """
    Detects potential hallucinations in AI output, verifies claims against a knowledge base,
    scores hallucination probability, and flags/quarantines suspicious content.
    """

    def __init__(self, knowledge_base_url, similarity_threshold=0.7, hallucination_threshold=0.8):
        """
        Initializes the HallucinationDetector.

        Args:
            knowledge_base_url (str): URL of the knowledge base API endpoint.  Assumes the KB accepts a query
                                         and returns relevant documents. Returns JSON with a 'results' key
                                         containing a list of text strings.
            similarity_threshold (float): Cosine similarity threshold for claim verification (0-1).
            hallucination_threshold (float): Hallucination probability threshold for flagging content (0-1).
        """
        self.knowledge_base_url = knowledge_base_url
        self.similarity_threshold = similarity_threshold
        self.hallucination_threshold = hallucination_threshold

    def detect_hallucinations(self, ai_output):
        """
        Detects potential hallucinations in AI output.

        Args:
            ai_output (str): The AI-generated text to analyze.

        Returns:
            dict: A dictionary containing analysis results, including:
                - 'hallucinations': A list of potential hallucination flags (True/False) for each claim.
                - 'claim_verifications': A list of verification results for each claim.
                - 'hallucination_scores': A list of hallucination probability scores for each claim.
                - 'quarantine_flag': A boolean indicating whether the entire output should be quarantined.
                - 'flagged_claims': A list of the claims that were flagged as potential hallucinations
        """

        claims = self.extract_claims(ai_output)
        hallucinations = []
        claim_verifications = []
        hallucination_scores = []
        flagged_claims = []

        for claim in claims:
            verification_result = self.verify_claim(claim)
            claim_verifications.append(verification_result)

            hallucination_score = self.calculate_hallucination_score(verification_result)
            hallucination_scores.append(hallucination_score)

            is_hallucination = hallucination_score > self.hallucination_threshold
            hallucinations.append(is_hallucination)

            if is_hallucination:
                flagged_claims.append(claim)

        quarantine_flag = any(hallucinations)  # Quarantine if ANY claim is a hallucination

        return {
            'hallucinations': hallucinations,
            'claim_verifications': claim_verifications,
            'hallucination_scores': hallucination_scores,
            'quarantine_flag': quarantine_flag,
            'flagged_claims': flagged_claims
        }

    def extract_claims(self, text):
        """
        Extracts factual claims from the given text using simple sentence splitting.
        More sophisticated methods (NER, dependency parsing) could be used for better accuracy.

        Args:
            text (str): The text to extract claims from.

        Returns:
            list: A list of strings, where each string is a potential factual claim.
        """
        sentences = nltk.sent_tokenize(text)
        return sentences

    def verify_claim(self, claim):
        """
        Verifies a claim against the knowledge base.

        Args:
            claim (str): The claim to verify.

        Returns:
            dict: A dictionary containing verification results, including:
                - 'is_supported': A boolean indicating whether the claim is supported by the knowledge base.
                - 'supporting_evidence': A list of supporting documents from the knowledge base.
                - 'similarity_score': The maximum cosine similarity score between the claim and the supporting evidence.
        """

        try:
            response = requests.get(self.knowledge_base_url, params={'query': claim})
            response.raise_for_status()  # Raise HTTPError for bad responses (4xx or 5xx)
            data = response.json()

            if 'results' not in data or not isinstance(data['results'], list):
                print(f"Warning: Unexpected knowledge base response format: {data}")
                return {
                    'is_supported': False,
                    'supporting_evidence': [],
                    'similarity_score': 0.0
                }

            knowledge_base_documents = data['results']
        except requests.exceptions.RequestException as e:
            print(f"Error querying knowledge base: {e}")
            return {
                'is_supported': False,
                'supporting_evidence': [],
                'similarity_score': 0.0
            }


        if not knowledge_base_documents:
            return {
                'is_supported': False,
                'supporting_evidence': [],
                'similarity_score': 0.0
            }

        claim_embedding = self.get_embedding(claim)
        max_similarity = 0.0
        best_supporting_evidence = []

        for document in knowledge_base_documents:
            document_embedding = self.get_embedding(document)
            similarity = cosine_similarity([claim_embedding], [document_embedding])[0][0]
            if similarity > max_similarity:
                max_similarity = similarity
                best_supporting_evidence = [document] # Replace with the best evidence found so far

        is_supported = max_similarity >= self.similarity_threshold

        return {
            'is_supported': is_supported,
            'supporting_evidence': best_supporting_evidence,
            'similarity_score': max_similarity
        }

    def calculate_hallucination_score(self, verification_result):
        """
        Calculates a hallucination probability score based on the verification result.

        Args:
            verification_result (dict): The result of the claim verification.

        Returns:
            float: A hallucination probability score (0-1).  Higher score indicates higher probability of hallucination.
        """
        if verification_result['is_supported']:
            return 1.0 - verification_result['similarity_score'] # Lower similiarity, higher hallucination score
        else:
            return 1.0  # Not supported, high hallucination probability

    def get_embedding(self, text):
        """
        Generates a text embedding using spaCy.

        Args:
            text (str): The text to embed.

        Returns:
            numpy.ndarray: The text embedding.
        """
        doc = nlp(text)
        return doc.vector


# Example Usage (replace with your actual knowledge base and AI output)
if __name__ == '__main__':
    # Mock knowledge base (replace with a real API endpoint)
    MOCK_KNOWLEDGE_BASE_URL = "https://example.com/knowledge_base"  # Replace with your actual KB URL

    def mock_knowledge_base(query):
        """
        A simple mock knowledge base for testing.
        Returns relevant documents based on the query.
        """
        if "capital of France" in query:
            return {"results": ["Paris is the capital of France."]}
        elif "invented the telephone" in query:
            return {"results": ["Alexander Graham Bell invented the telephone."]}
        elif "first man on the moon" in query:
            return {"results": ["Neil Armstrong was the first man to walk on the Moon."]}
        elif "invented the light bulb" in query:
            return {"results": ["Thomas Edison is credited with inventing the practical light bulb."]} # Corrected
        else:
            return {"results": []} # No relevant documents

    # Monkey patch requests.get for testing
    import requests
    def mock_requests_get(url, params=None):
        class MockResponse:
            def __init__(self, json_data, status_code):
                self.json_data = json_data
                self.status_code = status_code

            def json(self):
                return self.json_data

            def raise_for_status(self):
                if self.status_code >= 400:
                    raise requests.exceptions.HTTPError(f"HTTP Error: {self.status_code}")

        query = params['query'] if params else None
        json_data = mock_knowledge_base(query)
        return MockResponse(json_data, 200)

    requests.get = mock_requests_get


    detector = HallucinationDetector(
        knowledge_base_url=MOCK_KNOWLEDGE_BASE_URL,
        similarity_threshold=0.75,
        hallucination_threshold=0.8
    )


    ai_output_1 = "The capital of France is Paris.  Alexander Graham Bell invented the telephone.  The first man on the moon was Neil Armstrong."
    ai_output_2 = "The capital of Germany is Berlin.  Einstein invented the light bulb.  The first woman on Mars was Valentina Tereshkova."
    ai_output_3 = "The capital of France is Paris. Thomas Edison invented the light bulb."

    print("Analyzing AI Output 1:")
    result1 = detector.detect_hallucinations(ai_output_1)
    print(result1)

    print("\nAnalyzing AI Output 2:")
    result2 = detector.detect_hallucinations(ai_output_2)
    print(result2)

    print("\nAnalyzing AI Output 3:")
    result3 = detector.detect_hallucinations(ai_output_3)
    print(result3)
