"""System Health Check Management Command

This command performs comprehensive system health checks including:
- Database connectivity and performance
- Cache system status
- File system permissions and disk space
- External service connectivity
- Application metrics and performance
- Security status and vulnerabilities

Usage:
    python manage.py system_health [options]
    
Options:
    --verbose, -v: Verbose output with detailed information
    --json: Output results in JSON format
    --save: Save results to database
    --email: Send results via email
    --threshold: Set custom thresholds for warnings
    --services: Check specific services only
    --quick: Perform quick health check (basic checks only)
    --detailed: Perform detailed health check (all checks)
    
Examples:
    python manage.py system_health
    python manage.py system_health --verbose --save
    python manage.py system_health --json --services database,cache
    python manage.py system_health --quick
    python manage.py system_health --detailed --email
"""

import json
import time
import psutil
import logging
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional

from django.core.management.base import BaseCommand, CommandError
from django.core.cache import cache
from django.db import connection, connections
from django.conf import settings
from django.core.mail import send_mail
from django.utils import timezone
from django.template.loader import render_to_string

from apps.core.models import SystemHealth, ApplicationMetrics
from apps.core.logging import get_logger


class Command(BaseCommand):
    """System Health Check Command"""
    
    help = 'Perform comprehensive system health checks'
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.logger = get_logger('core.management.system_health')
        self.start_time = None
        self.results = {
            'timestamp': None,
            'duration': 0,
            'overall_status': 'unknown',
            'checks': {},
            'summary': {
                'total_checks': 0,
                'passed': 0,
                'warnings': 0,
                'failed': 0,
            },
            'recommendations': [],
            'metadata': {
                'version': '1.0.0',
                'command': 'system_health',
                'hostname': None,
                'environment': getattr(settings, 'ENVIRONMENT', 'unknown'),
            }
        }
    
    def add_arguments(self, parser):
        """Add command arguments"""
        parser.add_argument(
            '--verbose', '-v',
            action='store_true',
            help='Verbose output with detailed information'
        )
        parser.add_argument(
            '--json',
            action='store_true',
            help='Output results in JSON format'
        )
        parser.add_argument(
            '--save',
            action='store_true',
            help='Save results to database'
        )
        parser.add_argument(
            '--email',
            action='store_true',
            help='Send results via email'
        )
        parser.add_argument(
            '--threshold',
            type=str,
            help='Custom thresholds in JSON format'
        )
        parser.add_argument(
            '--services',
            type=str,
            help='Comma-separated list of services to check'
        )
        parser.add_argument(
            '--quick',
            action='store_true',
            help='Perform quick health check (basic checks only)'
        )
        parser.add_argument(
            '--detailed',
            action='store_true',
            help='Perform detailed health check (all checks)'
        )
        parser.add_argument(
            '--timeout',
            type=int,
            default=30,
            help='Timeout for individual checks in seconds'
        )
    
    def handle(self, *args, **options):
        """Main command handler"""
        try:
            self.start_time = time.time()
            self.results['timestamp'] = timezone.now().isoformat()
            self.results['metadata']['hostname'] = self.get_hostname()
            
            self.logger.info("Starting system health check")
            
            # Parse options
            verbose = options.get('verbose', False)
            json_output = options.get('json', False)
            save_results = options.get('save', False)
            email_results = options.get('email', False)
            quick_check = options.get('quick', False)
            detailed_check = options.get('detailed', False)
            timeout = options.get('timeout', 30)
            
            # Parse custom thresholds
            thresholds = self.parse_thresholds(options.get('threshold'))
            
            # Parse services filter
            services_filter = self.parse_services(options.get('services'))
            
            # Determine check level
            if quick_check:
                check_level = 'quick'
            elif detailed_check:
                check_level = 'detailed'
            else:
                check_level = 'standard'
            
            # Perform health checks
            self.perform_health_checks(
                check_level=check_level,
                services_filter=services_filter,
                thresholds=thresholds,
                timeout=timeout,
                verbose=verbose
            )
            
            # Calculate duration
            self.results['duration'] = time.time() - self.start_time
            
            # Determine overall status
            self.calculate_overall_status()
            
            # Generate recommendations
            self.generate_recommendations()
            
            # Output results
            self.output_results(json_output, verbose)
            
            # Save results if requested
            if save_results:
                self.save_results()
            
            # Send email if requested
            if email_results:
                self.send_email_results()
            
            # Set exit code based on results
            if self.results['overall_status'] == 'critical':
                raise CommandError("Critical system health issues detected")
            elif self.results['overall_status'] == 'warning':
                self.logger.warning("System health warnings detected")
            
            self.logger.info("System health check completed successfully")
            
        except Exception as e:
            self.logger.error(f"System health check failed: {str(e)}")
            raise CommandError(f"Health check failed: {str(e)}")
    
    def perform_health_checks(self, check_level='standard', services_filter=None, 
                            thresholds=None, timeout=30, verbose=False):
        """Perform system health checks"""
        checks_to_run = self.get_checks_for_level(check_level, services_filter)
        
        for check_name in checks_to_run:
            try:
                if verbose:
                    self.stdout.write(f"Running check: {check_name}")
                
                check_result = self.run_check(check_name, thresholds, timeout)
                self.results['checks'][check_name] = check_result
                self.results['summary']['total_checks'] += 1
                
                # Update summary counters
                status = check_result.get('status', 'unknown')
                if status == 'ok':
                    self.results['summary']['passed'] += 1
                elif status == 'warning':
                    self.results['summary']['warnings'] += 1
                elif status == 'critical':
                    self.results['summary']['failed'] += 1
                
                if verbose:
                    self.stdout.write(
                        f"  Status: {status} - {check_result.get('message', 'No message')}"
                    )
                
            except Exception as e:
                self.logger.error(f"Check {check_name} failed: {str(e)}")
                self.results['checks'][check_name] = {
                    'status': 'critical',
                    'message': f"Check failed: {str(e)}",
                    'timestamp': timezone.now().isoformat(),
                    'duration': 0,
                    'error': str(e)
                }
                self.results['summary']['total_checks'] += 1
                self.results['summary']['failed'] += 1
    
    def get_checks_for_level(self, level, services_filter=None):
        """Get list of checks to run based on level"""
        all_checks = {
            'database': ['database_connectivity', 'database_performance'],
            'cache': ['cache_connectivity', 'cache_performance'],
            'filesystem': ['disk_space', 'file_permissions'],
            'system': ['memory_usage', 'cpu_usage'],
            'network': ['network_connectivity'],
            'security': ['security_headers', 'ssl_certificates'],
            'application': ['application_metrics', 'error_rates'],
        }
        
        if level == 'quick':
            return ['database_connectivity', 'cache_connectivity', 'disk_space']
        elif level == 'detailed':
            checks = []
            for service_checks in all_checks.values():
                checks.extend(service_checks)
            return checks
        else:  # standard
            return [
                'database_connectivity', 'database_performance',
                'cache_connectivity', 'disk_space', 'memory_usage',
                'application_metrics'
            ]
    
    def run_check(self, check_name, thresholds=None, timeout=30):
        """Run individual health check"""
        start_time = time.time()
        
        try:
            if check_name == 'database_connectivity':
                result = self.check_database_connectivity()
            elif check_name == 'database_performance':
                result = self.check_database_performance(thresholds)
            elif check_name == 'cache_connectivity':
                result = self.check_cache_connectivity()
            elif check_name == 'cache_performance':
                result = self.check_cache_performance(thresholds)
            elif check_name == 'disk_space':
                result = self.check_disk_space(thresholds)
            elif check_name == 'file_permissions':
                result = self.check_file_permissions()
            elif check_name == 'memory_usage':
                result = self.check_memory_usage(thresholds)
            elif check_name == 'cpu_usage':
                result = self.check_cpu_usage(thresholds)
            elif check_name == 'network_connectivity':
                result = self.check_network_connectivity()
            elif check_name == 'security_headers':
                result = self.check_security_headers()
            elif check_name == 'ssl_certificates':
                result = self.check_ssl_certificates()
            elif check_name == 'application_metrics':
                result = self.check_application_metrics(thresholds)
            elif check_name == 'error_rates':
                result = self.check_error_rates(thresholds)
            else:
                result = {
                    'status': 'unknown',
                    'message': f"Unknown check: {check_name}"
                }
            
            # Add timing information
            result['duration'] = time.time() - start_time
            result['timestamp'] = timezone.now().isoformat()
            
            return result
            
        except Exception as e:
            return {
                'status': 'critical',
                'message': f"Check failed: {str(e)}",
                'duration': time.time() - start_time,
                'timestamp': timezone.now().isoformat(),
                'error': str(e)
            }
    
    def check_database_connectivity(self):
        """Check database connectivity"""
        try:
            with connection.cursor() as cursor:
                cursor.execute("SELECT 1")
                result = cursor.fetchone()
                
            if result and result[0] == 1:
                return {
                    'status': 'ok',
                    'message': 'Database connectivity is healthy'
                }
            else:
                return {
                    'status': 'critical',
                    'message': 'Database connectivity test failed'
                }
        except Exception as e:
            return {
                'status': 'critical',
                'message': f"Database connectivity failed: {str(e)}"
            }
    
    def check_cache_connectivity(self):
        """Check cache connectivity"""
        try:
            test_key = 'health_check_test'
            test_value = 'test_value'
            
            cache.set(test_key, test_value, 60)
            retrieved_value = cache.get(test_key)
            cache.delete(test_key)
            
            if retrieved_value == test_value:
                return {
                    'status': 'ok',
                    'message': 'Cache connectivity is healthy'
                }
            else:
                return {
                    'status': 'critical',
                    'message': 'Cache connectivity test failed'
                }
        except Exception as e:
            return {
                'status': 'critical',
                'message': f"Cache connectivity failed: {str(e)}"
            }
    
    def check_disk_space(self, thresholds=None):
        """Check disk space usage"""
        try:
            disk_usage = psutil.disk_usage('/')
            used_percent = (disk_usage.used / disk_usage.total) * 100
            
            warning_threshold = thresholds.get('disk_warning', 80) if thresholds else 80
            critical_threshold = thresholds.get('disk_critical', 90) if thresholds else 90
            
            if used_percent >= critical_threshold:
                status = 'critical'
                message = f"Disk usage critical: {used_percent:.1f}% used"
            elif used_percent >= warning_threshold:
                status = 'warning'
                message = f"Disk usage warning: {used_percent:.1f}% used"
            else:
                status = 'ok'
                message = f"Disk usage healthy: {used_percent:.1f}% used"
            
            return {
                'status': status,
                'message': message,
                'metrics': {
                    'used_percent': used_percent,
                    'total_gb': disk_usage.total / (1024**3),
                    'used_gb': disk_usage.used / (1024**3),
                    'free_gb': disk_usage.free / (1024**3)
                }
            }
        except Exception as e:
            return {
                'status': 'critical',
                'message': f"Disk space check failed: {str(e)}"
            }
    
    def check_memory_usage(self, thresholds=None):
        """Check memory usage"""
        try:
            memory = psutil.virtual_memory()
            used_percent = memory.percent
            
            warning_threshold = thresholds.get('memory_warning', 80) if thresholds else 80
            critical_threshold = thresholds.get('memory_critical', 90) if thresholds else 90
            
            if used_percent >= critical_threshold:
                status = 'critical'
                message = f"Memory usage critical: {used_percent:.1f}% used"
            elif used_percent >= warning_threshold:
                status = 'warning'
                message = f"Memory usage warning: {used_percent:.1f}% used"
            else:
                status = 'ok'
                message = f"Memory usage healthy: {used_percent:.1f}% used"
            
            return {
                'status': status,
                'message': message,
                'metrics': {
                    'used_percent': used_percent,
                    'total_gb': memory.total / (1024**3),
                    'used_gb': memory.used / (1024**3),
                    'available_gb': memory.available / (1024**3)
                }
            }
        except Exception as e:
            return {
                'status': 'critical',
                'message': f"Memory usage check failed: {str(e)}"
            }
    
    def check_application_metrics(self, thresholds=None):
        """Check application metrics"""
        try:
            # Get recent metrics
            recent_metrics = ApplicationMetrics.objects.filter(
                timestamp__gte=timezone.now() - timedelta(minutes=5)
            ).order_by('-timestamp').first()
            
            if not recent_metrics:
                return {
                    'status': 'warning',
                    'message': 'No recent application metrics available'
                }
            
            # Check response time
            response_time = recent_metrics.response_time
            response_warning = thresholds.get('response_warning', 1000) if thresholds else 1000
            response_critical = thresholds.get('response_critical', 2000) if thresholds else 2000
            
            if response_time >= response_critical:
                status = 'critical'
                message = f"Response time critical: {response_time}ms"
            elif response_time >= response_warning:
                status = 'warning'
                message = f"Response time warning: {response_time}ms"
            else:
                status = 'ok'
                message = f"Application metrics healthy: {response_time}ms response time"
            
            return {
                'status': status,
                'message': message,
                'metrics': {
                    'response_time': response_time,
                    'request_count': recent_metrics.request_count,
                    'error_count': recent_metrics.error_count,
                    'timestamp': recent_metrics.timestamp.isoformat()
                }
            }
        except Exception as e:
            return {
                'status': 'critical',
                'message': f"Application metrics check failed: {str(e)}"
            }
    
    def parse_thresholds(self, threshold_str):
        """Parse custom thresholds from JSON string"""
        if not threshold_str:
            return {}
        
        try:
            return json.loads(threshold_str)
        except json.JSONDecodeError:
            self.logger.warning(f"Invalid threshold JSON: {threshold_str}")
            return {}
    
    def parse_services(self, services_str):
        """Parse services filter from comma-separated string"""
        if not services_str:
            return None
        
        return [service.strip() for service in services_str.split(',')]
    
    def calculate_overall_status(self):
        """Calculate overall system status"""
        if self.results['summary']['failed'] > 0:
            self.results['overall_status'] = 'critical'
        elif self.results['summary']['warnings'] > 0:
            self.results['overall_status'] = 'warning'
        elif self.results['summary']['passed'] > 0:
            self.results['overall_status'] = 'healthy'
        else:
            self.results['overall_status'] = 'unknown'
    
    def generate_recommendations(self):
        """Generate recommendations based on check results"""
        recommendations = []
        
        for check_name, check_result in self.results['checks'].items():
            if check_result['status'] == 'critical':
                if 'disk' in check_name:
                    recommendations.append("Free up disk space or add more storage")
                elif 'memory' in check_name:
                    recommendations.append("Optimize memory usage or add more RAM")
                elif 'database' in check_name:
                    recommendations.append("Check database configuration and connectivity")
                elif 'cache' in check_name:
                    recommendations.append("Check cache service configuration")
            elif check_result['status'] == 'warning':
                if 'disk' in check_name:
                    recommendations.append("Monitor disk space usage closely")
                elif 'memory' in check_name:
                    recommendations.append("Monitor memory usage and consider optimization")
        
        self.results['recommendations'] = list(set(recommendations))
    
    def output_results(self, json_output=False, verbose=False):
        """Output health check results"""
        if json_output:
            self.stdout.write(json.dumps(self.results, indent=2))
        else:
            self.output_formatted_results(verbose)
    
    def output_formatted_results(self, verbose=False):
        """Output formatted health check results"""
        # Header
        self.stdout.write("\n" + "=" * 60)
        self.stdout.write("SYSTEM HEALTH CHECK RESULTS")
        self.stdout.write("=" * 60)
        
        # Summary
        self.stdout.write(f"\nOverall Status: {self.results['overall_status'].upper()}")
        self.stdout.write(f"Timestamp: {self.results['timestamp']}")
        self.stdout.write(f"Duration: {self.results['duration']:.2f} seconds")
        
        # Statistics
        summary = self.results['summary']
        self.stdout.write(f"\nChecks Summary:")
        self.stdout.write(f"  Total: {summary['total_checks']}")
        self.stdout.write(f"  Passed: {summary['passed']}")
        self.stdout.write(f"  Warnings: {summary['warnings']}")
        self.stdout.write(f"  Failed: {summary['failed']}")
        
        # Individual check results
        if verbose or summary['warnings'] > 0 or summary['failed'] > 0:
            self.stdout.write("\nDetailed Results:")
            for check_name, result in self.results['checks'].items():
                status_symbol = {
                    'ok': '✓',
                    'warning': '⚠',
                    'critical': '✗',
                    'unknown': '?'
                }.get(result['status'], '?')
                
                self.stdout.write(
                    f"  {status_symbol} {check_name}: {result['message']}"
                )
                
                if verbose and 'metrics' in result:
                    for key, value in result['metrics'].items():
                        self.stdout.write(f"    {key}: {value}")
        
        # Recommendations
        if self.results['recommendations']:
            self.stdout.write("\nRecommendations:")
            for i, recommendation in enumerate(self.results['recommendations'], 1):
                self.stdout.write(f"  {i}. {recommendation}")
        
        self.stdout.write("\n" + "=" * 60 + "\n")
    
    def save_results(self):
        """Save health check results to database"""
        try:
            SystemHealth.objects.create(
                status=self.results['overall_status'],
                details=self.results,
                timestamp=timezone.now()
            )
            self.logger.info("Health check results saved to database")
        except Exception as e:
            self.logger.error(f"Failed to save health check results: {str(e)}")
    
    def send_email_results(self):
        """Send health check results via email"""
        try:
            if not hasattr(settings, 'HEALTH_CHECK_EMAIL_RECIPIENTS'):
                self.logger.warning("No email recipients configured for health checks")
                return
            
            subject = f"System Health Check - {self.results['overall_status'].title()}"
            
            # Render email template
            context = {
                'results': self.results,
                'hostname': self.results['metadata']['hostname'],
                'environment': self.results['metadata']['environment'],
            }
            
            message = render_to_string('core/emails/health_check.txt', context)
            html_message = render_to_string('core/emails/health_check.html', context)
            
            send_mail(
                subject=subject,
                message=message,
                html_message=html_message,
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=settings.HEALTH_CHECK_EMAIL_RECIPIENTS,
                fail_silently=False
            )
            
            self.logger.info("Health check results sent via email")
            
        except Exception as e:
            self.logger.error(f"Failed to send health check email: {str(e)}")
    
    def get_hostname(self):
        """Get system hostname"""
        try:
            import socket
            return socket.gethostname()
        except Exception:
            return 'unknown'