"""
Core health check execution logic with HTTP request handling.
"""

import requests
import logging
import time
from datetime import datetime
from typing import Dict, Any, Optional, Callable
from urllib.parse import urljoin

from .retry import RetryHandler
from .config import ConfigValidator

logger = logging.getLogger(__name__)


class HealthCheckResult:
    """Represents the result of a health check."""
    
    def __init__(self, service_name: str, url: str, status: str, 
                 response_time: float, error_message: Optional[str] = None,
                 status_code: Optional[int] = None):
        """
        Initialize health check result.
        
        Args:
            service_name: Name of the service checked
            url: URL that was checked
            status: Status ('healthy', 'unhealthy', 'error')
            response_time: Response time in milliseconds
            error_message: Error message if check failed
            status_code: HTTP status code received
        """
        self.service_name = service_name
        self.url = url
        self.status = status
        self.response_time = response_time
        self.error_message = error_message
        self.status_code = status_code
        self.timestamp = datetime.now()
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert result to dictionary format."""
        return {
            'service_name': self.service_name,
            'url': self.url,
            'status': self.status,
            'response_time_ms': round(self.response_time * 1000, 2),
            'error_message': self.error_message,
            'status_code': self.status_code,
            'timestamp': self.timestamp.isoformat()
        }
    
    def is_healthy(self) -> bool:
        """Check if service is healthy."""
        return self.status == 'healthy'


class HealthCheckExecutor:
    """Executes health checks on configured services."""
    
    def __init__(self, callback: Optional[Callable[[HealthCheckResult], None]] = None):
        """
        Initialize health check executor.
        
        Args:
            callback: Optional callback function for health check results
        """
        self.callback = callback
        self.session = requests.Session()
        # Set default headers for health checks
        self.session.headers.update({
            'User-Agent': 'HealthChecker/1.0',
            'Accept': 'application/json, text/plain, */*',
            'Cache-Control': 'no-cache'
        })
    
    def check_service(self, service_config: Dict[str, Any]) -> HealthCheckResult:
        """
        Perform health check on a single service.
        
        Args:
            service_config: Service configuration dictionary
            
        Returns:
            HealthCheckResult object
        """
        service_name = service_config['name']
        base_url = service_config['url']
        health_endpoint = service_config['health_endpoint']
        timeout = service_config.get('timeout', 10)
        expected_status = service_config.get('expected_status_code', 200)
        max_retries = service_config.get('max_retries', 3)
        
        # Construct full health check URL
        health_url = urljoin(base_url.rstrip('/') + '/', health_endpoint.lstrip('/'))
        
        logger.info(f"Starting health check for {service_name} at {health_url}")
        
        # Initialize retry handler
        retry_handler = RetryHandler(max_retries=max_retries)
        
        try:
            # Perform health check with retry logic
            start_time = time.time()
            
            def make_request():
                response = self.session.get(
                    health_url,
                    timeout=timeout,
                    allow_redirects=True
                )
                response.raise_for_status()  # Raise exception for HTTP errors
                return response
            
            # Execute with retry
            response = retry_handler.execute_with_retry(make_request)
            
            # Calculate response time
            response_time = time.time() - start_time
            
            # Check if status code matches expected
            if response.status_code == expected_status:
                status = 'healthy'
                error_message = None
                logger.info(f"Health check passed for {service_name} in {response_time:.2f}s")
            else:
                status = 'unhealthy'
                error_message = f"Unexpected status code: {response.status_code} (expected {expected_status})"
                logger.warning(f"Health check failed for {service_name}: {error_message}")
            
            result = HealthCheckResult(
                service_name=service_name,
                url=health_url,
                status=status,
                response_time=response_time,
                error_message=error_message,
                status_code=response.status_code
            )
            
        except requests.exceptions.Timeout:
            response_time = timeout  # Timeout occurred
            error_message = f"Request timeout after {timeout}s"
            status = 'error'
            logger.error(f"Health check failed for {service_name}: {error_message}")
            
            result = HealthCheckResult(
                service_name=service_name,
                url=health_url,
                status=status,
                response_time=response_time,
                error_message=error_message
            )
            
        except requests.exceptions.ConnectionError as e:
            response_time = time.time() - start_time
            error_message = f"Connection error: {str(e)}"
            status = 'error'
            logger.error(f"Health check failed for {service_name}: {error_message}")
            
            result = HealthCheckResult(
                service_name=service_name,
                url=health_url,
                status=status,
                response_time=response_time,
                error_message=error_message
            )
            
        except requests.exceptions.RequestException as e:
            response_time = time.time() - start_time
            error_message = f"Request error: {str(e)}"
            status = 'error'
            logger.error(f"Health check failed for {service_name}: {error_message}")
            
            result = HealthCheckResult(
                service_name=service_name,
                url=health_url,
                status=status,
                response_time=response_time,
                error_message=error_message
            )
            
        except Exception as e:
            response_time = time.time() - start_time
            error_message = f"Unexpected error: {str(e)}"
            status = 'error'
            logger.error(f"Health check failed for {service_name}: {error_message}")
            
            result = HealthCheckResult(
                service_name=service_name,
                url=health_url,
                status=status,
                response_time=response_time,
                error_message=error_message
            )
        
        # Execute callback if provided
        if self.callback:
            try:
                self.callback(result)
            except Exception as e:
                logger.error(f"Error executing callback for {service_name}: {e}")
        
        return result
    
    def check_all_services(self, services: list) -> Dict[str, HealthCheckResult]:
        """
        Perform health checks on all configured services.
        
        Args:
            services: List of service configurations
            
        Returns:
            Dictionary mapping service names to HealthCheckResult objects
        """
        results = {}
        
        for service_config in services:
            result = self.check_service(service_config)
            results[result.service_name] = result
        
        return results
    
    def close(self):
        """Close the HTTP session."""
        if self.session:
            self.session.close()
            logger.info("HTTP session closed")
    
    def __del__(self):
        """Cleanup on object deletion."""
        self.close()