"""
AgentClient - Offline-capable agent communication with optional cloud telemetry

Free tier: Works completely offline with local storage
Premium tier: Adds cloud monitoring when API key provided
"""

import os
import json
from pathlib import Path
from datetime import datetime, date
from typing import Optional, Callable, List, Dict, Any

from qalron.layer1 import MarkBluHasher
from qalron.layer2.envelope import MessageEnvelope


# Free tier limits
FREE_TIER_LIMITS = {
    "messages_per_day": 1000,
    "agents_per_user": 5
}


class FreeTierLimitExceeded(Exception):
    """Raised when free tier limits are exceeded"""
    pass


class AgentClient:
    """
    Universal agent client with optional cloud telemetry.
    
    Works in two modes:
    - FREE: No API key, local-only, 1000 msgs/day
    - PREMIUM: With API key, cloud monitoring, 100k msgs/month
    
    Examples:
        # Free tier (no API key)
        >>> client = AgentClient(agent_id="researcher")
        >>> client.send_message("analyst", "Analyze this data")
        
        # Premium tier (with API key)
        >>> client = AgentClient(
        ...     agent_id="researcher",
        ...     api_key="sk-premium-xyz"
        ... )
        >>> client.send_message("analyst", "Analyze this data")
    """
    
    def __init__(
        self,
        agent_id: str,
        api_key: Optional[str] = None,
        hasher: Optional[MarkBluHasher] = None,
        llm: Optional[Callable] = None,
        telemetry: Optional[bool] = None,
        server_url: str = "https://api.QALRON.io"
    ):
        """
        Initialize agent client.
        
        Args:
            agent_id: Unique identifier for this agent
            api_key: Optional API key for premium features
            hasher: MarkBluHasher instance (creates default if not provided)
            llm: Optional LLM function for chat (user provides their own)
            telemetry: Enable telemetry (auto-enabled if api_key present)
            server_url: Server URL for premium features
        """
        self.agent_id = agent_id
        self.api_key = api_key or os.getenv("QALRON_API_KEY")
        self.hasher = hasher or MarkBluHasher()
        self.llm = llm
        self.server_url = server_url
        
        # Auto-detect mode based on API key
        if telemetry is None:
            self.telemetry_enabled = bool(self.api_key)
        else:
            self.telemetry_enabled = telemetry
        
        # Set up storage and limits
        self._storage_dir = Path.home() / ".QALRON"
        self._storage_dir.mkdir(exist_ok=True)
        
        # In-memory message storage
        self.messages: List[MessageEnvelope] = []
        
        # Initialize telemetry client if premium
        if self.telemetry_enabled:
            if not self.api_key:
                raise ValueError("Telemetry requires api_key. Get one at https://QALRON.io")
            
            # Import here to avoid circular dependency
            from qalron.client.telemetry import TelemetryClient
            self.telemetry = TelemetryClient(self.api_key, self.server_url)
            print("🌐 Premium mode: Cloud monitoring enabled")
        else:
            self.telemetry = None
            limit = FREE_TIER_LIMITS["messages_per_day"]
            print(f"🔒 Free mode: Local-only ({limit} msgs/day)")
    
    def _get_usage_file(self) -> Path:
        """Get path to usage tracking file"""
        return self._storage_dir / "usage.json"
    
    def _get_messages_file(self) -> Path:
        """Get path to messages storage file"""
        return self._storage_dir / f"messages_{self.agent_id}.jsonl"
    
    def _check_free_tier_limits(self):
        """
        Check and enforce free tier limits.
        
        Raises:
            FreeTierLimitExceeded: If daily limit exceeded
        """
        usage_file = self._get_usage_file()
        
        # Load existing usage
        if usage_file.exists():
            with open(usage_file) as f:
                usage = json.load(f)
        else:
            usage = {"date": str(date.today()), "count": 0}
        
        # Reset if new day
        today = str(date.today())
        if usage["date"] != today:
            usage = {"date": today, "count": 0}
        
        # Check limit
        limit = FREE_TIER_LIMITS["messages_per_day"]
        if usage["count"] >= limit:
            raise FreeTierLimitExceeded(
                f"Free tier limit exceeded ({limit} messages/day).\n"
                f"Upgrade at https://QALRON.io/pricing for unlimited usage.\n\n"
                f"Premium features:\n"
                f"  ✅ 100,000 messages/month\n"
                f"  ✅ Real-time dashboard\n"
                f"  ✅ Team collaboration\n"
                f"  ✅ Advanced analytics"
            )
        
        # Increment and save
        usage["count"] += 1
        with open(usage_file, "w") as f:
            json.dump(usage, f)
    
    def _save_message_locally(self, envelope: MessageEnvelope):
        """Save message to local storage (JSONL format)"""
        messages_file = self._get_messages_file()
        with open(messages_file, "a") as f:
            f.write(envelope.to_json() + "\n")
    
    def send_message(
        self,
        to_agent: str,
        content: str,
        metadata: Optional[Dict[str, Any]] = None
    ) -> MessageEnvelope:
        """
        Send a secure message to another agent.
        
        Message is cryptographically signed using MarkBluHasher for
        tamper detection.
        
        Args:
            to_agent: Receiving agent ID
            content: Message content
            metadata: Optional metadata (priority, tags, etc)
            
        Returns:
            Signed MessageEnvelope
            
        Raises:
            FreeTierLimitExceeded: If free tier daily limit exceeded
        """
        # Check limits (free tier only)
        if not self.api_key:
            self._check_free_tier_limits()
        
        # Create secure envelope
        envelope = MessageEnvelope(
            sender=self.agent_id,
            receiver=to_agent,
            content=content,
            metadata=metadata or {}
        )
        
        # Sign with quantum-resistant hash
        envelope.sign(self.hasher)
        
        # Store locally (always)
        self.messages.append(envelope)
        self._save_message_locally(envelope)
        
        # Optional: Send telemetry to cloud (premium only)
        if self.telemetry:
            try:
                self.telemetry.track_event({
                    "type": "message_sent",
                    "from": self.agent_id,
                    "to": to_agent,
                    "timestamp": envelope.timestamp,
                    "signature_preview": envelope.signature[:16] if envelope.signature else None
                })
            except Exception as e:
                # Telemetry failure doesn't break core functionality
                print(f"⚠️ Telemetry failed (non-critical): {e}")
        
        return envelope
    
    def verify_message(self, envelope: MessageEnvelope) -> bool:
        """
        Verify message integrity using MarkBluHasher.
        
        Args:
            envelope: MessageEnvelope to verify
            
        Returns:
            True if message is unmodified, False if tampered
        """
        return envelope.verify(self.hasher)
    
    def get_messages(
        self,
        sender: Optional[str] = None,
        receiver: Optional[str] = None
    ) -> List[MessageEnvelope]:
        """
        Get messages from local storage.
        
        Args:
            sender: Filter by sender (optional)
            receiver: Filter by receiver (optional)
            
        Returns:
            List of matching messages
        """
        messages = self.messages.copy()
        
        if sender:
            messages = [m for m in messages if m.sender == sender]
        if receiver:
            messages = [m for m in messages if m.receiver == receiver]
        
        return messages
    
    
    def _calculate_llm_cost(self, model: str, prompt: str, response: str) -> float:
        """
        Calculate LLM cost based on tokens and model pricing.
        
        Args:
            model: Model name (e.g., 'gpt-4', 'gpt-3.5-turbo')
            prompt: Input prompt text
            response: Output response text
            
        Returns:
            Cost in USD
        """
        # Rough token estimation (1 token ≈ 4 characters)
        prompt_tokens = len(prompt) / 4
        completion_tokens = len(response) / 4
        
        # Pricing as of 2026 (per 1000 tokens)
        pricing = {
            "gpt-4": {"prompt": 0.03, "completion": 0.06},
            "gpt-4-turbo": {"prompt": 0.01, "completion": 0.03},
            "gpt-3.5-turbo": {"prompt": 0.001, "completion": 0.002},
            "claude-3-opus": {"prompt": 0.015, "completion": 0.075},
            "claude-3-sonnet": {"prompt": 0.003, "completion": 0.015}
        }
        
        # Default to gpt-4 pricing if model unknown
        rates = pricing.get(model, pricing["gpt-4"])
        
        cost = (prompt_tokens / 1000 * rates["prompt"]) + \
               (completion_tokens / 1000 * rates["completion"])
        
        return round(cost, 4)
    
    def chat(self, message: str, model: str = "gpt-4") -> str:
        """
        Send message to user's LLM and get response.
        
        NOTE: User must provide their own LLM function.
        QALRON doesn't include LLM - you bring your own.
        
        Args:
            message: Message to send to LLM
            model: Model name for cost calculation (default: gpt-4)
            
        Returns:
            LLM response
            
        Raises:
            ValueError: If no LLM configured
        """
        if not self.llm:
            raise ValueError(
                "No LLM configured. Provide llm parameter:\n\n"
                "  from openai import OpenAI\n"
                "  llm = OpenAI().chat.completions.create\n"
                "  client = AgentClient(agent_id='...', llm=llm)"
            )
        
        # Call user's LLM
        response = self.llm(
            messages=[{"role": "user", "content": message}]
        )
        
        # Calculate cost
        cost = self._calculate_llm_cost(model, message, response)
        
        # Track with telemetry (premium feature)
        if self.telemetry:
            try:
                self.telemetry.track_event({
                    "type": "llm_call",
                    "agent": self.agent_id,
                    "model": model,
                    "cost": cost,
                    "prompt_length": len(message),
                    "response_length": len(response),
                    "timestamp": datetime.utcnow().isoformat()
                })
            except Exception as e:
                print(f"⚠️ Telemetry failed (non-critical): {e}")
        
        return response
    
    def get_usage_stats(self) -> Dict[str, Any]:
        """
        Get usage statistics.
        
        Returns:
            Dictionary with usage stats
        """
        usage_file = self._get_usage_file()
        
        if usage_file.exists():
            with open(usage_file) as f:
                usage = json.load(f)
        else:
            usage = {"date": str(date.today()), "count": 0}
        
        limit = FREE_TIER_LIMITS["messages_per_day"]
        
        return {
            "messages_today": usage["count"],
            "daily_limit": limit,
            "remaining": max(0, limit - usage["count"]),
            "mode": "premium" if self.api_key else "free"
        }
    
    def __repr__(self) -> str:
        """String representation for debugging"""
        mode = "premium" if self.api_key else "free"
        return f"AgentClient(id={self.agent_id}, mode={mode}, messages={len(self.messages)})"
