import os
import sys
import json
import re
import math

# === Step 1: Check if `requests` is installed ===
try:
    import requests
except ImportError:
    print("\n⚠️ `requests` module not found! Checking internet connection...\n")
    def check_connection():
        try:
            # Ping Bing as a connection test
            os.system("ping -c 1 bing.com >nul 2>&1")  # Linux/macOS
            os.system("ping -n 1 bing.com >nul 2>&1")   # Windows
            return True
        except Exception:
            return False

    online = check_connection()
    if online:
        print("\n🌐 Internet detected! Installing `requests`...\n")
        os.system("pip install requests")
        try:
            import requests
            print("\n✅ `requests` installed successfully!\n")
        except ImportError:
            print("\n🚨 Installation failed! Please install manually: `pip install requests`\n")
            sys.exit(1)
    else:
        print("\n🚨 No internet detected! Running in offline mode...\n")
        # Create a fake requests module to avoid crashes
        class FakeRequests:
            def get(self, url, timeout=5):
                return None
        requests = FakeRequests()

# === Step 2: Define the CGG-EUSO Chatbot with Enhanced Thinking Chain System ===
class CGG_EUSO:
    def __init__(self):
        self.knowledge_file = "knowledge.json"
        self.responses_file = "responses.json"
        self.load_data()

    def load_data(self):
        """Load stored knowledge and responses from files."""
        try:
            with open(self.knowledge_file, "r", encoding="utf-8") as f:
                self.knowledge = json.load(f)
        except FileNotFoundError:
            self.knowledge = {}
        try:
            with open(self.responses_file, "r", encoding="utf-8") as f:
                self.responses = json.load(f)
        except FileNotFoundError:
            self.responses = {}

    def save_data(self):
        """Save learned knowledge and responses to files."""
        with open(self.knowledge_file, "w", encoding="utf-8") as f:
            json.dump(self.knowledge, f, ensure_ascii=False, indent=4)
        with open(self.responses_file, "w", encoding="utf-8") as f:
            json.dump(self.responses, f, ensure_ascii=False, indent=4)

    def check_online(self):
        """Check if the computer is online using Bing."""
        try:
            requests.get("http://www.bing.com", timeout=5)
            return True
        except Exception:
            return False

    def thinking_chain(self, raw_text):
        """
        Processes raw online data to extract key points with multi-step reasoning.
        - Removes HTML tags and extra spaces.
        - Splits the text into sentences.
        - Selects the first few sentences as the summary.
        - Simplifies complex information.
        """
        # Remove HTML tags and extra whitespace
        clean_text = re.sub(r'<.*?>', ' ', raw_text)
        clean_text = re.sub(r'\s+', ' ', clean_text).strip()
        # Split into sentences
        sentences = re.split(r'(?<=[.!?]) +', clean_text)
        # Take first 3 sentences as key points (if available)
        summary = " ".join(sentences[:3])
        # If too long, shorten it
        if len(summary) > 300:
            summary = summary[:300] + "..."
        return summary

    def search_bing(self, query):
        """Search Bing for the query and return processed result."""
        try:
            response = requests.get(f"https://www.bing.com/search?q={query}", timeout=5)
            raw_text = response.text
            processed_text = self.thinking_chain(raw_text)
            return processed_text
        except Exception:
            return ""

    def search_duckduckgo(self, query):
        """Search DuckDuckGo using its API for the query."""
        try:
            url = f"https://api.duckduckgo.com/?q={query}&format=json"
            response = requests.get(url, timeout=5)
            data = response.json()
            abstract = data.get("AbstractText", "").strip()
            if abstract:
                return abstract
            # Fallback: process raw response if no abstract provided
            return self.thinking_chain(response.text)
        except Exception:
            return ""

    def similarity(self, text1, text2):
        """
        Compute a simple similarity score between two texts based on word overlap.
        Returns a value between 0 and 1.
        """
        words1 = set(text1.lower().split())
        words2 = set(text2.lower().split())
        if not words1 or not words2:
            return 0
        common = words1.intersection(words2)
        return len(common) / max(len(words1), len(words2))

    def fact_check(self, query):
        """
        Searches both Bing and DuckDuckGo, compares the results,
        and returns a merged accurate answer.
        """
        result_bing = self.search_bing(query)
        result_ddg = self.search_duckduckgo(query)
        # If both results are non-empty, compare similarity
        if result_bing and result_ddg:
            sim = self.similarity(result_bing, result_ddg)
            if sim >= 0.5:
                # Merge by choosing the longer summary as it might be more complete
                merged = result_bing if len(result_bing) >= len(result_ddg) else result_ddg
                return merged
            else:
                # If they differ a lot, favor the one with "definition" keyword or fallback to Bing
                if "definition" in result_ddg.lower():
                    return result_ddg
                return result_bing
        elif result_bing:
            return result_bing
        elif result_ddg:
            return result_ddg
        else:
            return "No reliable information found online."

    def learn_knowledge(self, topic, info):
        """Adds new knowledge if not already stored or updates if new info is better."""
        # Update if not present, or if new info is longer (assumed better)
        if topic not in self.knowledge or len(info) > len(self.knowledge[topic]):
            self.knowledge[topic] = info
            self.save_data()

    def learn_response(self, question, answer):
        """Learns how to respond to the user."""
        if question not in self.responses:
            self.responses[question] = answer
            self.save_data()

    def predict_questions(self, user_input):
        """
        Suggests related questions based on stored knowledge.
        If similar questions exist, returns a list of suggestions.
        """
        suggestions = []
        for question in self.knowledge.keys():
            # Simple similarity: if the query is a substring or has high word overlap
            if user_input.lower() in question.lower() or self.similarity(user_input, question) > 0.3:
                suggestions.append(question)
        return suggestions

    def respond(self, user_input):
        """
        Generates a response:
          - If a learned response exists, returns it.
          - Else, if knowledge is stored, returns that.
          - Otherwise, if online, fact-checks the answer from the whole internet using our Thinking Chain.
          - If offline, prompts the user for input.
          - Also predicts and suggests related questions.
        """
        if user_input in self.responses:
            answer = self.responses[user_input]
        elif user_input in self.knowledge:
            answer = f"I found this in my knowledge: {self.knowledge[user_input]}"
        else:
            if self.check_online():
                answer = self.fact_check(user_input)
                self.learn_knowledge(user_input, answer)
            else:
                print("\n🤔 I don't know this yet. Please teach me!")
                new_response = input("Enter a response: ")
                self.learn_response(user_input, new_response)
                answer = "Got it! I'll remember this."
        # After answering, suggest related questions if available.
        suggestions = self.predict_questions(user_input)
        if suggestions:
            answer += "\n\nYou might also be interested in these related topics:\n" + "\n".join(suggestions)
        return answer

    def show_code(self):
        """Displays stored knowledge and responses (for debugging)."""
        print("\n📜 **CGG-EUSO Code Check Mode** 📜")
        print("\nKnowledge stored:")
        print(json.dumps(self.knowledge, ensure_ascii=False, indent=4))
        print("\nResponses stored:")
        print(json.dumps(self.responses, ensure_ascii=False, indent=4))

# === Step 3: Run the Chatbot ===
bot = CGG_EUSO()

print("\n💬 CGG-EUSO is ready! Choose a mode:")
print("1️⃣ Chat Mode")
print("2️⃣ Code Check Mode")

while True:
    mode = input("\nSelect (1/2): ").strip()
    if mode == "1":
        print("\n💬 Enter 'exit' to quit.\n")
        while True:
            user_input = input("You: ")
            if user_input.lower() == "exit":
                print("\nGoodbye! 👋")
                break
            response = bot.respond(user_input)
            print(f"CGG-EUSO: {response}")
        break
    elif mode == "2":
        bot.show_code()
        break
    else:
        print("\n❌ Invalid choice! Please enter '1' or '2'.")