# main.py

import logging
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from transformers import LlamaForCausalLM, LlamaTokenizer
import torch
import faiss
from googleapiclient.discovery import build
from config import YOUTUBE_API_KEY  # Importing the API key from config.py

# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Define a FastAPI application
app = FastAPI(
    title="YouTube Video Emotion Analyzer",
    description="Analyze emotions in YouTube video comments using LLaMA-2 and FAISS.",
    version="1.0.0"
)

# Define a Pydantic model for input data
class EmotionRequest(BaseModel):
    video_id: str  # Accepts the video ID from the request body

class EmotionAnalyzer:
    def __init__(self, model_name='meta-llama/Llama-2-7b-hf', knowledge_base=None, max_length=512):
        logger.info(f"Loading model: {model_name}")

        # Load the LLaMA-2 model and tokenizer
        self.model = LlamaForCausalLM.from_pretrained(model_name)
        self.tokenizer = LlamaTokenizer.from_pretrained(model_name)

        # Set pad_token to eos_token if missing
        if self.tokenizer.pad_token is None:
            self.tokenizer.pad_token = self.tokenizer.eos_token
  if self.tokenizer.pad_token is None:
            self.tokenizer.pad_token = self.tokenizer.eos_token

        # Knowledge base (FAISS index) for RAG
        self.knowledge_base = knowledge_base
        self.knowledge_base_texts = []  # Populate this elsewhere in your code
        self.max_length = max_length

    def retrieve_context(self, query_text):
        """Retrieve related context from the knowledge base using FAISS."""
        logger.info(f"Retrieving context for query: {query_text}")

        if self.knowledge_base is None:
            logger.warning("Knowledge base is None, returning empty context.")
            return ""

        # Get the embedding for the query text
        with torch.no_grad():
            inputs = self.tokenizer(query_text, return_tensors="pt")
            query_embedding = self.model.get_input_embeddings()(inputs['input_ids'])
            query_embedding = query_embedding.mean(dim=1).cpu().numpy()  # Averaging embeddings

        # Retrieve nearest context from FAISS index
        distances, indices = self.knowledge_base.search(query_embedding, k=1)
    
        try:
            retrieved_text = self.knowledge_base_texts[indices[0][0]]  # Ensure knowledge_base_texts is defined
            logger.info(f"Retrieved text: {retrieved_text}")
        except (IndexError, AttributeError) as e:  
            logger.error(f"Error retrieving text from knowledge_base_texts: {e}")
            retrieved_text = ""
        
        return retrieved_text

    def analyze_emotions(self, text):
        """Perform RAG-based emotion analysis."""
        logger.info(f"Analyzing emotions for text: {text}")
 """Perform RAG-based emotion analysis."""
        logger.info(f"Analyzing emotions for text: {text}")

        # Retrieve context using RAG method (retrieval)
        context = self.retrieve_context(text)
        
        # Create the prompt combining the original text and retrieved context
        prompt = f"Context: {context}\nText: {text}\nEmotion:"
        logger.info(f"Generated prompt: {prompt}")
        
        # Tokenize the prompt with max_length
        inputs = self.tokenizer(
            prompt,
            return_tensors="pt",
            padding=True,
            truncation=True,
            max_length=self.max_length  # Define max_length explicitly
        )
            
        # Generate the output using the LLaMA model
        outputs = self.model.generate(
            inputs['input_ids'],
            attention_mask=inputs['attention_mask'],
            max_new_tokens=50,
            pad_token_id=self.tokenizer.pad_token_id  # Use the pad_token_id directly
        )   
            
        # Decode and extract the emotion from the generated text
        generated_text = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
        logger.info(f"Generated text: {generated_text}")
            
        try:
            emotion = generated_text.split("Emotion:")[-1].strip().split()[0]
        except IndexError:
            logger.error("Error extracting emotion from the generated text.")
            emotion = "Unknown"
        
          emotion = "Unknown"
        
        return emotion, generated_text
        
# YouTube API setup
def get_youtube_service():
    """Build and return the YouTube service object."""
    return build('youtube', 'v3', developerKey=YOUTUBE_API_KEY)
        
def get_video_comments(video_id):
    """Fetch comments from a YouTube video using the video_id."""
    youtube = get_youtube_service()
    comments = []  
            
    try:
        response = youtube.commentThreads().list(
            part='snippet',
            videoId=video_id,
            textFormat='plainText',
            maxResults=100  # Adjust this value as needed
        ).execute()
            
        # Process the response to extract comments  
        for item in response.get('items', []):
            comment = item['snippet']['topLevelComment']['snippet']['textDisplay']   
            comments.append(comment)
            
        # Handle pagination if more comments are available
        while 'nextPageToken' in response:
            response = youtube.commentThreads().list(   
                part='snippet',
                videoId=video_id,
                textFormat='plainText',
                maxResults=100,
                pageToken=response['nextPageToken']
            ).execute()
        
           ).execute()
        
            for item in response.get('items', []):
                comment = item['snippet']['topLevelComment']['snippet']['textDisplay']
                comments.append(comment)

    except Exception as e:
        logger.error(f"Error fetching comments: {str(e)}")
        
    return comments
    
# Instantiate the EmotionAnalyzer  
# TODO: Load your FAISS index and texts here if using RAG
knowledge_base = None  # Replace with your FAISS index if available
emotion_analyzer = EmotionAnalyzer(knowledge_base=knowledge_base)
        
@app.post("/analyze_emotion/", summary="Analyze emotions in YouTube video comments")
async def analyze_emotion(request: EmotionRequest):
    """
    Analyze emotions for comments from a YouTube video.  
        
    - **video_id**: The ID of the YouTube video to analyze
    """
    logger.info(f"Received request to analyze video_id: {request.video_id}")
            
    comments = get_video_comments(request.video_id)
            
    if not comments:
        logger.warning(f"No comments found for video_id: {request.video_id}")
        raise HTTPException(status_code=404, detail="No comments found for this video ID.")
                
    results = []
    for comment in comments:
        emotion, generated_text = emotion_analyzer.analyze_emotions(comment)
        results.append({
            "comment": comment,
            "emotion": emotion,          "comment": comment,
            "emotion": emotion,
            "generated_text": generated_text
        })
                
    return {
        "video_id": request.video_id,
        "analysis": results
    }   
    
# Optional: Root endpoint with a welcome message
@app.get("/", summary="Welcome")   
async def root():
    return {"message": "Welcome to the YouTube Video Emotion Analyzer API. Use the /analyze_emotion/ endpoint to analyze emotions in video comments."}

# Run the application
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8000)

when I run this code on fast api it has errors what is the problem