# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
from fastapi import APIRouter, HTTPException, Depends, Body, Path, Request, Query
from typing import List, Dict, Any, Optional
from fastapi import status
import logging
import json

# Import auth module
from modules.auth import limiter, getCurrentUser, getRequestContext, RequestContext
from modules.datamodels.datamodelRbac import Role

# Import interfaces
import modules.interfaces.interfaceDbManagement as interfaceDbManagement
from modules.datamodels.datamodelMessaging import (
    MessagingSubscription,
    MessagingSubscriptionRegistration,
    MessagingDelivery,
    MessagingChannel,
    MessagingEventParameters,
    MessagingSubscriptionExecutionResult
)
from modules.datamodels.datamodelUam import User
from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResponse, PaginationMetadata

# Configure logger
logger = logging.getLogger(__name__)

# Create router for messaging endpoints
router = APIRouter(
    prefix="/api/messaging",
    tags=["Messaging"],
    responses={404: {"description": "Not found"}}
)


# Subscription Endpoints

@router.get("/subscriptions", response_model=PaginatedResponse[MessagingSubscription])
@limiter.limit("60/minute")
def get_subscriptions(
    request: Request,
    pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
    currentUser: User = Depends(getCurrentUser)
) -> PaginatedResponse[MessagingSubscription]:
    """Get subscriptions with optional pagination, sorting, and filtering."""
    paginationParams = None
    if pagination:
        try:
            paginationDict = json.loads(pagination)
            paginationParams = PaginationParams(**paginationDict) if paginationDict else None
        except (json.JSONDecodeError, ValueError) as e:
            raise HTTPException(
                status_code=400,
                detail=f"Invalid pagination parameter: {str(e)}"
            )
    
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    result = managementInterface.getAllSubscriptions(pagination=paginationParams)
    
    if paginationParams:
        return PaginatedResponse(
            items=result.items,
            pagination=PaginationMetadata(
                currentPage=paginationParams.page,
                pageSize=paginationParams.pageSize,
                totalItems=result.totalItems,
                totalPages=result.totalPages,
                sort=paginationParams.sort,
                filters=paginationParams.filters
            )
        )
    else:
        return PaginatedResponse(
            items=result,
            pagination=None
        )


@router.post("/subscriptions", response_model=MessagingSubscription)
@limiter.limit("60/minute")
def create_subscription(
    request: Request,
    subscription: MessagingSubscription,
    currentUser: User = Depends(getCurrentUser)
) -> MessagingSubscription:
    """Create a new subscription"""
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    
    subscriptionData = subscription.model_dump(exclude={"id"})
    newSubscription = managementInterface.createSubscription(subscriptionData)
    
    return MessagingSubscription(**newSubscription)


@router.get("/subscriptions/{subscriptionId}", response_model=MessagingSubscription)
@limiter.limit("60/minute")
def get_subscription(
    request: Request,
    subscriptionId: str = Path(..., description="ID of the subscription"),
    currentUser: User = Depends(getCurrentUser)
) -> MessagingSubscription:
    """Get a specific subscription"""
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    
    subscription = managementInterface.getSubscription(subscriptionId)
    if not subscription:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Subscription with ID {subscriptionId} not found"
        )
    
    return subscription


@router.put("/subscriptions/{subscriptionId}", response_model=MessagingSubscription)
@limiter.limit("60/minute")
def update_subscription(
    request: Request,
    subscriptionId: str = Path(..., description="ID of the subscription to update"),
    subscriptionData: MessagingSubscription = Body(...),
    currentUser: User = Depends(getCurrentUser)
) -> MessagingSubscription:
    """Update an existing subscription"""
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    
    existingSubscription = managementInterface.getSubscription(subscriptionId)
    if not existingSubscription:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Subscription with ID {subscriptionId} not found"
        )
    
    updateData = subscriptionData.model_dump(exclude={"id", "subscriptionId"})
    updatedSubscription = managementInterface.updateSubscription(subscriptionId, updateData)
    
    if not updatedSubscription:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Error updating the subscription"
        )
    
    return MessagingSubscription(**updatedSubscription)


@router.delete("/subscriptions/{subscriptionId}", response_model=Dict[str, Any])
@limiter.limit("60/minute")
def delete_subscription(
    request: Request,
    subscriptionId: str = Path(..., description="ID of the subscription to delete"),
    currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
    """Delete a subscription"""
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    
    existingSubscription = managementInterface.getSubscription(subscriptionId)
    if not existingSubscription:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Subscription with ID {subscriptionId} not found"
        )
    
    success = managementInterface.deleteSubscription(subscriptionId)
    if not success:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Error deleting the subscription"
        )
    
    return {"message": f"Subscription with ID {subscriptionId} successfully deleted"}


# Registration Endpoints

@router.get("/subscriptions/{subscriptionId}/registrations", response_model=PaginatedResponse[MessagingSubscriptionRegistration])
@limiter.limit("60/minute")
def get_subscription_registrations(
    request: Request,
    subscriptionId: str = Path(..., description="ID of the subscription"),
    pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
    currentUser: User = Depends(getCurrentUser)
) -> PaginatedResponse[MessagingSubscriptionRegistration]:
    """Get registrations for a subscription"""
    paginationParams = None
    if pagination:
        try:
            paginationDict = json.loads(pagination)
            paginationParams = PaginationParams(**paginationDict) if paginationDict else None
        except (json.JSONDecodeError, ValueError) as e:
            raise HTTPException(
                status_code=400,
                detail=f"Invalid pagination parameter: {str(e)}"
            )
    
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    result = managementInterface.getAllRegistrations(
        subscriptionId=subscriptionId,
        pagination=paginationParams
    )
    
    if paginationParams:
        return PaginatedResponse(
            items=result.items,
            pagination=PaginationMetadata(
                currentPage=paginationParams.page,
                pageSize=paginationParams.pageSize,
                totalItems=result.totalItems,
                totalPages=result.totalPages,
                sort=paginationParams.sort,
                filters=paginationParams.filters
            )
        )
    else:
        return PaginatedResponse(
            items=result,
            pagination=None
        )


@router.post("/subscriptions/{subscriptionId}/subscribe", response_model=MessagingSubscriptionRegistration)
@limiter.limit("60/minute")
def subscribe_user(
    request: Request,
    subscriptionId: str = Path(..., description="ID of the subscription"),
    channel: MessagingChannel = Body(..., embed=True),
    channelConfig: str = Body(..., embed=True),
    currentUser: User = Depends(getCurrentUser)
) -> MessagingSubscriptionRegistration:
    """Subscribe user to a subscription with a specific channel"""
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    
    registration = managementInterface.subscribeUser(
        subscriptionId=subscriptionId,
        userId=currentUser.id,
        channel=channel,
        channelConfig=channelConfig
    )
    
    return MessagingSubscriptionRegistration(**registration)


@router.delete("/subscriptions/{subscriptionId}/unsubscribe", response_model=Dict[str, Any])
@limiter.limit("60/minute")
def unsubscribe_user(
    request: Request,
    subscriptionId: str = Path(..., description="ID of the subscription"),
    channel: MessagingChannel = Body(..., embed=True),
    currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
    """Unsubscribe user from a subscription for a specific channel"""
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    
    success = managementInterface.unsubscribeUser(
        subscriptionId=subscriptionId,
        userId=currentUser.id,
        channel=channel
    )
    
    if not success:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Registration not found"
        )
    
    return {"message": f"Successfully unsubscribed from {subscriptionId} for channel {channel.value}"}


@router.get("/registrations", response_model=PaginatedResponse[MessagingSubscriptionRegistration])
@limiter.limit("60/minute")
def get_my_registrations(
    request: Request,
    pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
    currentUser: User = Depends(getCurrentUser)
) -> PaginatedResponse[MessagingSubscriptionRegistration]:
    """Get own registrations"""
    paginationParams = None
    if pagination:
        try:
            paginationDict = json.loads(pagination)
            paginationParams = PaginationParams(**paginationDict) if paginationDict else None
        except (json.JSONDecodeError, ValueError) as e:
            raise HTTPException(
                status_code=400,
                detail=f"Invalid pagination parameter: {str(e)}"
            )
    
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    result = managementInterface.getAllRegistrations(
        userId=currentUser.id,
        pagination=paginationParams
    )
    
    if paginationParams:
        return PaginatedResponse(
            items=result.items,
            pagination=PaginationMetadata(
                currentPage=paginationParams.page,
                pageSize=paginationParams.pageSize,
                totalItems=result.totalItems,
                totalPages=result.totalPages,
                sort=paginationParams.sort,
                filters=paginationParams.filters
            )
        )
    else:
        return PaginatedResponse(
            items=result,
            pagination=None
        )


@router.put("/registrations/{registrationId}", response_model=MessagingSubscriptionRegistration)
@limiter.limit("60/minute")
def update_registration(
    request: Request,
    registrationId: str = Path(..., description="ID of the registration to update"),
    registrationData: MessagingSubscriptionRegistration = Body(...),
    currentUser: User = Depends(getCurrentUser)
) -> MessagingSubscriptionRegistration:
    """Update a registration"""
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    
    existingRegistration = managementInterface.getRegistration(registrationId)
    if not existingRegistration:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Registration with ID {registrationId} not found"
        )
    
    updateData = registrationData.model_dump(exclude={"id", "subscriptionId", "userId"})
    updatedRegistration = managementInterface.updateRegistration(registrationId, updateData)
    
    if not updatedRegistration:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Error updating the registration"
        )
    
    return MessagingSubscriptionRegistration(**updatedRegistration)


@router.delete("/registrations/{registrationId}", response_model=Dict[str, Any])
@limiter.limit("60/minute")
def delete_registration(
    request: Request,
    registrationId: str = Path(..., description="ID of the registration to delete"),
    currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
    """Delete a registration"""
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    
    existingRegistration = managementInterface.getRegistration(registrationId)
    if not existingRegistration:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Registration with ID {registrationId} not found"
        )
    
    success = managementInterface.deleteRegistration(registrationId)
    if not success:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Error deleting the registration"
        )
    
    return {"message": f"Registration with ID {registrationId} successfully deleted"}


# Trigger Endpoints

def _getTriggerKey(request: Request) -> str:
    """Custom key function for trigger rate limiting per subscriptionId"""
    subscriptionId = request.path_params.get("subscriptionId", "unknown")
    return f"{request.client.host}:{subscriptionId}"


@router.post("/trigger/{subscriptionId}", response_model=MessagingSubscriptionExecutionResult)
@limiter.limit("60/minute", key_func=_getTriggerKey)
def trigger_subscription(
    request: Request,
    subscriptionId: str = Path(..., description="ID of the subscription to trigger"),
    eventParameters: Dict[str, Any] = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> MessagingSubscriptionExecutionResult:
    """
    Trigger a subscription with event parameters.
    
    Requires Mandate-Admin role or SysAdmin.
    """
    # RBAC-Check: Admin or Mandate-Admin can trigger
    if not _hasTriggerPermission(context):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Admin or Mandate-Admin role required to trigger subscriptions"
        )
    
    # Get messaging service from request app state
    from modules.services import getInterface as getServicesInterface
    services = getServicesInterface(context.user, None, mandateId=str(context.mandateId))
    
    # Konvertiere Dict zu Pydantic Model
    eventParams = MessagingEventParameters(triggerData=eventParameters)
    
    executionResult = services.messaging.executeSubscription(subscriptionId, eventParams)
    return executionResult


def _hasTriggerPermission(context: RequestContext) -> bool:
    """
    Check if user has permission to trigger subscriptions.
    Requires admin or mandate-admin role.
    """
    if context.hasSysAdminRole:
        return True
    
    if not context.roleIds:
        return False
    
    try:
        from modules.interfaces.interfaceDbApp import getRootInterface
        rootInterface = getRootInterface()
        
        for roleId in context.roleIds:
            role = rootInterface.getRole(roleId)
            if role:
                roleLabel = role.roleLabel
                # Admin role at mandate level or system admin
                if roleLabel in ("admin", "sysadmin"):
                    return True
        
        return False
        
    except Exception as e:
        logger.error(f"Error checking trigger permission: {e}")
        return False


# Delivery Endpoints

@router.get("/deliveries", response_model=PaginatedResponse[MessagingDelivery])
@limiter.limit("60/minute")
def get_deliveries(
    request: Request,
    subscriptionId: Optional[str] = Query(None, description="Filter by subscription ID"),
    pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
    currentUser: User = Depends(getCurrentUser)
) -> PaginatedResponse[MessagingDelivery]:
    """Get delivery history"""
    paginationParams = None
    if pagination:
        try:
            paginationDict = json.loads(pagination)
            paginationParams = PaginationParams(**paginationDict) if paginationDict else None
        except (json.JSONDecodeError, ValueError) as e:
            raise HTTPException(
                status_code=400,
                detail=f"Invalid pagination parameter: {str(e)}"
            )
    
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    result = managementInterface.getDeliveries(
        subscriptionId=subscriptionId,
        userId=currentUser.id,  # Users can only see their own deliveries
        pagination=paginationParams
    )
    
    if paginationParams:
        return PaginatedResponse(
            items=result.items,
            pagination=PaginationMetadata(
                currentPage=paginationParams.page,
                pageSize=paginationParams.pageSize,
                totalItems=result.totalItems,
                totalPages=result.totalPages,
                sort=paginationParams.sort,
                filters=paginationParams.filters
            )
        )
    else:
        return PaginatedResponse(
            items=result,
            pagination=None
        )


@router.get("/deliveries/{deliveryId}", response_model=MessagingDelivery)
@limiter.limit("60/minute")
def get_delivery(
    request: Request,
    deliveryId: str = Path(..., description="ID of the delivery"),
    currentUser: User = Depends(getCurrentUser)
) -> MessagingDelivery:
    """Get a specific delivery"""
    managementInterface = interfaceDbManagement.getInterface(currentUser)
    
    delivery = managementInterface.getDelivery(deliveryId)
    if not delivery:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Delivery with ID {deliveryId} not found"
        )
    
    return delivery

