"""Chat Explainer - Context-aware diagnostic chat assistant with real AI."""

import os
import json
import logging
import requests
from typing import Dict, List, Any, Optional
from doctor.diagnostics.symptoms import Symptom
from doctor.diagnostics.rules import DiagnosticRule
from doctor.core.os_detect import detect_os


logger = logging.getLogger(__name__)


SYSTEM_PROMPT = """You are the Device Doctor Assistant, a diagnostic explainer embedded in a read-only system diagnostics dashboard.

You do NOT execute fixes.
You do NOT modify the system.
You ONLY explain diagnostic findings.

For any issue, you must:
- Explain what it is
- Explain why it appears
- State whether it is normal or problematic
- Describe user impact (if any)
- Suggest SAFE, MANUAL actions only
- Share best practices
- State confidence and diagnostic limits

Rules:
- Do not assume all findings are problems
- Distinguish primary vs virtual/system components
- Correlate only relevant metrics
- Never mix unrelated signals
- Avoid alarmist language
- Say "No action required" when appropriate
- Be calm, clinical, and developer-friendly

Confidence must be LOW / MEDIUM / HIGH
and justified using relevant evidence only.

You are a diagnostic explainer, not a fixer."""


class ChatExplainer:
    """Handles context-aware chat explanations using real AI."""
    
    def __init__(
        self, 
        api_key: Optional[str] = None, 
        model: Optional[str] = None,
        provider: str = "ollama"
    ):
        """
        Initialize chat explainer.
        
        Args:
            api_key: API key (required for OpenAI, Gemini, Claude; optional for Ollama).
            model: Model to use. Defaults:
                - ollama: "llama3.2"
                - openai: "gpt-4o-mini"
                - gemini: "gemini-pro"
                - claude: "claude-3-haiku-20240307"
            provider: AI provider - "ollama" (free), "openai", "gemini", or "claude".
        """
        self.provider = provider.lower()
        self.api_key = api_key
        
        # Set default models per provider
        default_models = {
            "ollama": "llama3.2",
            "openai": "gpt-4o-mini",
            "gemini": "gemini-pro",
            "claude": "claude-3-haiku-20240307"
        }
        self.model = model or default_models.get(self.provider, "llama3.2")
        
        # Initialize provider-specific settings
        if self.provider == "ollama":
            self.use_ai = True
            self.ollama_url = os.getenv("OLLAMA_URL", "http://localhost:11434")
            logger.info(f"Using Ollama (free, local) at {self.ollama_url}")
            logger.info(f"Model: {self.model}")
        elif self.provider == "openai":
            self.api_key = api_key or os.getenv("OPENAI_API_KEY")
            self.use_ai = bool(self.api_key)
            if not self.use_ai:
                logger.warning("No OpenAI API key found. Falling back to rule-based.")
        elif self.provider == "gemini":
            self.api_key = api_key or os.getenv("GEMINI_API_KEY")
            self.use_ai = bool(self.api_key)
            if not self.use_ai:
                logger.warning("No Gemini API key found. Falling back to rule-based.")
        elif self.provider == "claude":
            self.api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
            self.use_ai = bool(self.api_key)
            if not self.use_ai:
                logger.warning("No Claude API key found. Falling back to rule-based.")
        else:
            logger.warning(f"Unknown provider: {self.provider}. Falling back to rule-based.")
            self.use_ai = False
        
        if self.use_ai:
            logger.info(f"Using {self.provider.upper()} with model: {self.model}")
    
    def explain(
        self,
        question: str,
        selected_issue: Optional[Dict[str, Any]],
        dashboard_state: Dict[str, Any]
    ) -> str:
        """
        Generate contextual explanation for a user question.
        
        Args:
            question: User's question.
            selected_issue: Selected issue context (if any).
            dashboard_state: Complete dashboard state.
            
        Returns:
            Explanation text.
        """
        if self.use_ai:
            return self._explain_with_ai(question, selected_issue, dashboard_state)
        else:
            return self._explain_rule_based(question, selected_issue, dashboard_state)
    
    def _explain_with_ai(
        self,
        question: str,
        selected_issue: Optional[Dict[str, Any]],
        dashboard_state: Dict[str, Any]
    ) -> str:
        """Generate explanation using real AI."""
        user_prompt = self._build_user_prompt(question, selected_issue, dashboard_state)
        
        if self.provider == "ollama":
            return self._call_ollama(user_prompt)
        elif self.provider == "openai":
            return self._call_openai(user_prompt)
        elif self.provider == "gemini":
            return self._call_gemini(user_prompt)
        elif self.provider == "claude":
            return self._call_claude(user_prompt)
        else:
            return self._explain_rule_based(question, selected_issue, dashboard_state)
    
    def _call_ollama(self, user_prompt: str) -> str:
        """Call Ollama API (free, local)."""
        try:
            # Combine system prompt and user prompt
            full_prompt = f"{SYSTEM_PROMPT}\n\n{user_prompt}"
            
            response = requests.post(
                f"{self.ollama_url}/api/generate",
                json={
                    "model": self.model,
                    "prompt": full_prompt,
                    "stream": False,
                    "options": {
                        "temperature": 0.7,
                        "num_predict": 1000
                    }
                },
                timeout=60  # Increased timeout for model loading
            )
            
            if response.status_code == 200:
                result = response.json()
                answer = result.get("response", "").strip()
                if not answer:
                    logger.warning("Ollama returned empty response")
                    return self._get_ollama_fallback_message()
                logger.info("Ollama AI response generated successfully")
                return answer
            else:
                error_msg = response.text[:200] if response.text else "Unknown error"
                logger.error(f"Ollama API error: {response.status_code} - {error_msg}")
                return self._get_ollama_fallback_message()
                
        except requests.exceptions.ConnectionError as e:
            logger.error(f"Cannot connect to Ollama at {self.ollama_url}: {e}")
            return self._get_ollama_fallback_message()
        except requests.exceptions.Timeout:
            logger.error("Ollama request timed out. Model may be loading.")
            return "The AI model is taking longer than expected. Please try again in a moment, or ensure Ollama is running: `ollama serve`"
        except Exception as e:
            logger.error(f"Ollama request failed: {e}", exc_info=True)
            return self._get_ollama_fallback_message()
    
    def _get_ollama_fallback_message(self) -> str:
        """Get helpful message when Ollama is not available."""
        return """I'm configured to use Ollama (free, local AI), but it's not running.

**To enable free AI chat:**

1. Install Ollama: https://ollama.ai
2. Start Ollama: `ollama serve`
3. Pull a model: `ollama pull llama3.2`
4. Refresh this page

**Alternative:** Set OPENAI_API_KEY for OpenAI (paid).

For now, I'll use rule-based responses."""
    
    def _call_openai(self, user_prompt: str) -> str:
        """Call OpenAI API (paid)."""
        try:
            from openai import OpenAI
            
            client = OpenAI(api_key=self.api_key)
            
            # Call AI API
            response = client.chat.completions.create(
                model=self.model,
                messages=[
                    {"role": "system", "content": SYSTEM_PROMPT},
                    {"role": "user", "content": user_prompt}
                ],
                temperature=0.7,
                max_tokens=1000
            )
            
            answer = response.choices[0].message.content.strip()
            logger.info("OpenAI chat response generated successfully")
            return answer
            
        except ImportError:
            logger.error("OpenAI library not installed. Install with: pip install openai")
            return "Error: OpenAI library not installed. Please install it or use a different provider."
        except Exception as e:
            logger.error(f"OpenAI request failed: {e}")
            return f"Error calling OpenAI: {str(e)[:200]}. Please check your API key and try again."
    
    def _call_gemini(self, user_prompt: str) -> str:
        """Call Google Gemini API."""
        try:
            import google.generativeai as genai
            
            genai.configure(api_key=self.api_key)
            model = genai.GenerativeModel(self.model)
            
            # Combine system prompt and user prompt
            full_prompt = f"{SYSTEM_PROMPT}\n\n{user_prompt}"
            
            response = model.generate_content(
                full_prompt,
                generation_config={
                    "temperature": 0.7,
                    "max_output_tokens": 1000,
                }
            )
            
            answer = response.text.strip()
            logger.info("Gemini chat response generated successfully")
            return answer
            
        except ImportError:
            logger.error("Google Generative AI library not installed. Install with: pip install google-generativeai")
            return "Error: Google Generative AI library not installed. Please install it or use a different provider."
        except Exception as e:
            logger.error(f"Gemini request failed: {e}")
            return f"Error calling Gemini: {str(e)[:200]}. Please check your API key and try again."
    
    def _call_claude(self, user_prompt: str) -> str:
        """Call Anthropic Claude API."""
        try:
            from anthropic import Anthropic
            
            client = Anthropic(api_key=self.api_key)
            
            # Combine system prompt and user prompt
            full_prompt = f"{SYSTEM_PROMPT}\n\n{user_prompt}"
            
            message = client.messages.create(
                model=self.model,
                max_tokens=1000,
                temperature=0.7,
                messages=[
                    {"role": "user", "content": full_prompt}
                ]
            )
            
            answer = message.content[0].text.strip()
            logger.info("Claude chat response generated successfully")
            return answer
            
        except ImportError:
            logger.error("Anthropic library not installed. Install with: pip install anthropic")
            return "Error: Anthropic library not installed. Please install it or use a different provider."
        except Exception as e:
            logger.error(f"Claude request failed: {e}")
            return f"Error calling Claude: {str(e)[:200]}. Please check your API key and try again."
    
    def _build_user_prompt(
        self,
        question: str,
        selected_issue: Optional[Dict[str, Any]],
        dashboard_state: Dict[str, Any]
    ) -> str:
        """Build user prompt with full context."""
        os_info = detect_os()
        
        prompt = f"""USER QUESTION: {question}

SYSTEM CONTEXT:
- OS: {os_info['name']} {os_info['version']}
- Architecture: {os_info['arch']}
- Platform: {os_info['platform']}
"""
        
        # Add health score
        health_score = dashboard_state.get("health_score", {})
        if health_score:
            prompt += f"""
SYSTEM HEALTH:
- Overall Score: {health_score.get('score', 0)}/100
- Status: {health_score.get('status', 'unknown')}
- Grade: {health_score.get('grade', 'N/A')}
"""
        
        # Add selected issue context
        if selected_issue:
            prompt += f"""
SELECTED ISSUE:
- Name: {selected_issue.get('name', 'Unknown')}
- Urgency: {selected_issue.get('urgency', 'low')}
- Confidence: {selected_issue.get('confidence', 0) * 100:.0f}%
- Explanation: {selected_issue.get('explanation', 'N/A')}
"""
            if selected_issue.get('recommendations'):
                prompt += f"- Recommendations: {', '.join(selected_issue.get('recommendations', []))}\n"
        
        # Add telemetry summary
        telemetry = dashboard_state.get("telemetry", {})
        if telemetry:
            cpu = telemetry.get("cpu", {})
            memory = telemetry.get("memory", {})
            disk = telemetry.get("disk", [])
            
            prompt += f"""
TELEMETRY DATA:
- CPU Usage: {cpu.get('average', 0):.1f}% average, {cpu.get('max', 0):.1f}% max
- Memory Usage: {memory.get('percent', 0):.1f}% ({memory.get('available_gb', 0):.1f} GB available)
- Disk Partitions: {len(disk)} checked
"""
            if disk:
                primary_disk = disk[0]
                prompt += f"- Primary Disk: {primary_disk.get('mountpoint', 'N/A')} - {primary_disk.get('percent', 0):.1f}% used\n"
        
        # Add top contributors if available
        top_contributors = dashboard_state.get("top_contributors", {})
        if top_contributors:
            top_mem = top_contributors.get("top_memory_processes", [])
            top_cpu = top_contributors.get("top_cpu_processes", [])
            
            if top_mem and selected_issue and "memory" in selected_issue.get("name", "").lower():
                prompt += "\nTOP MEMORY CONTRIBUTORS:\n"
                for proc in top_mem[:3]:
                    mem_display = f"{proc.get('memory_gb', 0)} GB" if proc.get('memory_gb') else f"{proc.get('memory_mb', 0)} MB"
                    prompt += f"- {proc.get('name')}: {proc.get('percent', 0)}% ({mem_display})\n"
            
            if top_cpu and selected_issue and "cpu" in selected_issue.get("name", "").lower():
                prompt += "\nTOP CPU CONTRIBUTORS:\n"
                for proc in top_cpu[:3]:
                    prompt += f"- {proc.get('name')}: {proc.get('cpu_percent', 0)}%\n"
        
        # Add all issues for context
        issues = dashboard_state.get("issues", [])
        if issues:
            prompt += f"\nALL DETECTED ISSUES ({len(issues)}):\n"
            for issue in issues[:5]:  # Limit to top 5
                prompt += f"- {issue.get('name', 'Unknown')} ({issue.get('urgency', 'low')} urgency)\n"
        
        prompt += "\nPlease provide a helpful, contextual answer to the user's question based on this diagnostic data."
        
        return prompt
    
    def _explain_rule_based(
        self,
        question: str,
        selected_issue: Optional[Dict[str, Any]],
        dashboard_state: Dict[str, Any]
    ) -> str:
        """Fallback rule-based explanation when AI is not available."""
        context = self._build_context(selected_issue, dashboard_state)
        question_lower = question.lower()
        
        if "what does this mean" in question_lower or "what is this" in question_lower:
            return self._explain_what_it_is(selected_issue, context)
        elif "is this a real problem" in question_lower or "is this serious" in question_lower:
            return self._explain_severity(selected_issue, context)
        elif "what should i do" in question_lower or "what can i do" in question_lower:
            return self._explain_actions(selected_issue, context)
        elif "is this normal" in question_lower:
            return self._explain_normalcy(selected_issue, context)
        else:
            return self._explain_general(question, selected_issue, context)
    
    def _build_context(
        self,
        selected_issue: Optional[Dict[str, Any]],
        dashboard_state: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Build context from dashboard state."""
        return {
            "health_score": dashboard_state.get("health_score", {}),
            "issues": dashboard_state.get("issues", []),
            "symptoms": dashboard_state.get("symptoms", []),
            "telemetry": dashboard_state.get("telemetry", {}),
            "top_contributors": dashboard_state.get("top_contributors", {}),
            "selected_issue": selected_issue
        }
    
    def _explain_what_it_is(
        self,
        issue: Optional[Dict[str, Any]],
        context: Dict[str, Any]
    ) -> str:
        """Explain what an issue is."""
        if not issue:
            return "Please select an issue from the dashboard to get an explanation."
        
        issue_name = issue.get("name", "Unknown Issue")
        explanation = issue.get("explanation", "")
        urgency = issue.get("urgency", "low")
        
        response = f"## What This Means\n\n"
        response += f"**{issue_name}** indicates that "
        
        if "memory" in issue_name.lower():
            response += "your system is experiencing memory pressure. This means available RAM is below recommended thresholds, which can lead to performance degradation, increased swapping, and potential application slowdowns."
        elif "cpu" in issue_name.lower():
            response += "your CPU is under sustained high load. This can cause system responsiveness issues, application lag, and reduced multitasking capability."
        elif "disk" in issue_name.lower():
            response += "your storage is running low on available space. This can prevent file saves, cause application failures, and impact system performance."
        elif "network" in issue_name.lower():
            response += "there are network connectivity issues detected. This may affect internet access, application connectivity, or network-dependent services."
        elif "security" in issue_name.lower():
            response += "certain security settings may not be configured optimally. This is observational only—no active threats were detected."
        else:
            response += explanation.lower() if explanation else "a system condition has been detected."
        
        response += f"\n\n**Urgency Level:** {urgency.upper()}\n\n"
        response += f"**Detailed Explanation:**\n{explanation}\n\n"
        
        # Add evidence
        if context.get("top_contributors"):
            top_mem = context["top_contributors"].get("top_memory_processes", [])
            top_cpu = context["top_contributors"].get("top_cpu_processes", [])
            
            if "memory" in issue_name.lower() and top_mem:
                response += "**Top Memory Contributors:**\n"
                for proc in top_mem[:3]:
                    mem_display = f"{proc.get('memory_gb', 0)} GB" if proc.get('memory_gb') else f"{proc.get('memory_mb', 0)} MB"
                    response += f"- {proc.get('name')}: {proc.get('percent', 0)}% ({mem_display})\n"
                response += "\n"
            
            if "cpu" in issue_name.lower() and top_cpu:
                response += "**Top CPU Contributors:**\n"
                for proc in top_cpu[:3]:
                    response += f"- {proc.get('name')}: {proc.get('cpu_percent', 0)}%\n"
                response += "\n"
        
        return response
    
    def _explain_severity(
        self,
        issue: Optional[Dict[str, Any]],
        context: Dict[str, Any]
    ) -> str:
        """Explain whether an issue is serious."""
        if not issue:
            return "Please select an issue to assess its severity."
        
        urgency = issue.get("urgency", "low")
        confidence = issue.get("confidence", 0.5)
        issue_name = issue.get("name", "")
        
        response = "## Is This a Real Problem?\n\n"
        
        if urgency == "high":
            response += "**Yes, this requires attention.** "
            response += f"{issue_name} is flagged as HIGH urgency, indicating it may impact system stability or performance significantly.\n\n"
        elif urgency == "medium":
            response += "**This is worth monitoring.** "
            response += f"{issue_name} is flagged as MEDIUM urgency. It may cause minor performance issues but is not immediately critical.\n\n"
        else:
            response += "**This is likely minor.** "
            response += f"{issue_name} is flagged as LOW urgency. It may not require immediate action, but monitoring is recommended.\n\n"
        
        # Add confidence assessment
        conf_level = "HIGH" if confidence >= 0.8 else "MEDIUM" if confidence >= 0.6 else "LOW"
        response += f"**Confidence Level:** {conf_level} ({int(confidence * 100)}%)\n\n"
        
        # Explain normalcy
        if "network" in issue_name.lower() and "interface" in issue_name.lower():
            response += "**Note:** Some network interfaces (like virtual adapters) may be down by design. This is often normal behavior.\n\n"
        
        if "security" in issue_name.lower() and "updates" in issue_name.lower():
            response += "**Note:** Pending updates are common and not necessarily urgent unless security-critical.\n\n"
        
        response += "**Impact:** "
        if urgency == "high":
            response += "May cause noticeable performance degradation or system instability."
        elif urgency == "medium":
            response += "May cause minor slowdowns or reduced efficiency."
        else:
            response += "Minimal immediate impact, but worth addressing to maintain optimal system health."
        
        return response
    
    def _explain_actions(
        self,
        issue: Optional[Dict[str, Any]],
        context: Dict[str, Any]
    ) -> str:
        """Explain what actions to take."""
        if not issue:
            return "Please select an issue to see recommended actions."
        
        issue_name = issue.get("name", "")
        recommendations = issue.get("recommendations", [])
        top_contributors = context.get("top_contributors", {})
        
        response = "## What Should I Do?\n\n"
        response += "**Recommended Actions (Manual Only):**\n\n"
        
        if recommendations:
            for i, rec in enumerate(recommendations, 1):
                response += f"{i}. {rec}\n"
        else:
            response += "No specific actions required at this time. Monitor the situation.\n"
        
        # Add contextual suggestions based on top contributors
        if top_contributors:
            top_mem = top_contributors.get("top_memory_processes", [])
            top_cpu = top_contributors.get("top_cpu_processes", [])
            
            if "memory" in issue_name.lower() and top_mem:
                response += "\n**Based on Top Contributors:**\n"
                for proc in top_mem[:2]:
                    proc_name = proc.get("name", "").lower()
                    if "chrome" in proc_name or "firefox" in proc_name or "safari" in proc_name:
                        response += f"- Consider closing unused tabs in {proc.get('name').split('(')[0].strip()}\n"
                    elif "slack" in proc_name or "discord" in proc_name:
                        response += f"- Restart {proc.get('name').split('(')[0].strip()} to free memory\n"
                    else:
                        response += f"- Review {proc.get('name').split('(')[0].strip()} usage\n"
        
        response += "\n**Important:** All actions are manual and safe. No system modifications will be made automatically.\n"
        
        return response
    
    def _explain_normalcy(
        self,
        issue: Optional[Dict[str, Any]],
        context: Dict[str, Any]
    ) -> str:
        """Explain whether behavior is normal."""
        if not issue:
            return "Please select an issue to assess if it's normal behavior."
        
        issue_name = issue.get("name", "")
        urgency = issue.get("urgency", "low")
        
        response = "## Is This Normal Behavior?\n\n"
        
        if "network" in issue_name.lower() and "interface" in issue_name.lower():
            response += "**Often Normal:** Virtual network interfaces (like `gif0`, `stf0`, `ap1`) are frequently down by design. "
            response += "These are system-managed interfaces that may not be active unless needed.\n\n"
            response += "**When to Worry:** If primary network interfaces (like `en0` on macOS or `eth0` on Linux) are down, that's unusual.\n\n"
            return response
        
        if "security" in issue_name.lower() and "updates" in issue_name.lower():
            response += "**Normal:** Having pending OS updates is common and expected. "
            response += "Systems regularly have updates available that haven't been installed yet.\n\n"
            response += "**When to Act:** Install updates when convenient, especially if they include security patches.\n\n"
            return response
        
        if urgency == "low":
            response += "**Likely Normal:** This finding is LOW urgency, suggesting it may be within expected system behavior.\n\n"
        elif urgency == "medium":
            response += "**Potentially Normal:** This finding is MEDIUM urgency. It may be normal for your workload, but worth monitoring.\n\n"
        else:
            response += "**Not Normal:** This HIGH urgency finding suggests atypical system behavior that warrants attention.\n\n"
        
        response += "**Context:** Consider your current workload. High memory usage during development work or running multiple applications is often expected.\n"
        
        return response
    
    def _explain_general(
        self,
        question: str,
        issue: Optional[Dict[str, Any]],
        context: Dict[str, Any]
    ) -> str:
        """Generate general explanation."""
        if not issue:
            return "I can help explain diagnostic findings. Please select an issue from the dashboard, or ask:\n\n- \"What does this mean?\"\n- \"Is this a real problem?\"\n- \"What should I do?\"\n- \"Is this normal behavior?\""
        
        issue_name = issue.get("name", "")
        explanation = issue.get("explanation", "")
        urgency = issue.get("urgency", "low")
        confidence = issue.get("confidence", 0.5)
        
        response = f"## About {issue_name}\n\n"
        response += f"{explanation}\n\n"
        response += f"**Urgency:** {urgency.upper()}\n"
        response += f"**Confidence:** {'HIGH' if confidence >= 0.8 else 'MEDIUM' if confidence >= 0.6 else 'LOW'} ({int(confidence * 100)}%)\n\n"
        
        if issue.get("recommendations"):
            response += "**Suggested Actions:**\n"
            for rec in issue.get("recommendations", [])[:3]:
                response += f"- {rec}\n"
        
        return response
