"""
GDPR Consent Management Service.
"""
from sqlalchemy.orm import Session
from typing import Dict, Any, Optional, List
from datetime import datetime, timedelta
from ..models.consent import Consent, ConsentType, ConsentStatus
from ...auth.models.user import User
from ...shared.config.logging_config import get_logger
from ...analytics.services.audit_service import audit_service

logger = get_logger(__name__)

class ConsentService:
    """Service for managing user consent (Article 7 GDPR)."""
    
    @staticmethod
    async def grant_consent(
        db: Session,
        user_id: int,
        consent_type: ConsentType,
        legal_basis: str,
        consent_method: str = 'explicit',
        consent_version: Optional[str] = None,
        expires_at: Optional[datetime] = None,
        ip_address: Optional[str] = None,
        user_agent: Optional[str] = None,
        source: Optional[str] = None,
        extra_metadata: Optional[Dict[str, Any]] = None
    ) -> Consent:
        """Grant consent for a specific purpose."""
        # Withdraw any existing consent of this type
        existing = db.query(Consent).filter(
            Consent.user_id == user_id,
            Consent.consent_type == consent_type,
            Consent.status == ConsentStatus.granted
        ).first()
        
        if existing:
            existing.status = ConsentStatus.withdrawn
            existing.withdrawn_at = datetime.utcnow()
        
        # Create new consent
        consent = Consent(
            user_id=user_id,
            consent_type=consent_type,
            status=ConsentStatus.granted,
            granted_at=datetime.utcnow(),
            expires_at=expires_at,
            legal_basis=legal_basis,
            consent_method=consent_method,
            consent_version=consent_version,
            ip_address=ip_address,
            user_agent=user_agent,
            source=source,
            extra_metadata=extra_metadata
        )
        
        db.add(consent)
        db.commit()
        db.refresh(consent)
        
        # Log consent grant
        await audit_service.log_action(
            db=db,
            action='consent_granted',
            resource_type='consent',
            user_id=user_id,
            resource_id=consent.id,
            ip_address=ip_address,
            user_agent=user_agent,
            details={
                'consent_type': consent_type.value,
                'legal_basis': legal_basis,
                'consent_method': consent_method
            },
            status='success'
        )
        
        logger.info(f'Consent granted: {consent_type.value} for user {user_id}')
        return consent
    
    @staticmethod
    async def withdraw_consent(
        db: Session,
        user_id: int,
        consent_type: ConsentType,
        ip_address: Optional[str] = None,
        user_agent: Optional[str] = None
    ) -> Consent:
        """Withdraw consent (Article 7(3) GDPR)."""
        consent = db.query(Consent).filter(
            Consent.user_id == user_id,
            Consent.consent_type == consent_type,
            Consent.status == ConsentStatus.granted
        ).order_by(Consent.granted_at.desc()).first()
        
        if not consent:
            raise ValueError(f'No active consent found for {consent_type.value}')
        
        consent.status = ConsentStatus.withdrawn
        consent.withdrawn_at = datetime.utcnow()
        db.commit()
        db.refresh(consent)
        
        # Log consent withdrawal
        await audit_service.log_action(
            db=db,
            action='consent_withdrawn',
            resource_type='consent',
            user_id=user_id,
            resource_id=consent.id,
            ip_address=ip_address,
            user_agent=user_agent,
            details={'consent_type': consent_type.value},
            status='success'
        )
        
        logger.info(f'Consent withdrawn: {consent_type.value} for user {user_id}')
        return consent
    
    @staticmethod
    def get_user_consents(
        db: Session,
        user_id: int,
        include_withdrawn: bool = False
    ) -> List[Consent]:
        """Get all consents for a user."""
        query = db.query(Consent).filter(Consent.user_id == user_id)
        
        if not include_withdrawn:
            query = query.filter(Consent.status == ConsentStatus.granted)
        
        return query.order_by(Consent.granted_at.desc()).all()
    
    @staticmethod
    def has_consent(
        db: Session,
        user_id: int,
        consent_type: ConsentType
    ) -> bool:
        """Check if user has active consent for a specific type."""
        consent = db.query(Consent).filter(
            Consent.user_id == user_id,
            Consent.consent_type == consent_type,
            Consent.status == ConsentStatus.granted
        ).first()
        
        if not consent:
            return False
        
        # Check if expired
        if consent.expires_at and consent.expires_at < datetime.utcnow():
            consent.status = ConsentStatus.expired
            db.commit()
            return False
        
        return True
    
    @staticmethod
    async def update_consent_preferences(
        db: Session,
        user_id: int,
        consents: Dict[ConsentType, bool],
        legal_basis: str = 'consent',
        ip_address: Optional[str] = None,
        user_agent: Optional[str] = None,
        source: Optional[str] = None
    ) -> List[Consent]:
        """Update multiple consent preferences at once."""
        results = []
        
        for consent_type, granted in consents.items():
            if granted:
                consent = await ConsentService.grant_consent(
                    db=db,
                    user_id=user_id,
                    consent_type=consent_type,
                    legal_basis=legal_basis,
                    ip_address=ip_address,
                    user_agent=user_agent,
                    source=source
                )
                results.append(consent)
            else:
                try:
                    consent = await ConsentService.withdraw_consent(
                        db=db,
                        user_id=user_id,
                        consent_type=consent_type,
                        ip_address=ip_address,
                        user_agent=user_agent
                    )
                    results.append(consent)
                except ValueError:
                    # No active consent to withdraw
                    pass
        
        return results

consent_service = ConsentService()

