from sqlalchemy.orm import Session
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
import logging
from ..models.security_event import SecurityEvent, SecurityEventType, SecurityEventSeverity
from ...auth.models.user import User
from ...bookings.models.booking import Booking
from ...payments.models.payment import Payment
from ...shared.config.logging_config import get_logger

logger = get_logger(__name__)

class SecurityScanService:
    """Service for automated security scanning"""
    
    @staticmethod
    def run_full_scan(db: Session) -> Dict[str, Any]:
        """Run a full security scan"""
        results = {
            "scan_id": f"scan_{datetime.utcnow().isoformat()}",
            "started_at": datetime.utcnow().isoformat(),
            "checks": [],
            "total_issues": 0,
            "critical_issues": 0,
            "high_issues": 0,
            "medium_issues": 0,
            "low_issues": 0
        }
        
        # Run all security checks
        checks = [
            SecurityScanService._check_weak_passwords(db),
            SecurityScanService._check_inactive_users(db),
            SecurityScanService._check_failed_login_attempts(db),
            SecurityScanService._check_suspicious_activity(db),
            SecurityScanService._check_unresolved_security_events(db),
            SecurityScanService._check_expired_tokens(db),
            SecurityScanService._check_unusual_payment_patterns(db),
            SecurityScanService._check_data_retention_compliance(db),
        ]
        
        for check in checks:
            if check:
                results["checks"].append(check)
                results["total_issues"] += check.get("issue_count", 0)
                severity = check.get("severity", "low")
                if severity == "critical":
                    results["critical_issues"] += check.get("issue_count", 0)
                elif severity == "high":
                    results["high_issues"] += check.get("issue_count", 0)
                elif severity == "medium":
                    results["medium_issues"] += check.get("issue_count", 0)
                else:
                    results["low_issues"] += check.get("issue_count", 0)
        
        completed_at = datetime.utcnow()
        results["completed_at"] = completed_at.isoformat()
        # Parse start time - handle ISO format with/without microseconds
        start_str = results["started_at"]
        try:
            # Remove timezone info and microseconds for simpler parsing
            if '.' in start_str:
                start_str = start_str.split('.')[0]
            if 'Z' in start_str:
                start_str = start_str.replace('Z', '')
            if '+' in start_str:
                start_str = start_str.split('+')[0]
            started_at = datetime.fromisoformat(start_str)
        except Exception:
            # Fallback: use current time if parsing fails
            started_at = completed_at
        results["duration_seconds"] = (completed_at - started_at).total_seconds()
        
        # Log critical and high issues as security events
        for check in results["checks"]:
            if check.get("severity") in ["critical", "high"] and check.get("issue_count", 0) > 0:
                SecurityScanService._log_scan_finding(db, check)
        
        return results
    
    @staticmethod
    def _check_weak_passwords(db: Session) -> Optional[Dict[str, Any]]:
        """Check for users with weak passwords"""
        # This is a placeholder - in production, you'd check password strength
        # For now, we'll check for users without password changes in a long time
        cutoff_date = datetime.utcnow() - timedelta(days=365)
        users = db.query(User).filter(
            User.created_at < cutoff_date,
            User.is_active == True
        ).all()
        
        if len(users) > 10:  # Threshold
            return {
                "check_name": "Weak Passwords",
                "check_type": "password_security",
                "severity": "medium",
                "status": "failed",
                "issue_count": len(users),
                "description": f"{len(users)} users have not changed passwords in over a year",
                "recommendation": "Enforce password rotation policy",
                "affected_items": [{"user_id": u.id, "email": u.email} for u in users[:10]]
            }
        return None
    
    @staticmethod
    def _check_inactive_users(db: Session) -> Optional[Dict[str, Any]]:
        """Check for inactive users that should be deactivated"""
        cutoff_date = datetime.utcnow() - timedelta(days=180)
        # Users who haven't logged in for 6 months
        inactive_users = db.query(User).filter(
            User.is_active == True
        ).all()
        
        # This is simplified - in production, track last login
        if len(inactive_users) > 50:
            return {
                "check_name": "Inactive Users",
                "check_type": "user_management",
                "severity": "low",
                "status": "warning",
                "issue_count": len(inactive_users),
                "description": f"Found {len(inactive_users)} potentially inactive users",
                "recommendation": "Review and deactivate inactive accounts",
                "affected_items": []
            }
        return None
    
    @staticmethod
    def _check_failed_login_attempts(db: Session) -> Optional[Dict[str, Any]]:
        """Check for excessive failed login attempts"""
        from ..models.security_event import SecurityEvent, SecurityEventType
        
        recent_failures = db.query(SecurityEvent).filter(
            SecurityEvent.event_type == SecurityEventType.login_failure,
            SecurityEvent.created_at >= datetime.utcnow() - timedelta(hours=24)
        ).count()
        
        if recent_failures > 50:
            return {
                "check_name": "Excessive Failed Logins",
                "check_type": "authentication",
                "severity": "high",
                "status": "failed",
                "issue_count": recent_failures,
                "description": f"{recent_failures} failed login attempts in the last 24 hours",
                "recommendation": "Review failed login attempts and consider IP blocking",
                "affected_items": []
            }
        return None
    
    @staticmethod
    def _check_suspicious_activity(db: Session) -> Optional[Dict[str, Any]]:
        """Check for suspicious activity patterns"""
        from ..models.security_event import SecurityEvent, SecurityEventType, SecurityEventSeverity
        
        suspicious_events = db.query(SecurityEvent).filter(
            SecurityEvent.event_type == SecurityEventType.suspicious_activity,
            SecurityEvent.resolved == False,
            SecurityEvent.created_at >= datetime.utcnow() - timedelta(days=7)
        ).count()
        
        if suspicious_events > 0:
            return {
                "check_name": "Unresolved Suspicious Activity",
                "check_type": "threat_detection",
                "severity": "critical" if suspicious_events > 5 else "high",
                "status": "failed",
                "issue_count": suspicious_events,
                "description": f"{suspicious_events} unresolved suspicious activity events in the last 7 days",
                "recommendation": "Review and resolve suspicious activity events immediately",
                "affected_items": []
            }
        return None
    
    @staticmethod
    def _check_unresolved_security_events(db: Session) -> Optional[Dict[str, Any]]:
        """Check for unresolved critical security events"""
        from ..models.security_event import SecurityEvent, SecurityEventSeverity
        
        unresolved_critical = db.query(SecurityEvent).filter(
            SecurityEvent.severity == SecurityEventSeverity.critical,
            SecurityEvent.resolved == False,
            SecurityEvent.created_at >= datetime.utcnow() - timedelta(days=7)
        ).count()
        
        if unresolved_critical > 0:
            return {
                "check_name": "Unresolved Critical Events",
                "check_type": "incident_management",
                "severity": "critical",
                "status": "failed",
                "issue_count": unresolved_critical,
                "description": f"{unresolved_critical} unresolved critical security events",
                "recommendation": "Resolve critical security events immediately",
                "affected_items": []
            }
        return None
    
    @staticmethod
    def _check_expired_tokens(db: Session) -> Optional[Dict[str, Any]]:
        """Check for expired tokens that should be cleaned up"""
        from ...auth.models.refresh_token import RefreshToken
        
        expired_tokens = db.query(RefreshToken).filter(
            RefreshToken.expires_at < datetime.utcnow()
        ).count()
        
        if expired_tokens > 1000:
            return {
                "check_name": "Expired Tokens",
                "check_type": "token_management",
                "severity": "low",
                "status": "warning",
                "issue_count": expired_tokens,
                "description": f"{expired_tokens} expired tokens found in database",
                "recommendation": "Clean up expired tokens to improve database performance",
                "affected_items": []
            }
        return None
    
    @staticmethod
    def _check_unusual_payment_patterns(db: Session) -> Optional[Dict[str, Any]]:
        """Check for unusual payment patterns that might indicate fraud"""
        from ...payments.models.payment import PaymentStatus
        
        # Check for multiple failed payments from same IP
        recent_payments = db.query(Payment).filter(
            Payment.payment_date >= datetime.utcnow() - timedelta(hours=24)
        ).all()
        
        # Simplified check - in production, use more sophisticated fraud detection
        failed_payments = [p for p in recent_payments if p.payment_status == PaymentStatus.failed]
        
        if len(failed_payments) > 20:
            return {
                "check_name": "Unusual Payment Patterns",
                "check_type": "fraud_detection",
                "severity": "medium",
                "status": "warning",
                "issue_count": len(failed_payments),
                "description": f"{len(failed_payments)} failed payments in the last 24 hours",
                "recommendation": "Review failed payment patterns for potential fraud",
                "affected_items": []
            }
        return None
    
    @staticmethod
    def _check_data_retention_compliance(db: Session) -> Optional[Dict[str, Any]]:
        """Check data retention policy compliance"""
        from ..models.gdpr_compliance import DataRetentionPolicy
        
        policies = db.query(DataRetentionPolicy).filter(
            DataRetentionPolicy.is_active == True,
            DataRetentionPolicy.auto_delete == True
        ).all()
        
        # Check if there's data that should have been deleted
        issues = []
        for policy in policies:
            # This is simplified - in production, check actual data age
            if policy.retention_days < 30:  # Very short retention
                issues.append({
                    "policy": policy.data_type,
                    "retention_days": policy.retention_days
                })
        
        if issues:
            return {
                "check_name": "Data Retention Compliance",
                "check_type": "gdpr_compliance",
                "severity": "high",
                "status": "warning",
                "issue_count": len(issues),
                "description": f"Found {len(issues)} data retention policies that may need review",
                "recommendation": "Review data retention policies for GDPR compliance",
                "affected_items": issues
            }
        return None
    
    @staticmethod
    def _log_scan_finding(db: Session, check: Dict[str, Any]):
        """Log scan findings as security events"""
        try:
            event = SecurityEvent(
                event_type=SecurityEventType.suspicious_activity,
                severity=SecurityEventSeverity(check["severity"]),
                description=f"Security Scan: {check['check_name']} - {check['description']}",
                details={
                    "check_type": check.get("check_type"),
                    "issue_count": check.get("issue_count"),
                    "recommendation": check.get("recommendation"),
                    "affected_items": check.get("affected_items", [])
                }
            )
            db.add(event)
            db.commit()
        except Exception as e:
            logger.error(f"Error logging scan finding: {str(e)}")
            db.rollback()
    
    @staticmethod
    def schedule_scan(db: Session, interval_hours: int = 24) -> Dict[str, Any]:
        """Schedule automatic security scans"""
        # In production, use a task scheduler like Celery or APScheduler
        # For now, this is a placeholder that returns scan configuration
        return {
            "scheduled": True,
            "interval_hours": interval_hours,
            "next_scan": (datetime.utcnow() + timedelta(hours=interval_hours)).isoformat(),
            "message": "Scan scheduled. In production, use a task scheduler to run scans automatically."
        }

security_scan_service = SecurityScanService()

