# Install Required Libraries
!pip install transformers torch beautifulsoup4 requests nltk

# Import Libraries and Initialize Components
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
import requests
from bs4 import BeautifulSoup
import nltk
from nltk.tokenize import sent_tokenize
import re
import time

# Download NLTK data
nltk.download('punkt')

# Initialize the tokenizer and model
tokenizer = AutoTokenizer.from_pretrained("microsoft/DialoGPT-large")
model = AutoModelForCausalLM.from_pretrained("microsoft/DialoGPT-large")

# Advanced Chatbot Class
class AdvancedChatbot:
    def __init__(self, tokenizer, model):
        self.tokenizer = tokenizer
        self.model = model
        self.conversation_history = []
        self.knowledge_base = {}
    
    def generate_response(self, user_input):
        # Encode the user input and append to the conversation history
        new_user_input_ids = self.tokenizer.encode(user_input + self.tokenizer.eos_token, return_tensors='pt')
        self.conversation_history.append(new_user_input_ids)
        
        # Concatenate the conversation history
        bot_input_ids = torch.cat(self.conversation_history, dim=-1)
        
        # Generate a response
        chat_history_ids = self.model.generate(
            bot_input_ids,
            max_length=1000,
            pad_token_id=self.tokenizer.eos_token_id,
            do_sample=True,
            top_p=0.95,
            top_k=50
        )
        
        # Decode the response
        response = self.tokenizer.decode(chat_history_ids[:, bot_input_ids.shape[-1]:][0], skip_special_tokens=True)
        
        # Append to conversation history
        self.conversation_history.append(chat_history_ids[:, bot_input_ids.shape[-1]:])
        
        return response
    
    def search_online(self, query):
        # Simple web search using DuckDuckGo (no API required)
        search_url = f"https://html.duckduckgo.com/html/?q={requests.utils.quote(query)}"
        response = requests.get(search_url)
        soup = BeautifulSoup(response.text, 'html.parser')
        results = soup.find_all('a', {'class': 'result__a'}, href=True)
        snippets = []
        for result in results[:5]:
            snippets.append(result.text)
        return ' '.join(snippets)
    
    def self_validate(self, response, user_input):
        # Simple validation by checking response length and relevance
        if len(response) < 20:
            return False
        # Check for relevance (basic keyword matching)
        keywords = user_input.split()
        relevance = any(word.lower() in response.lower() for word in keywords)
        return relevance
    
    def handle_query(self, user_input):
        response = self.generate_response(user_input)
        if not self.self_validate(response, user_input):
            # Perform an online search to find better information
            search_result = self.search_online(user_input)
            if search_result:
                response = self.generate_response(search_result)
        return response
    
    def run(self):
        print("Hello! I am your advanced chatbot. How can I assist you today?")
        while True:
            user_input = input("You: ")
            if user_input.lower() in ['exit', 'quit', 'bye']:
                print("Chatbot: Goodbye!")
                break
            response = self.handle_query(user_input)
            print(f"Chatbot: {response}")
            time.sleep(1)  # Added delay for better user experience

# Initialize and run the chatbot
chatbot = AdvancedChatbot(tokenizer, model)
chatbot.run()