"""
User consent management API endpoints for GDPR compliance
"""

from typing import List, Optional
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, status, Request
from pydantic import BaseModel, Field
from datetime import datetime, timezone
import structlog
import hashlib

from shared.auth.decorators import get_current_user_dep
from app.core.database import get_db
from app.models.consent import UserConsent, ConsentHistory
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_

logger = structlog.get_logger()

router = APIRouter()


class ConsentRequest(BaseModel):
    """Request model for granting/updating consent"""
    terms_accepted: bool = Field(..., description="Accept terms of service")
    privacy_accepted: bool = Field(..., description="Accept privacy policy")
    marketing_consent: bool = Field(default=False, description="Consent to marketing communications")
    analytics_consent: bool = Field(default=False, description="Consent to analytics cookies")
    consent_method: str = Field(..., description="How consent was given (registration, settings, cookie_banner)")
    consent_version: str = Field(default="1.0", description="Version of terms/privacy policy")


class ConsentResponse(BaseModel):
    """Response model for consent data"""
    id: str
    user_id: str
    terms_accepted: bool
    privacy_accepted: bool
    marketing_consent: bool
    analytics_consent: bool
    consent_version: str
    consent_method: str
    consented_at: str
    withdrawn_at: Optional[str]


class ConsentHistoryResponse(BaseModel):
    """Response model for consent history"""
    id: str
    user_id: str
    action: str
    consent_snapshot: dict
    created_at: str


def hash_text(text: str) -> str:
    """Create hash of consent text for verification"""
    return hashlib.sha256(text.encode()).hexdigest()


@router.post("/api/v1/auth/me/consent", response_model=ConsentResponse, status_code=status.HTTP_201_CREATED)
async def record_consent(
    consent_data: ConsentRequest,
    request: Request,
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Record user consent for data processing
    GDPR Article 7 - Conditions for consent
    """
    try:
        user_id = UUID(current_user["user_id"])

        ip_address = request.client.host if request.client else None
        user_agent = request.headers.get("user-agent")

        consent = UserConsent(
            user_id=user_id,
            terms_accepted=consent_data.terms_accepted,
            privacy_accepted=consent_data.privacy_accepted,
            marketing_consent=consent_data.marketing_consent,
            analytics_consent=consent_data.analytics_consent,
            consent_version=consent_data.consent_version,
            consent_method=consent_data.consent_method,
            ip_address=ip_address,
            user_agent=user_agent,
            consented_at=datetime.now(timezone.utc)
        )

        db.add(consent)
        await db.flush()

        history = ConsentHistory(
            user_id=user_id,
            consent_id=consent.id,
            action="granted",
            consent_snapshot=consent_data.dict(),
            ip_address=ip_address,
            user_agent=user_agent,
            consent_method=consent_data.consent_method,
            created_at=datetime.now(timezone.utc)
        )
        db.add(history)

        await db.commit()
        await db.refresh(consent)

        logger.info(
            "consent_recorded",
            user_id=str(user_id),
            consent_version=consent_data.consent_version,
            method=consent_data.consent_method
        )

        return ConsentResponse(
            id=str(consent.id),
            user_id=str(consent.user_id),
            terms_accepted=consent.terms_accepted,
            privacy_accepted=consent.privacy_accepted,
            marketing_consent=consent.marketing_consent,
            analytics_consent=consent.analytics_consent,
            consent_version=consent.consent_version,
            consent_method=consent.consent_method,
            consented_at=consent.consented_at.isoformat(),
            withdrawn_at=consent.withdrawn_at.isoformat() if consent.withdrawn_at else None
        )

    except Exception as e:
        await db.rollback()
        logger.error("error_recording_consent", error=str(e), user_id=current_user.get("user_id"))
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to record consent"
        )


@router.get("/api/v1/auth/me/consent/current", response_model=Optional[ConsentResponse])
async def get_current_consent(
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Get current active consent for user
    """
    try:
        user_id = UUID(current_user["user_id"])

        query = select(UserConsent).where(
            and_(
                UserConsent.user_id == user_id,
                UserConsent.withdrawn_at.is_(None)
            )
        ).order_by(UserConsent.consented_at.desc())

        result = await db.execute(query)
        consent = result.scalar_one_or_none()

        if not consent:
            return None

        return ConsentResponse(
            id=str(consent.id),
            user_id=str(consent.user_id),
            terms_accepted=consent.terms_accepted,
            privacy_accepted=consent.privacy_accepted,
            marketing_consent=consent.marketing_consent,
            analytics_consent=consent.analytics_consent,
            consent_version=consent.consent_version,
            consent_method=consent.consent_method,
            consented_at=consent.consented_at.isoformat(),
            withdrawn_at=consent.withdrawn_at.isoformat() if consent.withdrawn_at else None
        )

    except Exception as e:
        logger.error("error_getting_consent", error=str(e), user_id=current_user.get("user_id"))
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to retrieve consent"
        )


@router.get("/api/v1/auth/me/consent/history", response_model=List[ConsentHistoryResponse])
async def get_consent_history(
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Get complete consent history for user
    GDPR Article 7(1) - Demonstrating consent
    """
    try:
        user_id = UUID(current_user["user_id"])

        query = select(ConsentHistory).where(
            ConsentHistory.user_id == user_id
        ).order_by(ConsentHistory.created_at.desc())

        result = await db.execute(query)
        history = result.scalars().all()

        return [
            ConsentHistoryResponse(
                id=str(h.id),
                user_id=str(h.user_id),
                action=h.action,
                consent_snapshot=h.consent_snapshot,
                created_at=h.created_at.isoformat()
            )
            for h in history
        ]

    except Exception as e:
        logger.error("error_getting_consent_history", error=str(e), user_id=current_user.get("user_id"))
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to retrieve consent history"
        )


@router.put("/api/v1/auth/me/consent", response_model=ConsentResponse)
async def update_consent(
    consent_data: ConsentRequest,
    request: Request,
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Update user consent preferences
    GDPR Article 7(3) - Withdrawal of consent
    """
    try:
        user_id = UUID(current_user["user_id"])

        query = select(UserConsent).where(
            and_(
                UserConsent.user_id == user_id,
                UserConsent.withdrawn_at.is_(None)
            )
        ).order_by(UserConsent.consented_at.desc())

        result = await db.execute(query)
        current_consent = result.scalar_one_or_none()

        if current_consent:
            current_consent.withdrawn_at = datetime.now(timezone.utc)
            history = ConsentHistory(
                user_id=user_id,
                consent_id=current_consent.id,
                action="updated",
                consent_snapshot=current_consent.to_dict(),
                ip_address=request.client.host if request.client else None,
                user_agent=request.headers.get("user-agent"),
                consent_method=consent_data.consent_method,
                created_at=datetime.now(timezone.utc)
            )
            db.add(history)

        new_consent = UserConsent(
            user_id=user_id,
            terms_accepted=consent_data.terms_accepted,
            privacy_accepted=consent_data.privacy_accepted,
            marketing_consent=consent_data.marketing_consent,
            analytics_consent=consent_data.analytics_consent,
            consent_version=consent_data.consent_version,
            consent_method=consent_data.consent_method,
            ip_address=request.client.host if request.client else None,
            user_agent=request.headers.get("user-agent"),
            consented_at=datetime.now(timezone.utc)
        )

        db.add(new_consent)
        await db.flush()

        history = ConsentHistory(
            user_id=user_id,
            consent_id=new_consent.id,
            action="granted" if not current_consent else "updated",
            consent_snapshot=consent_data.dict(),
            ip_address=request.client.host if request.client else None,
            user_agent=request.headers.get("user-agent"),
            consent_method=consent_data.consent_method,
            created_at=datetime.now(timezone.utc)
        )
        db.add(history)

        await db.commit()
        await db.refresh(new_consent)

        logger.info(
            "consent_updated",
            user_id=str(user_id),
            consent_version=consent_data.consent_version
        )

        return ConsentResponse(
            id=str(new_consent.id),
            user_id=str(new_consent.user_id),
            terms_accepted=new_consent.terms_accepted,
            privacy_accepted=new_consent.privacy_accepted,
            marketing_consent=new_consent.marketing_consent,
            analytics_consent=new_consent.analytics_consent,
            consent_version=new_consent.consent_version,
            consent_method=new_consent.consent_method,
            consented_at=new_consent.consented_at.isoformat(),
            withdrawn_at=None
        )

    except Exception as e:
        await db.rollback()
        logger.error("error_updating_consent", error=str(e), user_id=current_user.get("user_id"))
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to update consent"
        )


@router.post("/api/v1/auth/me/consent/withdraw", status_code=status.HTTP_200_OK)
async def withdraw_consent(
    request: Request,
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """
    Withdraw all consent
    GDPR Article 7(3) - Right to withdraw consent
    """
    try:
        user_id = UUID(current_user["user_id"])

        query = select(UserConsent).where(
            and_(
                UserConsent.user_id == user_id,
                UserConsent.withdrawn_at.is_(None)
            )
        )

        result = await db.execute(query)
        consents = result.scalars().all()

        for consent in consents:
            consent.withdrawn_at = datetime.now(timezone.utc)

            history = ConsentHistory(
                user_id=user_id,
                consent_id=consent.id,
                action="withdrawn",
                consent_snapshot=consent.to_dict(),
                ip_address=request.client.host if request.client else None,
                user_agent=request.headers.get("user-agent"),
                consent_method="user_withdrawal",
                created_at=datetime.now(timezone.utc)
            )
            db.add(history)

        await db.commit()

        logger.info("consent_withdrawn", user_id=str(user_id), count=len(consents))

        return {
            "message": "Consent withdrawn successfully",
            "withdrawn_count": len(consents)
        }

    except Exception as e:
        await db.rollback()
        logger.error("error_withdrawing_consent", error=str(e), user_id=current_user.get("user_id"))
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to withdraw consent"
        )
