from fastapi import APIRouter, Depends, HTTPException, status, Request, Query
from sqlalchemy.orm import Session
from typing import Optional, List
from datetime import datetime, timedelta
from pydantic import BaseModel, EmailStr
import logging
from ...shared.config.logging_config import get_logger

logger = get_logger(__name__)

from ...shared.config.database import get_db
from ...security.middleware.auth import get_current_user, authorize_roles
from ...auth.models.user import User
from ..models.security_event import (
    SecurityEvent,
    SecurityEventType,
    SecurityEventSeverity,
    IPWhitelist,
    IPBlacklist
)
from ..services.security_monitoring_service import security_monitoring_service
from ..services.gdpr_service import gdpr_service
from ..services.encryption_service import encryption_service
from ..services.security_scan_service import security_scan_service

# OAuth service is optional - only import if httpx is available
try:
    from ...auth.services.oauth_service import oauth_service
    OAUTH_AVAILABLE = True
except ImportError:
    OAUTH_AVAILABLE = False
    oauth_service = None

router = APIRouter(prefix="/security", tags=["Security"])

# Security Events
class SecurityEventResponse(BaseModel):
    id: int
    user_id: Optional[int]
    event_type: str
    severity: str
    ip_address: Optional[str]
    description: Optional[str]
    created_at: datetime
    
    class Config:
        from_attributes = True

@router.get("/events", response_model=List[SecurityEventResponse])
async def get_security_events(
    user_id: Optional[int] = Query(None),
    event_type: Optional[str] = Query(None),
    severity: Optional[str] = Query(None),
    ip_address: Optional[str] = Query(None),
    resolved: Optional[bool] = Query(None),
    days: int = Query(7, ge=1, le=90),
    limit: int = Query(100, ge=1, le=1000),
    offset: int = Query(0, ge=0),
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Get security events"""
    
    event_type_enum = None
    if event_type:
        try:
            event_type_enum = SecurityEventType(event_type)
        except ValueError:
            raise HTTPException(status_code=400, detail="Invalid event type")
    
    severity_enum = None
    if severity:
        try:
            severity_enum = SecurityEventSeverity(severity)
        except ValueError:
            raise HTTPException(status_code=400, detail="Invalid severity")
    
    start_date = datetime.utcnow() - timedelta(days=days)
    
    events = security_monitoring_service.get_security_events(
        db=db,
        user_id=user_id,
        event_type=event_type_enum,
        severity=severity_enum,
        ip_address=ip_address,
        resolved=resolved,
        start_date=start_date,
        limit=limit,
        offset=offset
    )
    
    return events

@router.get("/events/stats")
async def get_security_stats(
    days: int = Query(7, ge=1, le=90),
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Get security statistics"""
    
    stats = security_monitoring_service.get_security_stats(db=db, days=days)
    return stats

@router.post("/events/{event_id}/resolve")
async def resolve_security_event(
    event_id: int,
    resolution_notes: Optional[str] = None,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Mark a security event as resolved"""
    
    try:
        event = security_monitoring_service.resolve_event(
            db=db,
            event_id=event_id,
            resolved_by=current_user.id,
            resolution_notes=resolution_notes
        )
        return {"status": "success", "message": "Event resolved", "event_id": event.id}
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))

# IP Whitelist/Blacklist
class IPWhitelistCreate(BaseModel):
    ip_address: str
    description: Optional[str] = None

class IPBlacklistCreate(BaseModel):
    ip_address: str
    reason: Optional[str] = None
    blocked_until: Optional[datetime] = None

@router.post("/ip/whitelist")
async def add_ip_to_whitelist(
    data: IPWhitelistCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Add IP address to whitelist"""
    
    # Check if already exists
    existing = db.query(IPWhitelist).filter(
        IPWhitelist.ip_address == data.ip_address
    ).first()
    
    if existing:
        existing.is_active = True
        existing.description = data.description
        db.commit()
        return {"status": "success", "message": "IP whitelist updated"}
    
    whitelist = IPWhitelist(
        ip_address=data.ip_address,
        description=data.description,
        created_by=current_user.id
    )
    db.add(whitelist)
    db.commit()
    
    return {"status": "success", "message": "IP added to whitelist"}

@router.delete("/ip/whitelist/{ip_address}")
async def remove_ip_from_whitelist(
    ip_address: str,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Remove IP address from whitelist"""
    
    whitelist = db.query(IPWhitelist).filter(
        IPWhitelist.ip_address == ip_address
    ).first()
    
    if not whitelist:
        raise HTTPException(status_code=404, detail="IP not found in whitelist")
    
    whitelist.is_active = False
    db.commit()
    
    return {"status": "success", "message": "IP removed from whitelist"}

@router.get("/ip/whitelist")
async def get_whitelisted_ips(
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Get all whitelisted IPs"""
    
    whitelist = db.query(IPWhitelist).filter(
        IPWhitelist.is_active == True
    ).all()
    
    return [{"id": w.id, "ip_address": w.ip_address, "description": w.description} for w in whitelist]

@router.post("/ip/blacklist")
async def add_ip_to_blacklist(
    data: IPBlacklistCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Add IP address to blacklist"""
    
    existing = db.query(IPBlacklist).filter(
        IPBlacklist.ip_address == data.ip_address
    ).first()
    
    if existing:
        existing.is_active = True
        existing.reason = data.reason
        existing.blocked_until = data.blocked_until
        db.commit()
        return {"status": "success", "message": "IP blacklist updated"}
    
    blacklist = IPBlacklist(
        ip_address=data.ip_address,
        reason=data.reason,
        blocked_until=data.blocked_until,
        created_by=current_user.id
    )
    db.add(blacklist)
    db.commit()
    
    return {"status": "success", "message": "IP added to blacklist"}

@router.delete("/ip/blacklist/{ip_address}")
async def remove_ip_from_blacklist(
    ip_address: str,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Remove IP address from blacklist"""
    
    blacklist = db.query(IPBlacklist).filter(
        IPBlacklist.ip_address == ip_address
    ).first()
    
    if not blacklist:
        raise HTTPException(status_code=404, detail="IP not found in blacklist")
    
    blacklist.is_active = False
    db.commit()
    
    return {"status": "success", "message": "IP removed from blacklist"}

@router.get("/ip/blacklist")
async def get_blacklisted_ips(
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Get all blacklisted IPs"""
    
    blacklist = db.query(IPBlacklist).filter(
        IPBlacklist.is_active == True
    ).all()
    
    return [{"id": b.id, "ip_address": b.ip_address, "reason": b.reason, "blocked_until": b.blocked_until} for b in blacklist]

# OAuth Provider Management
class OAuthProviderCreate(BaseModel):
    name: str
    display_name: str
    client_id: str
    client_secret: str
    authorization_url: str
    token_url: str
    userinfo_url: str
    scopes: Optional[str] = None
    is_active: bool = True
    is_sso_enabled: bool = False

class OAuthProviderUpdate(BaseModel):
    display_name: Optional[str] = None
    client_id: Optional[str] = None
    client_secret: Optional[str] = None
    authorization_url: Optional[str] = None
    token_url: Optional[str] = None
    userinfo_url: Optional[str] = None
    scopes: Optional[str] = None
    is_active: Optional[bool] = None
    is_sso_enabled: Optional[bool] = None

@router.get("/oauth/providers")
async def get_oauth_providers(
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Get all OAuth providers"""
    from ..models.security_event import OAuthProvider
    providers = db.query(OAuthProvider).all()
    return [{
        "id": p.id,
        "name": p.name,
        "display_name": p.display_name,
        "is_active": p.is_active,
        "is_sso_enabled": p.is_sso_enabled,
        "created_at": p.created_at.isoformat() if p.created_at else None
    } for p in providers]

@router.post("/oauth/providers")
async def create_oauth_provider(
    data: OAuthProviderCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Create a new OAuth provider"""
    from ..models.security_event import OAuthProvider
    from ..services.encryption_service import encryption_service
    
    # Encrypt client secret
    encrypted_secret = encryption_service.encrypt(data.client_secret)
    
    provider = OAuthProvider(
        name=data.name,
        display_name=data.display_name,
        client_id=data.client_id,
        client_secret=encrypted_secret,
        authorization_url=data.authorization_url,
        token_url=data.token_url,
        userinfo_url=data.userinfo_url,
        scopes=data.scopes,
        is_active=data.is_active,
        is_sso_enabled=data.is_sso_enabled
    )
    db.add(provider)
    db.commit()
    db.refresh(provider)
    
    return {
        "id": provider.id,
        "name": provider.name,
        "display_name": provider.display_name,
        "is_active": provider.is_active,
        "is_sso_enabled": provider.is_sso_enabled
    }

@router.put("/oauth/providers/{provider_id}")
async def update_oauth_provider(
    provider_id: int,
    data: OAuthProviderUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Update an OAuth provider"""
    from ..models.security_event import OAuthProvider
    from ..services.encryption_service import encryption_service
    
    provider = db.query(OAuthProvider).filter(OAuthProvider.id == provider_id).first()
    if not provider:
        raise HTTPException(status_code=404, detail="OAuth provider not found")
    
    if data.display_name is not None:
        provider.display_name = data.display_name
    if data.client_id is not None:
        provider.client_id = data.client_id
    if data.client_secret is not None:
        provider.client_secret = encryption_service.encrypt(data.client_secret)
    if data.authorization_url is not None:
        provider.authorization_url = data.authorization_url
    if data.token_url is not None:
        provider.token_url = data.token_url
    if data.userinfo_url is not None:
        provider.userinfo_url = data.userinfo_url
    if data.scopes is not None:
        provider.scopes = data.scopes
    if data.is_active is not None:
        provider.is_active = data.is_active
    if data.is_sso_enabled is not None:
        provider.is_sso_enabled = data.is_sso_enabled
    
    db.commit()
    db.refresh(provider)
    
    return {
        "id": provider.id,
        "name": provider.name,
        "display_name": provider.display_name,
        "is_active": provider.is_active,
        "is_sso_enabled": provider.is_sso_enabled
    }

@router.delete("/oauth/providers/{provider_id}")
async def delete_oauth_provider(
    provider_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Delete an OAuth provider"""
    from ..models.security_event import OAuthProvider
    
    provider = db.query(OAuthProvider).filter(OAuthProvider.id == provider_id).first()
    if not provider:
        raise HTTPException(status_code=404, detail="OAuth provider not found")
    
    db.delete(provider)
    db.commit()
    
    return {"status": "success", "message": "OAuth provider deleted"}

# GDPR Request Management
@router.get("/gdpr/requests")
async def get_gdpr_requests(
    status: Optional[str] = Query(None),
    request_type: Optional[str] = Query(None),
    limit: int = Query(50, ge=1, le=100),
    offset: int = Query(0, ge=0),
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Get all GDPR requests"""
    from ..models.gdpr_compliance import DataSubjectRequest, DataSubjectRequestStatus, DataSubjectRequestType
    
    query = db.query(DataSubjectRequest)
    
    if status:
        try:
            status_enum = DataSubjectRequestStatus(status)
            query = query.filter(DataSubjectRequest.status == status_enum)
        except ValueError:
            pass
    
    if request_type:
        try:
            type_enum = DataSubjectRequestType(request_type)
            query = query.filter(DataSubjectRequest.request_type == type_enum)
        except ValueError:
            pass
    
    requests = query.order_by(DataSubjectRequest.created_at.desc()).offset(offset).limit(limit).all()
    
    return [{
        "id": r.id,
        "user_id": r.user_id,
        "email": r.email,
        "request_type": r.request_type.value,
        "status": r.status.value,
        "description": r.description,
        "verified": r.verified,
        "verified_at": r.verified_at.isoformat() if r.verified_at else None,
        "assigned_to": r.assigned_to,
        "completed_at": r.completed_at.isoformat() if r.completed_at else None,
        "created_at": r.created_at.isoformat() if r.created_at else None
    } for r in requests]

@router.get("/gdpr/requests/{request_id}")
async def get_gdpr_request(
    request_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Get a specific GDPR request"""
    from ..models.gdpr_compliance import DataSubjectRequest
    
    request = db.query(DataSubjectRequest).filter(DataSubjectRequest.id == request_id).first()
    if not request:
        raise HTTPException(status_code=404, detail="GDPR request not found")
    
    return {
        "id": request.id,
        "user_id": request.user_id,
        "email": request.email,
        "request_type": request.request_type.value,
        "status": request.status.value,
        "description": request.description,
        "verified": request.verified,
        "verified_at": request.verified_at.isoformat() if request.verified_at else None,
        "assigned_to": request.assigned_to,
        "notes": request.notes,
        "response_data": request.response_data,
        "completed_at": request.completed_at.isoformat() if request.completed_at else None,
        "created_at": request.created_at.isoformat() if request.created_at else None
    }

@router.post("/gdpr/requests/{request_id}/assign")
async def assign_gdpr_request(
    request_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Assign a GDPR request to the current admin"""
    from ..models.gdpr_compliance import DataSubjectRequest
    
    request = db.query(DataSubjectRequest).filter(DataSubjectRequest.id == request_id).first()
    if not request:
        raise HTTPException(status_code=404, detail="GDPR request not found")
    
    request.assigned_to = current_user.id
    db.commit()
    
    return {"status": "success", "message": "Request assigned"}

@router.post("/gdpr/requests/{request_id}/complete")
async def complete_gdpr_request(
    request_id: int,
    notes: Optional[str] = None,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Mark a GDPR request as completed"""
    from ..models.gdpr_compliance import DataSubjectRequest, DataSubjectRequestStatus
    
    request = db.query(DataSubjectRequest).filter(DataSubjectRequest.id == request_id).first()
    if not request:
        raise HTTPException(status_code=404, detail="GDPR request not found")
    
    request.status = DataSubjectRequestStatus.completed
    request.completed_at = datetime.utcnow()
    request.completed_by = current_user.id
    if notes:
        request.notes = notes
    
    db.commit()
    
    return {"status": "success", "message": "Request completed"}

# OAuth Routes
@router.get("/oauth/{provider_name}/authorize")
async def oauth_authorize(
    provider_name: str,
    redirect_uri: str = Query(...),
    state: Optional[str] = None,
    db: Session = Depends(get_db)
):
    """Get OAuth authorization URL"""
    if not OAUTH_AVAILABLE:
        raise HTTPException(status_code=503, detail="OAuth service is not available. Please install httpx: pip install httpx")
    
    try:
        auth_url = oauth_service.get_authorization_url(
            db=db,
            provider_name=provider_name,
            redirect_uri=redirect_uri,
            state=state
        )
        return {"authorization_url": auth_url}
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

@router.post("/oauth/{provider_name}/callback")
async def oauth_callback(
    provider_name: str,
    code: str = Query(...),
    redirect_uri: str = Query(...),
    state: Optional[str] = None,
    db: Session = Depends(get_db)
):
    """Handle OAuth callback"""
    if not OAUTH_AVAILABLE:
        raise HTTPException(status_code=503, detail="OAuth service is not available. Please install httpx: pip install httpx")
    
    try:
        # Exchange code for token
        token_data = await oauth_service.exchange_code_for_token(
            db=db,
            provider_name=provider_name,
            code=code,
            redirect_uri=redirect_uri
        )
        
        # Get user info
        user_info = await oauth_service.get_user_info(
            db=db,
            provider_name=provider_name,
            access_token=token_data['access_token']
        )
        
        # Find or create user
        user = oauth_service.find_or_create_user_from_oauth(
            db=db,
            provider_name=provider_name,
            user_info=user_info
        )
        
        # Save OAuth token
        from ..models.security_event import OAuthProvider
        provider = db.query(OAuthProvider).filter(
            OAuthProvider.name == provider_name
        ).first()
        
        oauth_service.save_oauth_token(
            db=db,
            user_id=user.id,
            provider_id=provider.id,
            provider_user_id=user_info.get('sub') or user_info.get('id'),
            access_token=token_data['access_token'],
            refresh_token=token_data.get('refresh_token'),
            expires_in=token_data.get('expires_in'),
            scopes=token_data.get('scope')
        )
        
        # Generate JWT tokens for the user
        from ...auth.services.auth_service import auth_service
        tokens = auth_service.generate_tokens(user.id)
        
        return {
            "status": "success",
            "token": tokens["accessToken"],
            "refreshToken": tokens["refreshToken"],
            "user": {
                "id": user.id,
                "email": user.email,
                "full_name": user.full_name
            }
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

# GDPR Routes
class DataSubjectRequestCreate(BaseModel):
    email: EmailStr
    request_type: str
    description: Optional[str] = None

@router.post("/gdpr/request")
async def create_data_subject_request(
    data: DataSubjectRequestCreate,
    request: Request,
    db: Session = Depends(get_db)
):
    """Create a GDPR data subject request"""
    
    try:
        request_type = DataSubjectRequestType(data.request_type)
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid request type")
    
    try:
        gdpr_request = gdpr_service.create_data_subject_request(
            db=db,
            email=data.email,
            request_type=request_type,
            description=data.description,
            ip_address=request.client.host if request.client else None,
            user_agent=request.headers.get("User-Agent")
        )
        
        return {
            "status": "success",
            "message": "Request created. Please check your email for verification.",
            "verification_token": gdpr_request.verification_token
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

@router.post("/gdpr/verify/{verification_token}")
async def verify_data_subject_request(
    verification_token: str,
    db: Session = Depends(get_db)
):
    """Verify a data subject request"""
    
    verified = gdpr_service.verify_request(db=db, verification_token=verification_token)
    
    if not verified:
        raise HTTPException(status_code=404, detail="Invalid verification token")
    
    return {"status": "success", "message": "Request verified"}

@router.get("/gdpr/data/{user_id}")
async def get_user_data(
    user_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """Get user data (GDPR access request)"""
    # Users can only access their own data, unless admin
    if current_user.id != user_id:
        # Check if user is admin
        from ...auth.models.role import Role
        role = db.query(Role).filter(Role.id == current_user.role_id).first()
        if not role or role.name != "admin":
            raise HTTPException(status_code=403, detail="Access denied")
    
    try:
        data = gdpr_service.get_user_data(db=db, user_id=user_id)
        return {"status": "success", "data": data}
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))

@router.delete("/gdpr/data/{user_id}")
async def delete_user_data(
    user_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Delete user data (GDPR erasure request)"""
    
    success = gdpr_service.delete_user_data(db=db, user_id=user_id)
    
    if not success:
        raise HTTPException(status_code=404, detail="User not found")
    
    return {"status": "success", "message": "User data deleted"}

@router.get("/gdpr/export/{user_id}")
async def export_user_data(
    user_id: int,
    format: str = Query("json", regex="^(json|csv)$"),
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """Export user data (GDPR portability request) - supports JSON and CSV formats"""
    if current_user.id != user_id:
        # Check if user is admin
        from ...auth.models.role import Role
        role = db.query(Role).filter(Role.id == current_user.role_id).first()
        if not role or role.name != "admin":
            raise HTTPException(status_code=403, detail="Access denied")
    
    try:
        result = gdpr_service.export_user_data(db=db, user_id=user_id, format=format)
        
        if format.lower() == 'csv':
            from fastapi.responses import Response
            return Response(
                content=result['data'],
                media_type='text/csv',
                headers={
                    'Content-Disposition': f'attachment; filename="user_data_{user_id}_{datetime.utcnow().strftime("%Y%m%d")}.csv"'
                }
            )
        
        return {"status": "success", "data": result['data']}
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))

# Security Scanning
@router.post("/scan/run")
async def run_security_scan(
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Run a manual security scan"""
    try:
        results = security_scan_service.run_full_scan(db=db)
        return {"status": "success", "results": results}
    except Exception as e:
        import traceback
        error_details = traceback.format_exc()
        logger.error(f"Security scan failed: {str(e)}\n{error_details}")
        raise HTTPException(status_code=500, detail=f"Scan failed: {str(e)}")

@router.post("/scan/schedule")
async def schedule_security_scan(
    interval_hours: int = Query(24, ge=1, le=168),  # 1 hour to 1 week
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Schedule automatic security scans"""
    try:
        schedule = security_scan_service.schedule_scan(db=db, interval_hours=interval_hours)
        return {"status": "success", "schedule": schedule}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Failed to schedule scan: {str(e)}")

