"""
REST API Client - Configurable HTTP client with retry logic and failure handling
"""

import time
import logging
import json
import threading
from datetime import datetime, timedelta
from typing import Dict, Any, Optional, List, Union
from urllib.parse import urljoin, urlparse
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

from ..core.thread_manager import ThreadedComponent
from ..core.message_bus import MessageBus, Message, MessageType, MessageBuilder
from ..config.settings import ApiConfig
from ..config.manager import ConfigManager
from ..database.manager import DatabaseManager

logger = logging.getLogger(__name__)


class APIEndpoint:
    """Configuration for a single API endpoint"""
    
    def __init__(self, name: str, config: Dict[str, Any]):
        self.name = name
        self.url = config.get('url', '')
        self.method = config.get('method', 'GET').upper()
        self.headers = config.get('headers', {})
        self.params = config.get('params', {})
        self.data = config.get('data', {})
        self.interval = config.get('interval', 300)  # 5 minutes default
        self.enabled = config.get('enabled', True)
        self.timeout = config.get('timeout', 30)
        self.retry_attempts = config.get('retry_attempts', 3)
        self.retry_delay = config.get('retry_delay', 5)
        self.auth = config.get('auth', None)
        self.response_handler = config.get('response_handler', 'default')
        
        # Runtime state
        self.last_request = None
        self.last_success = None
        self.last_error = None
        self.consecutive_failures = 0
        self.total_requests = 0
        self.successful_requests = 0
    
    def should_execute(self) -> bool:
        """Check if endpoint should be executed based on interval"""
        if not self.enabled:
            return False
        
        if self.last_request is None:
            return True
        
        next_execution = self.last_request + timedelta(seconds=self.interval)
        return datetime.utcnow() >= next_execution
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization"""
        return {
            'name': self.name,
            'url': self.url,
            'method': self.method,
            'headers': self.headers,
            'params': self.params,
            'data': self.data,
            'interval': self.interval,
            'enabled': self.enabled,
            'timeout': self.timeout,
            'retry_attempts': self.retry_attempts,
            'retry_delay': self.retry_delay,
            'auth': self.auth,
            'response_handler': self.response_handler,
            'last_request': self.last_request.isoformat() if self.last_request else None,
            'last_success': self.last_success.isoformat() if self.last_success else None,
            'last_error': self.last_error,
            'consecutive_failures': self.consecutive_failures,
            'total_requests': self.total_requests,
            'successful_requests': self.successful_requests
        }


class ResponseHandler:
    """Base class for response handlers"""
    
    def handle_response(self, endpoint: APIEndpoint, response: requests.Response) -> Optional[Dict[str, Any]]:
        """Handle API response and return processed data"""
        raise NotImplementedError
    
    def handle_error(self, endpoint: APIEndpoint, error: Exception) -> Optional[Dict[str, Any]]:
        """Handle API error and return error data"""
        return {
            'error': str(error),
            'endpoint': endpoint.name,
            'timestamp': datetime.utcnow().isoformat()
        }


class DefaultResponseHandler(ResponseHandler):
    """Default response handler - processes JSON responses"""
    
    def handle_response(self, endpoint: APIEndpoint, response: requests.Response) -> Optional[Dict[str, Any]]:
        try:
            if response.headers.get('content-type', '').startswith('application/json'):
                return response.json()
            else:
                return {
                    'status_code': response.status_code,
                    'content': response.text,
                    'headers': dict(response.headers)
                }
        except json.JSONDecodeError:
            return {
                'status_code': response.status_code,
                'content': response.text,
                'headers': dict(response.headers)
            }


class NewsResponseHandler(ResponseHandler):
    """Response handler for news APIs - extracts headline and ticker data"""
    
    def handle_response(self, endpoint: APIEndpoint, response: requests.Response) -> Optional[Dict[str, Any]]:
        try:
            data = response.json()
            
            # Extract news data for overlay templates
            processed_data = {
                'source': endpoint.name,
                'timestamp': datetime.utcnow().isoformat(),
                'raw_data': data
            }
            
            # Try to extract common news fields
            if 'articles' in data:
                # NewsAPI format
                articles = data['articles'][:5]  # Top 5 articles
                processed_data['headlines'] = [article.get('title', '') for article in articles]
                processed_data['ticker_text'] = ' • '.join(processed_data['headlines'])
            elif 'items' in data:
                # RSS format
                items = data['items'][:5]
                processed_data['headlines'] = [item.get('title', '') for item in items]
                processed_data['ticker_text'] = ' • '.join(processed_data['headlines'])
            elif isinstance(data, list):
                # Direct array format
                processed_data['headlines'] = [item.get('title', '') for item in data[:5]]
                processed_data['ticker_text'] = ' • '.join(processed_data['headlines'])
            
            return processed_data
            
        except (json.JSONDecodeError, KeyError, TypeError) as e:
            logger.error(f"Failed to process news response: {e}")
            return self.handle_error(endpoint, e)


class SportsResponseHandler(ResponseHandler):
    """Response handler for sports APIs - extracts scores and game data"""
    
    def handle_response(self, endpoint: APIEndpoint, response: requests.Response) -> Optional[Dict[str, Any]]:
        try:
            data = response.json()
            
            processed_data = {
                'source': endpoint.name,
                'timestamp': datetime.utcnow().isoformat(),
                'raw_data': data
            }
            
            # Extract sports data
            if 'games' in data:
                # Generic sports API format
                games = data['games'][:3]  # Top 3 games
                game_data = []
                for game in games:
                    game_data.append({
                        'team1': game.get('home_team', 'Home'),
                        'team2': game.get('away_team', 'Away'),
                        'score1': game.get('home_score', 0),
                        'score2': game.get('away_score', 0),
                        'status': game.get('status', 'Live')
                    })
                processed_data['games'] = game_data
            
            return processed_data
            
        except (json.JSONDecodeError, KeyError, TypeError) as e:
            logger.error(f"Failed to process sports response: {e}")
            return self.handle_error(endpoint, e)


class APIClient(ThreadedComponent):
    """REST API Client component"""
    
    def __init__(self, message_bus: MessageBus, db_manager: DatabaseManager,
                 config_manager: ConfigManager, settings: ApiConfig):
        super().__init__("api_client", message_bus)
        self.db_manager = db_manager
        self.config_manager = config_manager
        self.settings = settings
        
        # HTTP session with retry logic
        self.session = requests.Session()
        self._setup_session()
        
        # API endpoints configuration
        self.endpoints: Dict[str, APIEndpoint] = {}
        self._load_endpoints()
        
        # Response handlers
        self.response_handlers = {
            'default': DefaultResponseHandler(),
            'news': NewsResponseHandler(),
            'sports': SportsResponseHandler()
        }
        
        # Statistics
        self.stats = {
            'total_requests': 0,
            'successful_requests': 0,
            'failed_requests': 0,
            'start_time': datetime.utcnow()
        }
        
        # Register message queue
        self.message_queue = self.message_bus.register_component(self.name)
        
        logger.info("APIClient initialized")
    
    def initialize(self) -> bool:
        """Initialize API client"""
        try:
            # Subscribe to messages
            self.message_bus.subscribe(self.name, MessageType.CONFIG_UPDATE, self._handle_config_update)
            self.message_bus.subscribe(self.name, MessageType.API_REQUEST, self._handle_api_request)
            
            logger.info("APIClient initialized successfully")
            return True
            
        except Exception as e:
            logger.error(f"APIClient initialization failed: {e}")
            return False
    
    def _setup_session(self):
        """Setup HTTP session with retry logic"""
        retry_strategy = Retry(
            total=self.settings.retry_attempts,
            backoff_factor=self.settings.retry_delay_seconds,
            status_forcelist=[429, 500, 502, 503, 504],
        )
        
        adapter = HTTPAdapter(max_retries=retry_strategy)
        self.session.mount("http://", adapter)
        self.session.mount("https://", adapter)
        
        # Set default headers
        self.session.headers.update({
            'User-Agent': f'MbetterClient/{self.settings.user_agent}',
            'Accept': 'application/json, text/plain, */*',
            'Accept-Encoding': 'gzip, deflate'
        })
        
        # Set timeout
        self.session.timeout = self.settings.timeout_seconds
    
    def _load_endpoints(self):
        """Load API endpoints from configuration"""
        try:
            endpoints_config = self.config_manager.get_section_config("api_endpoints")
            
            if not endpoints_config:
                # Create default configuration
                default_endpoints = self._get_default_endpoints()
                self.config_manager.update_section("api_endpoints", default_endpoints)
                endpoints_config = default_endpoints
            
            # Load endpoints
            for name, config in endpoints_config.items():
                self.endpoints[name] = APIEndpoint(name, config)
            
            logger.info(f"Loaded {len(self.endpoints)} API endpoints")
            
        except Exception as e:
            logger.error(f"Failed to load API endpoints: {e}")
    
    def _get_default_endpoints(self) -> Dict[str, Dict[str, Any]]:
        """Get default API endpoints configuration"""
        return {
            "news_example": {
                "url": "https://newsapi.org/v2/top-headlines",
                "method": "GET",
                "headers": {
                    "X-API-Key": "your-api-key-here"
                },
                "params": {
                    "country": "us",
                    "pageSize": 5
                },
                "interval": 300,  # 5 minutes
                "enabled": False,  # Disabled by default until API key is configured
                "timeout": 30,
                "retry_attempts": 3,
                "retry_delay": 5,
                "response_handler": "news"
            },
            "sports_example": {
                "url": "https://api.sportsdata.io/v3/nfl/scores/json/LiveBoxScores",
                "method": "GET",
                "headers": {
                    "Ocp-Apim-Subscription-Key": "your-api-key-here"
                },
                "interval": 60,  # 1 minute during games
                "enabled": False,
                "timeout": 30,
                "retry_attempts": 3,
                "retry_delay": 5,
                "response_handler": "sports"
            },
            "weather_example": {
                "url": "https://api.openweathermap.org/data/2.5/weather",
                "method": "GET",
                "params": {
                    "q": "New York",
                    "appid": "your-api-key-here",
                    "units": "metric"
                },
                "interval": 1800,  # 30 minutes
                "enabled": False,
                "timeout": 15,
                "retry_attempts": 2,
                "retry_delay": 3,
                "response_handler": "default"
            }
        }
    
    def run(self):
        """Main run loop"""
        try:
            logger.info("APIClient thread started")
            
            # Send ready status
            ready_message = MessageBuilder.system_status(
                sender=self.name,
                status="ready",
                details={
                    "endpoints": len(self.endpoints),
                    "enabled_endpoints": sum(1 for ep in self.endpoints.values() if ep.enabled)
                }
            )
            self.message_bus.publish(ready_message)
            
            # Main execution loop
            while self.running:
                try:
                    # Process messages
                    message = self.message_bus.get_message(self.name, timeout=1.0)
                    if message:
                        self._process_message(message)
                    
                    # Execute scheduled API requests
                    self._execute_scheduled_requests()
                    
                    # Update heartbeat
                    self.heartbeat()
                    
                    time.sleep(1.0)
                    
                except Exception as e:
                    logger.error(f"APIClient run loop error: {e}")
                    time.sleep(5.0)
                    
        except Exception as e:
            logger.error(f"APIClient run failed: {e}")
        finally:
            logger.info("APIClient thread ended")
    
    def _execute_scheduled_requests(self):
        """Execute API requests that are due"""
        for endpoint in self.endpoints.values():
            if endpoint.should_execute():
                self._execute_endpoint_request(endpoint)
    
    def _execute_endpoint_request(self, endpoint: APIEndpoint):
        """Execute a single API request"""
        try:
            endpoint.last_request = datetime.utcnow()
            endpoint.total_requests += 1
            self.stats['total_requests'] += 1
            
            logger.debug(f"Executing API request: {endpoint.name} -> {endpoint.url}")
            
            # Prepare request parameters
            request_kwargs = {
                'method': endpoint.method,
                'url': endpoint.url,
                'headers': endpoint.headers,
                'timeout': endpoint.timeout
            }
            
            if endpoint.method == 'GET':
                request_kwargs['params'] = endpoint.params
            else:
                request_kwargs['json'] = endpoint.data
            
            # Add authentication if configured
            if endpoint.auth:
                if endpoint.auth.get('type') == 'bearer':
                    request_kwargs['headers']['Authorization'] = f"Bearer {endpoint.auth.get('token')}"
                elif endpoint.auth.get('type') == 'basic':
                    request_kwargs['auth'] = (endpoint.auth.get('username'), endpoint.auth.get('password'))
            
            # Execute request
            response = self.session.request(**request_kwargs)
            response.raise_for_status()
            
            # Handle successful response
            handler = self.response_handlers.get(endpoint.response_handler, self.response_handlers['default'])
            processed_data = handler.handle_response(endpoint, response)
            
            # Update endpoint status
            endpoint.last_success = datetime.utcnow()
            endpoint.last_error = None
            endpoint.consecutive_failures = 0
            endpoint.successful_requests += 1
            self.stats['successful_requests'] += 1
            
            # Send response data via message bus
            if processed_data:
                response_message = Message(
                    type=MessageType.API_RESPONSE,
                    sender=self.name,
                    data={
                        'endpoint': endpoint.name,
                        'success': True,
                        'data': processed_data,
                        'timestamp': datetime.utcnow().isoformat()
                    }
                )
                self.message_bus.publish(response_message)
            
            logger.debug(f"API request successful: {endpoint.name}")
            
        except Exception as e:
            # Handle request failure
            endpoint.last_error = str(e)
            endpoint.consecutive_failures += 1
            self.stats['failed_requests'] += 1
            
            logger.error(f"API request failed: {endpoint.name} - {e}")
            
            # Send error message
            error_message = Message(
                type=MessageType.API_RESPONSE,
                sender=self.name,
                data={
                    'endpoint': endpoint.name,
                    'success': False,
                    'error': str(e),
                    'consecutive_failures': endpoint.consecutive_failures,
                    'timestamp': datetime.utcnow().isoformat()
                }
            )
            self.message_bus.publish(error_message)
            
            # Disable endpoint if too many consecutive failures
            if endpoint.consecutive_failures >= self.settings.max_consecutive_failures:
                endpoint.enabled = False
                logger.warning(f"Endpoint disabled due to consecutive failures: {endpoint.name}")
    
    def _process_message(self, message: Message):
        """Process received message"""
        try:
            # Messages are handled by subscribed handlers
            pass
        except Exception as e:
            logger.error(f"Failed to process message: {e}")
    
    def _handle_config_update(self, message: Message):
        """Handle configuration update message"""
        try:
            config_section = message.data.get("config_section")
            
            if config_section == "api_endpoints":
                logger.info("API endpoints configuration updated")
                self._load_endpoints()
            
        except Exception as e:
            logger.error(f"Failed to handle config update: {e}")
    
    def _handle_api_request(self, message: Message):
        """Handle manual API request message"""
        try:
            endpoint_name = message.data.get("endpoint")
            
            if endpoint_name in self.endpoints:
                endpoint = self.endpoints[endpoint_name]
                self._execute_endpoint_request(endpoint)
            else:
                logger.warning(f"Unknown endpoint requested: {endpoint_name}")
            
        except Exception as e:
            logger.error(f"Failed to handle API request: {e}")
    
    def get_endpoint_status(self, endpoint_name: str) -> Optional[Dict[str, Any]]:
        """Get status of a specific endpoint"""
        endpoint = self.endpoints.get(endpoint_name)
        if endpoint:
            return endpoint.to_dict()
        return None
    
    def get_all_endpoints_status(self) -> Dict[str, Dict[str, Any]]:
        """Get status of all endpoints"""
        return {name: endpoint.to_dict() for name, endpoint in self.endpoints.items()}
    
    def get_client_stats(self) -> Dict[str, Any]:
        """Get client statistics"""
        uptime = datetime.utcnow() - self.stats['start_time']
        return {
            'total_requests': self.stats['total_requests'],
            'successful_requests': self.stats['successful_requests'],
            'failed_requests': self.stats['failed_requests'],
            'success_rate': (self.stats['successful_requests'] / max(1, self.stats['total_requests'])) * 100,
            'uptime_seconds': uptime.total_seconds(),
            'endpoints_count': len(self.endpoints),
            'enabled_endpoints': sum(1 for ep in self.endpoints.values() if ep.enabled)
        }
    
    def update_endpoint(self, endpoint_name: str, config: Dict[str, Any]) -> bool:
        """Update endpoint configuration"""
        try:
            if endpoint_name in self.endpoints:
                # Update existing endpoint
                endpoint = self.endpoints[endpoint_name]
                for key, value in config.items():
                    if hasattr(endpoint, key):
                        setattr(endpoint, key, value)
            else:
                # Create new endpoint
                self.endpoints[endpoint_name] = APIEndpoint(endpoint_name, config)
            
            # Save to configuration
            all_endpoints = {name: ep.to_dict() for name, ep in self.endpoints.items()}
            self.config_manager.update_section("api_endpoints", all_endpoints)
            
            logger.info(f"Updated endpoint configuration: {endpoint_name}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to update endpoint: {e}")
            return False
    
    def shutdown(self):
        """Shutdown API client"""
        try:
            logger.info("Shutting down APIClient...")
            
            if self.session:
                self.session.close()
            
        except Exception as e:
            logger.error(f"APIClient shutdown error: {e}")