"""
GDPR compliance routes for data export and deletion.
"""
from fastapi import APIRouter, Depends, HTTPException, Query, Response
from sqlalchemy.orm import Session, noload
from sqlalchemy import or_
from typing import Optional
from datetime import datetime
from ...shared.config.database import get_db
from ...shared.config.logging_config import get_logger
from ...security.middleware.auth import get_current_user, authorize_roles, get_current_user_optional
from ...auth.models.user import User
from ..services.gdpr_service import gdpr_service
from ..services.consent_service import consent_service
from ..services.data_processing_service import data_processing_service
from ..models.gdpr_request import GDPRRequest, GDPRRequestType, GDPRRequestStatus
from ..models.consent import ConsentType, ConsentStatus
from ...shared.utils.response_helpers import success_response
from fastapi import Request
from pydantic import BaseModel
from typing import Dict, Any, Optional, List

logger = get_logger(__name__)
router = APIRouter(prefix='/gdpr', tags=['gdpr'])

class AnonymousExportRequest(BaseModel):
    email: str

@router.post('/export')
async def request_data_export(
    request: Request,
    anonymous_request: Optional[AnonymousExportRequest] = None,
    current_user: Optional[User] = Depends(get_current_user_optional),
    db: Session = Depends(get_db)
):
    """Request export of user's personal data (GDPR) - supports both authenticated and anonymous users."""
    try:
        client_ip = request.client.host if request.client else None
        user_agent = request.headers.get('User-Agent')
        
        # Check if authenticated or anonymous
        if current_user:
            # Authenticated user
            gdpr_request = await gdpr_service.create_data_export_request(
                db=db,
                user_id=current_user.id,
                ip_address=client_ip,
                user_agent=user_agent,
                is_anonymous=False
            )
        elif anonymous_request and anonymous_request.email:
            # Anonymous user - requires email
            gdpr_request = await gdpr_service.create_data_export_request(
                db=db,
                user_email=anonymous_request.email,
                ip_address=client_ip,
                user_agent=user_agent,
                is_anonymous=True
            )
        else:
            raise HTTPException(
                status_code=400,
                detail='Either authentication required or email must be provided for anonymous requests'
            )
        
        return success_response(
            data={
                'request_id': gdpr_request.id,
                'verification_token': gdpr_request.verification_token,
                'status': gdpr_request.status.value,
                'expires_at': gdpr_request.expires_at.isoformat() if gdpr_request.expires_at else None,
                'is_anonymous': gdpr_request.is_anonymous
            },
            message='Data export request created. You will receive an email with download link once ready.'
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f'Error creating data export request: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/export/{request_id}')
async def get_export_data(
    request_id: int,
    verification_token: str = Query(...),
    current_user: Optional[User] = Depends(get_current_user_optional),
    db: Session = Depends(get_db)
):
    """Get exported user data - supports both authenticated and anonymous users via verification token."""
    try:
        # Build query - verification token is required for both authenticated and anonymous
        query = db.query(GDPRRequest).options(
            noload(GDPRRequest.user),
            noload(GDPRRequest.processor)
        ).filter(
            GDPRRequest.id == request_id,
            GDPRRequest.verification_token == verification_token,
            GDPRRequest.request_type == GDPRRequestType.data_export
        )
        
        # For authenticated users, also verify user_id matches
        if current_user:
            query = query.filter(GDPRRequest.user_id == current_user.id)
        
        gdpr_request = query.first()
        
        if not gdpr_request:
            raise HTTPException(status_code=404, detail='Export request not found or invalid token')
        
        if gdpr_request.status == GDPRRequestStatus.pending:
            # Process export
            export_data = await gdpr_service.export_user_data(
                db=db,
                user_id=gdpr_request.user_id,
                user_email=gdpr_request.user_email,
                request_id=request_id,
                is_anonymous=gdpr_request.is_anonymous
            )
            return success_response(data=export_data)
        elif gdpr_request.status == GDPRRequestStatus.completed and gdpr_request.export_file_path:
            # Return file if exists
            from pathlib import Path
            file_path = Path(gdpr_request.export_file_path)
            if file_path.exists():
                return Response(
                    content=file_path.read_bytes(),
                    media_type='application/json',
                    headers={
                        'Content-Disposition': f'attachment; filename="user_data_export_{request_id}.json"'
                    }
                )
        
        raise HTTPException(status_code=404, detail='Export file not found')
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error getting export data: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

class AnonymousDeletionRequest(BaseModel):
    email: str

@router.post('/delete')
async def request_data_deletion(
    request: Request,
    anonymous_request: Optional[AnonymousDeletionRequest] = None,
    current_user: Optional[User] = Depends(get_current_user_optional),
    db: Session = Depends(get_db)
):
    """Request deletion of user's personal data (GDPR - Right to be Forgotten) - supports anonymous users."""
    try:
        client_ip = request.client.host if request.client else None
        user_agent = request.headers.get('User-Agent')
        
        # Check if authenticated or anonymous
        if current_user:
            # Authenticated user
            gdpr_request = await gdpr_service.create_data_deletion_request(
                db=db,
                user_id=current_user.id,
                ip_address=client_ip,
                user_agent=user_agent,
                is_anonymous=False
            )
        elif anonymous_request and anonymous_request.email:
            # Anonymous user - requires email
            gdpr_request = await gdpr_service.create_data_deletion_request(
                db=db,
                user_email=anonymous_request.email,
                ip_address=client_ip,
                user_agent=user_agent,
                is_anonymous=True
            )
        else:
            raise HTTPException(
                status_code=400,
                detail='Either authentication required or email must be provided for anonymous requests'
            )
        
        return success_response(
            data={
                'request_id': gdpr_request.id,
                'verification_token': gdpr_request.verification_token,
                'status': gdpr_request.status.value,
                'is_anonymous': gdpr_request.is_anonymous
            },
            message='Data deletion request created. Please verify via email to proceed.'
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f'Error creating data deletion request: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/delete/{request_id}/confirm')
async def confirm_data_deletion(
    request_id: int,
    verification_token: str = Query(...),
    current_user: Optional[User] = Depends(get_current_user_optional),
    db: Session = Depends(get_db)
):
    """Confirm and process data deletion request - supports anonymous users via verification token."""
    try:
        # Build query - verification token is required for both authenticated and anonymous
        query = db.query(GDPRRequest).options(
            noload(GDPRRequest.user),
            noload(GDPRRequest.processor)
        ).filter(
            GDPRRequest.id == request_id,
            GDPRRequest.verification_token == verification_token,
            GDPRRequest.request_type == GDPRRequestType.data_deletion,
            GDPRRequest.status == GDPRRequestStatus.pending
        )
        
        # For authenticated users, also verify user_id matches
        if current_user:
            query = query.filter(GDPRRequest.user_id == current_user.id)
        
        gdpr_request = query.first()
        
        if not gdpr_request:
            raise HTTPException(status_code=404, detail='Deletion request not found or already processed')
        
        # Process deletion
        deletion_log = await gdpr_service.delete_user_data(
            db=db,
            user_id=gdpr_request.user_id,
            user_email=gdpr_request.user_email,
            request_id=request_id,
            processed_by=current_user.id if current_user else None,
            is_anonymous=gdpr_request.is_anonymous
        )
        
        return success_response(
            data=deletion_log,
            message=deletion_log.get('summary', {}).get('message', 'Your data has been deleted successfully.')
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error processing data deletion: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/requests')
async def get_user_gdpr_requests(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Get user's GDPR requests (both authenticated and anonymous requests by email)."""
    try:
        # Get requests by user_id (authenticated) or by email (includes anonymous)
        requests = db.query(GDPRRequest).options(
            noload(GDPRRequest.user),
            noload(GDPRRequest.processor)
        ).filter(
            or_(
                GDPRRequest.user_id == current_user.id,
                GDPRRequest.user_email == current_user.email
            )
        ).order_by(GDPRRequest.created_at.desc()).all()
        
        return success_response(data={
            'requests': [{
                'id': req.id,
                'request_type': req.request_type.value,
                'status': req.status.value,
                'is_anonymous': req.is_anonymous,
                'created_at': req.created_at.isoformat() if req.created_at else None,
                'processed_at': req.processed_at.isoformat() if req.processed_at else None,
            } for req in requests]
        })
    except Exception as e:
        logger.error(f'Error getting GDPR requests: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/admin/requests')
async def get_all_gdpr_requests(
    status: Optional[str] = Query(None),
    page: int = Query(1, ge=1),
    limit: int = Query(20, ge=1, le=100),
    current_user: User = Depends(authorize_roles('admin')),
    db: Session = Depends(get_db)
):
    """Get all GDPR requests (admin only)."""
    try:
        query = db.query(GDPRRequest).options(
            noload(GDPRRequest.user),
            noload(GDPRRequest.processor)
        )
        
        if status:
            try:
                status_enum = GDPRRequestStatus(status)
                query = query.filter(GDPRRequest.status == status_enum)
            except ValueError:
                raise HTTPException(status_code=400, detail=f'Invalid status: {status}')
        
        total = query.count()
        offset = (page - 1) * limit
        requests = query.order_by(GDPRRequest.created_at.desc()).offset(offset).limit(limit).all()
        
        return success_response(data={
            'requests': [{
                'id': req.id,
                'request_type': req.request_type.value,
                'status': req.status.value,
                'user_email': req.user_email,
                'created_at': req.created_at.isoformat() if req.created_at else None,
                'processed_at': req.processed_at.isoformat() if req.processed_at else None,
            } for req in requests],
            'pagination': {
                'page': page,
                'limit': limit,
                'total': total,
                'total_pages': (total + limit - 1) // limit
            }
        })
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error getting GDPR requests: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.delete('/admin/requests/{request_id}')
async def delete_gdpr_request(
    request_id: int,
    current_user: User = Depends(authorize_roles('admin')),
    db: Session = Depends(get_db)
):
    """Delete a GDPR request (admin only)."""
    try:
        gdpr_request = db.query(GDPRRequest).options(
            noload(GDPRRequest.user),
            noload(GDPRRequest.processor)
        ).filter(GDPRRequest.id == request_id).first()
        
        if not gdpr_request:
            raise HTTPException(status_code=404, detail='GDPR request not found')
        
        db.delete(gdpr_request)
        db.commit()
        
        logger.info(f'GDPR request {request_id} deleted by admin {current_user.id}')
        return success_response(message='GDPR request deleted successfully')
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error deleting GDPR request: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

# GDPR Rights - Additional Routes

class DataRectificationRequest(BaseModel):
    corrections: Dict[str, Any]  # e.g., {"full_name": "New Name", "email": "new@email.com"}

@router.post('/rectify')
async def request_data_rectification(
    request: Request,
    rectification_data: DataRectificationRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Request data rectification (Article 16 GDPR - Right to rectification)."""
    try:
        client_ip = request.client.host if request.client else None
        user_agent = request.headers.get('User-Agent')
        
        gdpr_request = await gdpr_service.request_data_rectification(
            db=db,
            user_id=current_user.id,
            corrections=rectification_data.corrections,
            ip_address=client_ip,
            user_agent=user_agent
        )
        
        return success_response(
            data={
                'request_id': gdpr_request.id,
                'verification_token': gdpr_request.verification_token,
                'status': gdpr_request.status.value
            },
            message='Data rectification request created. An admin will review and process your request.'
        )
    except Exception as e:
        logger.error(f'Error creating rectification request: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

class ProcessingRestrictionRequest(BaseModel):
    reason: str

@router.post('/restrict')
async def request_processing_restriction(
    request: Request,
    restriction_data: ProcessingRestrictionRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Request restriction of processing (Article 18 GDPR)."""
    try:
        client_ip = request.client.host if request.client else None
        user_agent = request.headers.get('User-Agent')
        
        gdpr_request = await gdpr_service.request_processing_restriction(
            db=db,
            user_id=current_user.id,
            reason=restriction_data.reason,
            ip_address=client_ip,
            user_agent=user_agent
        )
        
        return success_response(
            data={
                'request_id': gdpr_request.id,
                'verification_token': gdpr_request.verification_token,
                'status': gdpr_request.status.value
            },
            message='Processing restriction request created. Your account has been temporarily restricted.'
        )
    except Exception as e:
        logger.error(f'Error creating restriction request: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

class ProcessingObjectionRequest(BaseModel):
    processing_purpose: str
    reason: Optional[str] = None

@router.post('/object')
async def request_processing_objection(
    request: Request,
    objection_data: ProcessingObjectionRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Object to processing (Article 21 GDPR - Right to object)."""
    try:
        client_ip = request.client.host if request.client else None
        user_agent = request.headers.get('User-Agent')
        
        gdpr_request = await gdpr_service.request_processing_objection(
            db=db,
            user_id=current_user.id,
            processing_purpose=objection_data.processing_purpose,
            reason=objection_data.reason,
            ip_address=client_ip,
            user_agent=user_agent
        )
        
        return success_response(
            data={
                'request_id': gdpr_request.id,
                'verification_token': gdpr_request.verification_token,
                'status': gdpr_request.status.value
            },
            message='Processing objection registered. We will review your objection and stop processing for the specified purpose if valid.'
        )
    except Exception as e:
        logger.error(f'Error creating objection request: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

# Consent Management Routes

class ConsentUpdateRequest(BaseModel):
    consents: Dict[str, bool]  # e.g., {"marketing": true, "analytics": false}

@router.get('/consents')
async def get_user_consents(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Get user's consent status for all consent types."""
    try:
        consents = consent_service.get_user_consents(db=db, user_id=current_user.id, include_withdrawn=True)
        
        consent_status = {}
        for consent_type in ConsentType:
            consent_status[consent_type.value] = {
                'has_consent': consent_service.has_consent(db=db, user_id=current_user.id, consent_type=consent_type),
                'granted_at': None,
                'withdrawn_at': None,
                'status': 'none'
            }
        
        for consent in consents:
            consent_status[consent.consent_type.value] = {
                'has_consent': consent.status == ConsentStatus.granted and (not consent.expires_at or consent.expires_at > datetime.utcnow()),
                'granted_at': consent.granted_at.isoformat() if consent.granted_at else None,
                'withdrawn_at': consent.withdrawn_at.isoformat() if consent.withdrawn_at else None,
                'status': consent.status.value,
                'expires_at': consent.expires_at.isoformat() if consent.expires_at else None
            }
        
        return success_response(data={'consents': consent_status})
    except Exception as e:
        logger.error(f'Error getting consents: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/consents')
async def update_consents(
    request: Request,
    consent_data: ConsentUpdateRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Update user consent preferences."""
    try:
        client_ip = request.client.host if request.client else None
        user_agent = request.headers.get('User-Agent')
        
        # Convert string keys to ConsentType enum
        consents_dict = {}
        for key, value in consent_data.consents.items():
            try:
                consent_type = ConsentType(key)
                consents_dict[consent_type] = value
            except ValueError:
                continue
        
        results = await consent_service.update_consent_preferences(
            db=db,
            user_id=current_user.id,
            consents=consents_dict,
            legal_basis='consent',
            ip_address=client_ip,
            user_agent=user_agent,
            source='gdpr_page'
        )
        
        return success_response(
            data={'updated_consents': len(results)},
            message='Consent preferences updated successfully'
        )
    except Exception as e:
        logger.error(f'Error updating consents: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/consents/{consent_type}/withdraw')
async def withdraw_consent(
    request: Request,
    consent_type: str,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Withdraw specific consent (Article 7(3) GDPR)."""
    try:
        client_ip = request.client.host if request.client else None
        user_agent = request.headers.get('User-Agent')
        
        try:
            consent_type_enum = ConsentType(consent_type)
        except ValueError:
            raise HTTPException(status_code=400, detail=f'Invalid consent type: {consent_type}')
        
        consent = await consent_service.withdraw_consent(
            db=db,
            user_id=current_user.id,
            consent_type=consent_type_enum,
            ip_address=client_ip,
            user_agent=user_agent
        )
        
        return success_response(
            data={
                'consent_id': consent.id,
                'consent_type': consent.consent_type.value,
                'withdrawn_at': consent.withdrawn_at.isoformat() if consent.withdrawn_at else None
            },
            message=f'Consent for {consent_type} withdrawn successfully'
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f'Error withdrawing consent: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

# Data Processing Records (User View)

@router.get('/processing-records')
async def get_user_processing_records(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Get data processing records for the user (Article 15 GDPR - Right of access)."""
    try:
        summary = data_processing_service.get_user_processing_summary(
            db=db,
            user_id=current_user.id
        )
        
        return success_response(data=summary)
    except Exception as e:
        logger.error(f'Error getting processing records: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

# Admin Routes for Processing Requests

@router.post('/admin/rectify/{request_id}/process')
async def process_rectification(
    request_id: int,
    current_user: User = Depends(authorize_roles('admin')),
    db: Session = Depends(get_db)
):
    """Process data rectification request (admin only)."""
    try:
        result = await gdpr_service.process_data_rectification(
            db=db,
            request_id=request_id,
            processed_by=current_user.id
        )
        
        return success_response(
            data=result,
            message='Data rectification processed successfully'
        )
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    except Exception as e:
        logger.error(f'Error processing rectification: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

