# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Routes for Trustee feature data management.
Implements CRUD operations for organisations, roles, access, contracts, documents, and positions.

URL Structure: /api/trustee/{instanceId}/{entity}
- instanceId is the FeatureInstance ID (required for all operations)
- This ensures proper multi-tenant isolation at the URL level
"""

from fastapi import APIRouter, HTTPException, Depends, Body, Path, Request, Query, Response, UploadFile, File, Form
from fastapi.responses import StreamingResponse
from typing import List, Dict, Any, Optional
from fastapi import status
import logging
import json
import io
import base64

from modules.auth import limiter, getRequestContext, RequestContext
from .interfaceFeatureTrustee import getInterface
from modules.interfaces.interfaceDbApp import getRootInterface
from modules.interfaces.interfaceFeatures import getFeatureInterface
from .datamodelFeatureTrustee import (
    TrusteeOrganisation,
    TrusteeRole,
    TrusteeAccess,
    TrusteeContract,
    TrusteeDocument,
    TrusteePosition,
    TrusteePositionDocument,
)
from modules.datamodels.datamodelPagination import (
    PaginationParams,
    PaginatedResponse,
    PaginationMetadata,
    normalize_pagination_dict,
)

logger = logging.getLogger(__name__)

router = APIRouter(
    prefix="/api/trustee",
    tags=["Trustee"],
    responses={404: {"description": "Not found"}}
)


# ===== Helper Functions =====

def _parsePagination(pagination: Optional[str]) -> Optional[PaginationParams]:
    """Parse pagination parameter from JSON string."""
    if not pagination:
        return None
    try:
        paginationDict = json.loads(pagination)
        if paginationDict:
            paginationDict = normalize_pagination_dict(paginationDict)
            return PaginationParams(**paginationDict)
    except (json.JSONDecodeError, ValueError) as e:
        raise HTTPException(
            status_code=400,
            detail=f"Invalid pagination parameter: {str(e)}"
        )
    return None


def _validateInstanceAccess(instanceId: str, context: RequestContext) -> str:
    """
    Validate that the user has access to the feature instance.
    Returns the mandateId for the instance.
    
    Args:
        instanceId: The FeatureInstance ID from URL
        context: The request context with user info
        
    Returns:
        mandateId of the instance
        
    Raises:
        HTTPException 404 if instance not found
        HTTPException 403 if user doesn't have access
    """
    rootInterface = getRootInterface()
    featureInterface = getFeatureInterface(rootInterface.db)
    
    instance = featureInterface.getFeatureInstance(instanceId)
    if not instance:
        raise HTTPException(
            status_code=404,
            detail=f"Feature instance '{instanceId}' not found"
        )
    
    # Verify it's a trustee instance
    if instance.featureCode != "trustee":
        raise HTTPException(
            status_code=400,
            detail=f"Instance '{instanceId}' is not a trustee instance"
        )
    
    # Verify user has access to this instance
    if not context.hasSysAdminRole:
        # Check if user has FeatureAccess for this instance
        featureAccesses = rootInterface.getFeatureAccessesForUser(str(context.user.id))
        hasAccess = any(
            str(fa.featureInstanceId) == instanceId and fa.enabled
            for fa in featureAccesses
        )
        if not hasAccess:
            raise HTTPException(
                status_code=403,
                detail=f"Access denied to feature instance '{instanceId}'"
            )
    
    return str(instance.mandateId)


# ============================================================================
# ATTRIBUTES ENDPOINT (for FormGeneratorTable)
# ============================================================================

# Mapping of entity names to Pydantic model classes
_TRUSTEE_ENTITY_MODELS = {
    "TrusteeOrganisation": TrusteeOrganisation,
    "TrusteeRole": TrusteeRole,
    "TrusteeAccess": TrusteeAccess,
    "TrusteeContract": TrusteeContract,
    "TrusteeDocument": TrusteeDocument,
    "TrusteePosition": TrusteePosition,
    "TrusteePositionDocument": TrusteePositionDocument,
}


@router.get("/{instanceId}/attributes/{entityType}")
@limiter.limit("30/minute")
def get_entity_attributes(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    entityType: str = Path(..., description="Entity type (e.g., TrusteeDocument)"),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """
    Get attribute definitions for a Trustee entity.
    Used by FormGeneratorTable for dynamic column generation.
    """
    # Validate instance access
    _validateInstanceAccess(instanceId, context)
    
    # Check if entity type is valid
    if entityType not in _TRUSTEE_ENTITY_MODELS:
        raise HTTPException(
            status_code=404,
            detail=f"Unknown entity type: {entityType}. Valid types: {list(_TRUSTEE_ENTITY_MODELS.keys())}"
        )
    
    # Get the model class
    modelClass = _TRUSTEE_ENTITY_MODELS[entityType]
    
    # Import the attribute utils
    from modules.shared.attributeUtils import getModelAttributeDefinitions
    
    try:
        attrDefs = getModelAttributeDefinitions(modelClass)
        # Filter to only visible attributes
        visibleAttrs = [
            attr for attr in attrDefs.get("attributes", [])
            if isinstance(attr, dict) and attr.get("visible", True)
        ]
        return {"attributes": visibleAttrs}
    except Exception as e:
        logger.error(f"Error getting attributes for {entityType}: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"Error getting attributes for {entityType}: {str(e)}"
        )


# ============================================================================
# OPTIONS ENDPOINTS (for dropdowns)
# ============================================================================

@router.get("/mime-types/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
def get_mime_type_options(
    request: Request,
    context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
    """Get supported MIME types from the document extraction service.
    Returns: [{ value: "mime/type", label: "Description" }]
    """
    from modules.services.serviceExtraction.subRegistry import ExtractorRegistry
    
    registry = ExtractorRegistry()
    formats = registry.getSupportedFormats()
    
    # Collect all unique MIME types
    allMimeTypes = set()
    for mimeList in formats.get("mime_types", {}).values():
        allMimeTypes.update(mimeList)
    
    # Sort and create options with labels
    result = []
    for mimeType in sorted(allMimeTypes):
        # Create readable label from mime type
        parts = mimeType.split("/")
        if len(parts) == 2:
            mainType, subType = parts
            # Clean up subtype for label
            label = subType.replace("vnd.", "").replace("x-", "").replace("-", " ").title()
            result.append({"value": mimeType, "label": f"{label} ({mimeType})"})
        else:
            result.append({"value": mimeType, "label": mimeType})
    
    return result


@router.get("/{instanceId}/organisations/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
def get_organisation_options(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
    """Get organisation options for select dropdowns. Returns: [{ value, label }]"""
    mandateId = _validateInstanceAccess(instanceId, context)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.getAllOrganisations(None)
    items = result.items if hasattr(result, 'items') else result
    return [{"value": org["id"], "label": org.get("label") or org["id"]} for org in items]


@router.get("/{instanceId}/roles/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
def get_role_options(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
    """Get role options for select dropdowns. Returns: [{ value, label }]"""
    mandateId = _validateInstanceAccess(instanceId, context)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.getAllRoles(None)
    items = result.items if hasattr(result, 'items') else result
    return [{"value": role["id"], "label": role.get("desc") or role["id"]} for role in items]


@router.get("/{instanceId}/contracts/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
def get_contract_options(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    organisationId: Optional[str] = Query(None, description="Optional: Filter by organisation ID"),
    context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
    """
    Get contract options for select dropdowns.
    
    Optionally filter by organisationId to get only contracts for a specific organisation.
    This is useful for dependent dropdowns in forms.
    
    Returns: [{ value, label }]
    """
    mandateId = _validateInstanceAccess(instanceId, context)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    
    if organisationId:
        # Gefiltert nach Organisation
        items = interface.getContractsByOrganisation(organisationId)
    else:
        # Alle Contracts
        result = interface.getAllContracts(None)
        items = result.items if hasattr(result, 'items') else result
    
    return [{"value": c.id, "label": c.label or c.name or c.id} for c in items]


@router.get("/{instanceId}/documents/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
def get_document_options(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
    """Get document options for select dropdowns. Returns: [{ id, value, label }]"""
    mandateId = _validateInstanceAccess(instanceId, context)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.getAllDocuments(None)
    items = result.items if hasattr(result, 'items') else result
    # Include 'id' for FK resolution in tables
    return [{"id": d.id, "value": d.id, "label": d.documentName or d.id} for d in items]


@router.get("/{instanceId}/positions/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
def get_position_options(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
    """Get position options for select dropdowns. Returns: [{ id, value, label }]"""
    mandateId = _validateInstanceAccess(instanceId, context)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.getAllPositions(None)
    items = result.items if hasattr(result, 'items') else result
    
    def _makePositionLabel(p: TrusteePosition) -> str:
        parts = []
        if p.valuta:
            parts.append(str(p.valuta)[:10])  # Datum ohne Zeit
        if p.company:
            parts.append(p.company[:30])
        if p.desc:
            parts.append(p.desc[:30])
        return " - ".join(parts) if parts else p.id
    
    # Include 'id' for FK resolution in tables
    return [{"id": p.id, "value": p.id, "label": _makePositionLabel(p)} for p in items]


# ============================================================================
# CRUD ENDPOINTS
# ============================================================================

# ===== Organisation Routes =====

@router.get("/{instanceId}/organisations", response_model=PaginatedResponse[TrusteeOrganisation])
@limiter.limit("30/minute")
def get_organisations(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams"),
    context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteeOrganisation]:
    """Get all organisations for a feature instance with optional pagination."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    paginationParams = _parsePagination(pagination)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.getAllOrganisations(paginationParams)

    if paginationParams:
        return PaginatedResponse(
            items=result.items,
            pagination=PaginationMetadata(
                currentPage=paginationParams.page or 1,
                pageSize=paginationParams.pageSize or 20,
                totalItems=result.totalItems,
                totalPages=result.totalPages,
                sort=paginationParams.sort if paginationParams else [],
                filters=paginationParams.filters if paginationParams else None
            )
        )
    return PaginatedResponse(items=result.items, pagination=None)


@router.get("/{instanceId}/organisations/{orgId}", response_model=TrusteeOrganisation)
@limiter.limit("30/minute")
def get_organisation(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    orgId: str = Path(..., description="Organisation ID"),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeOrganisation:
    """Get a single organisation by ID."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    org = interface.getOrganisation(orgId)
    if not org:
        raise HTTPException(status_code=404, detail=f"Organisation {orgId} not found")
    return org


@router.post("/{instanceId}/organisations", response_model=TrusteeOrganisation, status_code=201)
@limiter.limit("10/minute")
def create_organisation(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    data: TrusteeOrganisation = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeOrganisation:
    """Create a new organisation."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.createOrganisation(data.model_dump())
    if not result:
        raise HTTPException(status_code=400, detail="Failed to create organisation")
    return result


@router.put("/{instanceId}/organisations/{orgId}", response_model=TrusteeOrganisation)
@limiter.limit("10/minute")
def update_organisation(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    orgId: str = Path(..., description="Organisation ID"),
    data: TrusteeOrganisation = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeOrganisation:
    """Update an organisation."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getOrganisation(orgId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Organisation {orgId} not found")

    result = interface.updateOrganisation(orgId, data.model_dump(exclude={"id"}))
    if not result:
        raise HTTPException(status_code=400, detail="Failed to update organisation")
    return result


@router.delete("/{instanceId}/organisations/{orgId}")
@limiter.limit("10/minute")
def delete_organisation(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    orgId: str = Path(..., description="Organisation ID"),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Delete an organisation."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getOrganisation(orgId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Organisation {orgId} not found")

    success = interface.deleteOrganisation(orgId)
    if not success:
        raise HTTPException(status_code=400, detail="Failed to delete organisation")
    return {"message": f"Organisation {orgId} deleted"}


# ===== Role Routes =====

@router.get("/{instanceId}/roles", response_model=PaginatedResponse[TrusteeRole])
@limiter.limit("30/minute")
def get_roles(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    pagination: Optional[str] = Query(None),
    context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteeRole]:
    """Get all roles with optional pagination."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    paginationParams = _parsePagination(pagination)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.getAllRoles(paginationParams)

    if paginationParams:
        return PaginatedResponse(
            items=result.items,
            pagination=PaginationMetadata(
                currentPage=paginationParams.page or 1,
                pageSize=paginationParams.pageSize or 20,
                totalItems=result.totalItems,
                totalPages=result.totalPages,
                sort=paginationParams.sort if paginationParams else [],
                filters=paginationParams.filters if paginationParams else None
            )
        )
    return PaginatedResponse(items=result.items, pagination=None)


@router.get("/{instanceId}/roles/{roleId}", response_model=TrusteeRole)
@limiter.limit("30/minute")
def get_role(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    roleId: str = Path(..., description="Role ID"),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeRole:
    """Get a single role by ID."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    role = interface.getRole(roleId)
    if not role:
        raise HTTPException(status_code=404, detail=f"Role {roleId} not found")
    return role


@router.post("/{instanceId}/roles", response_model=TrusteeRole, status_code=201)
@limiter.limit("10/minute")
def create_role(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    data: TrusteeRole = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeRole:
    """Create a new role (sysadmin only)."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.createRole(data.model_dump())
    if not result:
        raise HTTPException(status_code=400, detail="Failed to create role")
    return result


@router.put("/{instanceId}/roles/{roleId}", response_model=TrusteeRole)
@limiter.limit("10/minute")
def update_role(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    roleId: str = Path(...),
    data: TrusteeRole = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeRole:
    """Update a role (sysadmin only)."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getRole(roleId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Role {roleId} not found")

    result = interface.updateRole(roleId, data.model_dump(exclude={"id"}))
    if not result:
        raise HTTPException(status_code=400, detail="Failed to update role")
    return result


@router.delete("/{instanceId}/roles/{roleId}")
@limiter.limit("10/minute")
def delete_role(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    roleId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Delete a role (sysadmin only, fails if in use)."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getRole(roleId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Role {roleId} not found")

    success = interface.deleteRole(roleId)
    if not success:
        raise HTTPException(status_code=400, detail="Failed to delete role (may be in use)")
    return {"message": f"Role {roleId} deleted"}


# ===== Access Routes =====

@router.get("/{instanceId}/access", response_model=PaginatedResponse[TrusteeAccess])
@limiter.limit("30/minute")
def get_all_access(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    pagination: Optional[str] = Query(None),
    context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteeAccess]:
    """Get all access records with optional pagination."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    paginationParams = _parsePagination(pagination)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.getAllAccess(paginationParams)

    if paginationParams:
        return PaginatedResponse(
            items=result.items,
            pagination=PaginationMetadata(
                currentPage=paginationParams.page or 1,
                pageSize=paginationParams.pageSize or 20,
                totalItems=result.totalItems,
                totalPages=result.totalPages,
                sort=paginationParams.sort if paginationParams else [],
                filters=paginationParams.filters if paginationParams else None
            )
        )
    return PaginatedResponse(items=result.items, pagination=None)


@router.get("/{instanceId}/access/{accessId}", response_model=TrusteeAccess)
@limiter.limit("30/minute")
def get_access(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    accessId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeAccess:
    """Get a single access record by ID."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    access = interface.getAccess(accessId)
    if not access:
        raise HTTPException(status_code=404, detail=f"Access {accessId} not found")
    return access


@router.get("/{instanceId}/access/organisation/{orgId}", response_model=List[TrusteeAccess])
@limiter.limit("30/minute")
def get_access_by_organisation(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    orgId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> List[TrusteeAccess]:
    """Get all access records for an organisation."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    return interface.getAccessByOrganisation(orgId)


@router.get("/{instanceId}/access/user/{userId}", response_model=List[TrusteeAccess])
@limiter.limit("30/minute")
def get_access_by_user(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    userId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> List[TrusteeAccess]:
    """Get all access records for a user."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    return interface.getAccessByUser(userId)


@router.post("/{instanceId}/access", response_model=TrusteeAccess, status_code=201)
@limiter.limit("10/minute")
def create_access(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    data: TrusteeAccess = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeAccess:
    """Create a new access record."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.createAccess(data.model_dump())
    if not result:
        raise HTTPException(status_code=400, detail="Failed to create access")
    return result


@router.put("/{instanceId}/access/{accessId}", response_model=TrusteeAccess)
@limiter.limit("10/minute")
def update_access(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    accessId: str = Path(...),
    data: TrusteeAccess = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeAccess:
    """Update an access record."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getAccess(accessId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Access {accessId} not found")

    result = interface.updateAccess(accessId, data.model_dump(exclude={"id"}))
    if not result:
        raise HTTPException(status_code=400, detail="Failed to update access")
    return result


@router.delete("/{instanceId}/access/{accessId}")
@limiter.limit("10/minute")
def delete_access(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    accessId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Delete an access record."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getAccess(accessId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Access {accessId} not found")

    success = interface.deleteAccess(accessId)
    if not success:
        raise HTTPException(status_code=400, detail="Failed to delete access")
    return {"message": f"Access {accessId} deleted"}


# ===== Contract Routes =====

@router.get("/{instanceId}/contracts", response_model=PaginatedResponse[TrusteeContract])
@limiter.limit("30/minute")
def get_contracts(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    pagination: Optional[str] = Query(None),
    context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteeContract]:
    """Get all contracts with optional pagination."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    paginationParams = _parsePagination(pagination)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.getAllContracts(paginationParams)

    if paginationParams:
        return PaginatedResponse(
            items=result.items,
            pagination=PaginationMetadata(
                currentPage=paginationParams.page or 1,
                pageSize=paginationParams.pageSize or 20,
                totalItems=result.totalItems,
                totalPages=result.totalPages,
                sort=paginationParams.sort if paginationParams else [],
                filters=paginationParams.filters if paginationParams else None
            )
        )
    return PaginatedResponse(items=result.items, pagination=None)


@router.get("/{instanceId}/contracts/{contractId}", response_model=TrusteeContract)
@limiter.limit("30/minute")
def get_contract(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    contractId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeContract:
    """Get a single contract by ID."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    contract = interface.getContract(contractId)
    if not contract:
        raise HTTPException(status_code=404, detail=f"Contract {contractId} not found")
    return contract


@router.get("/{instanceId}/contracts/organisation/{orgId}", response_model=List[TrusteeContract])
@limiter.limit("30/minute")
def get_contracts_by_organisation(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    orgId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> List[TrusteeContract]:
    """Get all contracts for an organisation."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    return interface.getContractsByOrganisation(orgId)


@router.post("/{instanceId}/contracts", response_model=TrusteeContract, status_code=201)
@limiter.limit("10/minute")
def create_contract(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    data: TrusteeContract = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeContract:
    """Create a new contract."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.createContract(data.model_dump())
    if not result:
        raise HTTPException(status_code=400, detail="Failed to create contract")
    return result


@router.put("/{instanceId}/contracts/{contractId}", response_model=TrusteeContract)
@limiter.limit("10/minute")
def update_contract(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    contractId: str = Path(...),
    data: TrusteeContract = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeContract:
    """Update a contract (organisationId is immutable)."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getContract(contractId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Contract {contractId} not found")

    result = interface.updateContract(contractId, data.model_dump(exclude={"id"}))
    if not result:
        raise HTTPException(status_code=400, detail="Failed to update contract (organisationId cannot be changed)")
    return result


@router.delete("/{instanceId}/contracts/{contractId}")
@limiter.limit("10/minute")
def delete_contract(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    contractId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Delete a contract."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getContract(contractId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Contract {contractId} not found")

    success = interface.deleteContract(contractId)
    if not success:
        raise HTTPException(status_code=400, detail="Failed to delete contract")
    return {"message": f"Contract {contractId} deleted"}


# ===== Document Routes =====

@router.get("/{instanceId}/documents", response_model=PaginatedResponse[TrusteeDocument])
@limiter.limit("30/minute")
def get_documents(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    pagination: Optional[str] = Query(None),
    context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteeDocument]:
    """Get all documents (metadata only) with optional pagination."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    paginationParams = _parsePagination(pagination)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.getAllDocuments(paginationParams)

    if paginationParams:
        return PaginatedResponse(
            items=result.items,
            pagination=PaginationMetadata(
                currentPage=paginationParams.page or 1,
                pageSize=paginationParams.pageSize or 20,
                totalItems=result.totalItems,
                totalPages=result.totalPages,
                sort=paginationParams.sort if paginationParams else [],
                filters=paginationParams.filters if paginationParams else None
            )
        )
    return PaginatedResponse(items=result.items, pagination=None)


@router.get("/{instanceId}/documents/{documentId}", response_model=TrusteeDocument)
@limiter.limit("30/minute")
def get_document(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    documentId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeDocument:
    """Get document metadata by ID."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    doc = interface.getDocument(documentId)
    if not doc:
        raise HTTPException(status_code=404, detail=f"Document {documentId} not found")
    return doc


@router.get("/{instanceId}/documents/{documentId}/data")
@limiter.limit("10/minute")
def get_document_data(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    documentId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
):
    """Download document binary data."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    doc = interface.getDocument(documentId)
    if not doc:
        raise HTTPException(status_code=404, detail=f"Document {documentId} not found")

    data = interface.getDocumentData(documentId)
    if not data:
        raise HTTPException(status_code=404, detail="Document data not found")

    return StreamingResponse(
        io.BytesIO(data),
        media_type=doc.documentMimeType or "application/octet-stream",
        headers={"Content-Disposition": f"attachment; filename={doc.documentName or 'document'}"}
    )


@router.get("/{instanceId}/documents/contract/{contractId}", response_model=List[TrusteeDocument])
@limiter.limit("30/minute")
def get_documents_by_contract(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    contractId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> List[TrusteeDocument]:
    """Get all documents for a contract."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    return interface.getDocumentsByContract(contractId)


@router.post("/{instanceId}/documents", response_model=TrusteeDocument, status_code=201)
@limiter.limit("10/minute")
async def create_document(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeDocument:
    """Create a new document. Accepts JSON body with optional base64-encoded documentData."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    # Parse JSON body
    body = await request.json()
    
    # Handle documentData: convert base64 string to bytes if present
    if "documentData" in body and body["documentData"]:
        dataValue = body["documentData"]
        if isinstance(dataValue, str):
            # Base64-encoded data from frontend
            try:
                body["documentData"] = base64.b64decode(dataValue)
            except Exception as e:
                logger.warning(f"Failed to decode base64 documentData: {e}")
                body["documentData"] = None
        elif isinstance(dataValue, bytes):
            # Already bytes
            pass
        else:
            # Unknown format (e.g., File object serialized wrong)
            body["documentData"] = None
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.createDocument(body)
    if not result:
        raise HTTPException(status_code=400, detail="Failed to create document")
    return result


@router.post("/{instanceId}/documents/upload", response_model=TrusteeDocument, status_code=201)
@limiter.limit("10/minute")
async def upload_document(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    file: UploadFile = File(..., description="Document file"),
    documentName: str = Form(..., description="Document name"),
    documentMimeType: str = Form(default="application/octet-stream", description="MIME type"),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeDocument:
    """Upload a document with multipart/form-data."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    # Read file content
    fileContent = await file.read()
    
    # Build document data
    docData = {
        "documentName": documentName,
        "documentMimeType": documentMimeType or file.content_type or "application/octet-stream",
        "documentData": fileContent
    }
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.createDocument(docData)
    if not result:
        raise HTTPException(status_code=400, detail="Failed to create document")
    return result


@router.put("/{instanceId}/documents/{documentId}", response_model=TrusteeDocument)
@limiter.limit("10/minute")
def update_document(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    documentId: str = Path(...),
    data: TrusteeDocument = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteeDocument:
    """Update document metadata."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getDocument(documentId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Document {documentId} not found")

    result = interface.updateDocument(documentId, data.model_dump(exclude={"id"}))
    if not result:
        raise HTTPException(status_code=400, detail="Failed to update document")
    return result


@router.delete("/{instanceId}/documents/{documentId}")
@limiter.limit("10/minute")
def delete_document(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    documentId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Delete a document."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getDocument(documentId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Document {documentId} not found")

    success = interface.deleteDocument(documentId)
    if not success:
        raise HTTPException(status_code=400, detail="Failed to delete document")
    return {"message": f"Document {documentId} deleted"}


# ===== Position Routes =====

@router.get("/{instanceId}/positions", response_model=PaginatedResponse[TrusteePosition])
@limiter.limit("30/minute")
def get_positions(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    pagination: Optional[str] = Query(None),
    context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteePosition]:
    """Get all positions with optional pagination."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    paginationParams = _parsePagination(pagination)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.getAllPositions(paginationParams)

    if paginationParams:
        return PaginatedResponse(
            items=result.items,
            pagination=PaginationMetadata(
                currentPage=paginationParams.page or 1,
                pageSize=paginationParams.pageSize or 20,
                totalItems=result.totalItems,
                totalPages=result.totalPages,
                sort=paginationParams.sort if paginationParams else [],
                filters=paginationParams.filters if paginationParams else None
            )
        )
    return PaginatedResponse(items=result.items, pagination=None)


@router.get("/{instanceId}/positions/{positionId}", response_model=TrusteePosition)
@limiter.limit("30/minute")
def get_position(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    positionId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteePosition:
    """Get a single position by ID."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    position = interface.getPosition(positionId)
    if not position:
        raise HTTPException(status_code=404, detail=f"Position {positionId} not found")
    return position


@router.get("/{instanceId}/positions/contract/{contractId}", response_model=List[TrusteePosition])
@limiter.limit("30/minute")
def get_positions_by_contract(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    contractId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> List[TrusteePosition]:
    """Get all positions for a contract."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    return interface.getPositionsByContract(contractId)


@router.get("/{instanceId}/positions/organisation/{orgId}", response_model=List[TrusteePosition])
@limiter.limit("30/minute")
def get_positions_by_organisation(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    orgId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> List[TrusteePosition]:
    """Get all positions for an organisation."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    return interface.getPositionsByOrganisation(orgId)


@router.post("/{instanceId}/positions", response_model=TrusteePosition, status_code=201)
@limiter.limit("10/minute")
def create_position(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    data: TrusteePosition = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteePosition:
    """Create a new position."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.createPosition(data.model_dump())
    if not result:
        raise HTTPException(status_code=400, detail="Failed to create position")
    return result


@router.put("/{instanceId}/positions/{positionId}", response_model=TrusteePosition)
@limiter.limit("10/minute")
def update_position(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    positionId: str = Path(...),
    data: TrusteePosition = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteePosition:
    """Update a position."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getPosition(positionId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Position {positionId} not found")

    result = interface.updatePosition(positionId, data.model_dump(exclude={"id"}))
    if not result:
        raise HTTPException(status_code=400, detail="Failed to update position")
    return result


@router.delete("/{instanceId}/positions/{positionId}")
@limiter.limit("10/minute")
def delete_position(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    positionId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Delete a position."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getPosition(positionId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Position {positionId} not found")

    success = interface.deletePosition(positionId)
    if not success:
        raise HTTPException(status_code=400, detail="Failed to delete position")
    return {"message": f"Position {positionId} deleted"}


# ===== Position-Document Link Routes =====

@router.get("/{instanceId}/position-documents")
@limiter.limit("30/minute")
def get_position_documents(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    pagination: Optional[str] = Query(None),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Get all position-document links with optional pagination.
    
    Each item includes _permissions: { canUpdate, canDelete } for row-level permission UI.
    """
    mandateId = _validateInstanceAccess(instanceId, context)
    
    paginationParams = _parsePagination(pagination)
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.getAllPositionDocuments(paginationParams)

    if paginationParams:
        return {
            "items": result.items,
            "pagination": {
                "currentPage": paginationParams.page or 1,
                "pageSize": paginationParams.pageSize or 20,
                "totalItems": result.totalItems,
                "totalPages": result.totalPages,
                "sort": paginationParams.sort if paginationParams else [],
                "filters": paginationParams.filters if paginationParams else None
            }
        }
    return {"items": result.items, "pagination": None}


@router.get("/{instanceId}/position-documents/{linkId}", response_model=TrusteePositionDocument)
@limiter.limit("30/minute")
def get_position_document(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    linkId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteePositionDocument:
    """Get a single position-document link by ID."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    link = interface.getPositionDocument(linkId)
    if not link:
        raise HTTPException(status_code=404, detail=f"Link {linkId} not found")
    return link


@router.get("/{instanceId}/position-documents/position/{positionId}", response_model=List[TrusteePositionDocument])
@limiter.limit("30/minute")
def get_documents_for_position(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    positionId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> List[TrusteePositionDocument]:
    """Get all document links for a position."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    return interface.getDocumentsForPosition(positionId)


@router.get("/{instanceId}/position-documents/document/{documentId}", response_model=List[TrusteePositionDocument])
@limiter.limit("30/minute")
def get_positions_for_document(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    documentId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> List[TrusteePositionDocument]:
    """Get all position links for a document."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    return interface.getPositionsForDocument(documentId)


@router.post("/{instanceId}/position-documents", response_model=TrusteePositionDocument, status_code=201)
@limiter.limit("10/minute")
def create_position_document(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    data: TrusteePositionDocument = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteePositionDocument:
    """Create a new position-document link."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.createPositionDocument(data.model_dump())
    if not result:
        raise HTTPException(status_code=400, detail="Failed to create link")
    return result


@router.put("/{instanceId}/position-documents/{linkId}", response_model=TrusteePositionDocument)
@limiter.limit("10/minute")
def update_position_document(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    linkId: str = Path(...),
    data: TrusteePositionDocument = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> TrusteePositionDocument:
    """Update a position-document link."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    result = interface.updatePositionDocument(linkId, data.model_dump(exclude_unset=True))
    if not result:
        raise HTTPException(status_code=400, detail="Failed to update link")
    return result


@router.delete("/{instanceId}/position-documents/{linkId}")
@limiter.limit("10/minute")
def delete_position_document(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    linkId: str = Path(...),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Delete a position-document link."""
    mandateId = _validateInstanceAccess(instanceId, context)
    
    interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
    existing = interface.getPositionDocument(linkId)
    if not existing:
        raise HTTPException(status_code=404, detail=f"Link {linkId} not found")

    success = interface.deletePositionDocument(linkId)
    if not success:
        raise HTTPException(status_code=400, detail="Failed to delete link")
    return {"message": f"Link {linkId} deleted"}


# ===== Instance Roles Management =====
# These endpoints allow feature admins to manage instance-specific roles and their AccessRules

from modules.datamodels.datamodelRbac import Role, AccessRule, AccessRuleContext


def _validateInstanceAdmin(instanceId: str, context: RequestContext) -> str:
    """
    Validate that the user has admin access to the feature instance.
    Returns the mandateId if authorized.
    
    This checks for the RESOURCE permission 'instance-roles.manage'
    via AccessRules assigned to the user's current roles.
    """
    mandateId = _validateInstanceAccess(instanceId, context)
    
    # SysAdmin role always has access
    if context.hasSysAdminRole:
        return mandateId
    
    # Check for instance-roles.manage resource permission via AccessRules
    rootInterface = getRootInterface()
    hasAdminPermission = False
    
    for roleId in context.roleIds:
        rules = rootInterface.db.getRecordset(
            AccessRule,
            {"roleId": roleId, "context": AccessRuleContext.RESOURCE.value, "item": "resource.trustee.instance-roles.manage"}
        )
        if rules:
            hasAdminPermission = True
            break
    
    if not hasAdminPermission:
        raise HTTPException(
            status_code=403,
            detail="Keine Berechtigung zur Rollenverwaltung"
        )
    
    return mandateId


@router.get("/{instanceId}/instance-roles", response_model=PaginatedResponse)
@limiter.limit("30/minute")
def get_instance_roles(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse:
    """
    Get all roles for this feature instance.
    Requires feature admin permission.
    """
    mandateId = _validateInstanceAdmin(instanceId, context)
    
    rootInterface = getRootInterface()
    
    # Get instance-specific roles (Pydantic models)
    roles = rootInterface.getRolesByFeatureCode("trustee", featureInstanceId=instanceId)
    
    return PaginatedResponse(
        items=[r.model_dump() for r in roles],
        pagination=None
    )


@router.get("/{instanceId}/instance-roles/{roleId}", response_model=Dict[str, Any])
@limiter.limit("30/minute")
def get_instance_role(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    roleId: str = Path(..., description="Role ID"),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Get a specific instance role."""
    mandateId = _validateInstanceAdmin(instanceId, context)
    
    rootInterface = getRootInterface()
    role = rootInterface.getRole(roleId)
    
    if not role:
        raise HTTPException(status_code=404, detail=f"Role {roleId} not found")
    
    # Verify role belongs to this instance
    if str(role.featureInstanceId) != instanceId:
        raise HTTPException(status_code=404, detail=f"Role {roleId} not found in this instance")
    
    return role.model_dump()


@router.get("/{instanceId}/instance-roles/{roleId}/rules", response_model=PaginatedResponse)
@limiter.limit("30/minute")
def get_instance_role_rules(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    roleId: str = Path(..., description="Role ID"),
    context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse:
    """
    Get all AccessRules for a specific instance role.
    Requires feature admin permission.
    """
    mandateId = _validateInstanceAdmin(instanceId, context)
    
    rootInterface = getRootInterface()
    
    # Verify role belongs to this instance (Pydantic model)
    role = rootInterface.getRole(roleId)
    if not role or str(role.featureInstanceId) != instanceId:
        raise HTTPException(status_code=404, detail=f"Role {roleId} not found in this instance")
    
    # Get AccessRules for this role (Pydantic models)
    rules = rootInterface.getAccessRulesByRole(roleId)
    
    return PaginatedResponse(
        items=[r.model_dump() for r in rules],
        pagination=None
    )


@router.post("/{instanceId}/instance-roles/{roleId}/rules", response_model=Dict[str, Any], status_code=201)
@limiter.limit("10/minute")
def create_instance_role_rule(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    roleId: str = Path(..., description="Role ID"),
    ruleData: Dict[str, Any] = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """
    Create a new AccessRule for an instance role.
    Requires feature admin permission.
    """
    mandateId = _validateInstanceAdmin(instanceId, context)
    
    rootInterface = getRootInterface()
    
    # Verify role belongs to this instance (Pydantic model)
    role = rootInterface.getRole(roleId)
    if not role or str(role.featureInstanceId) != instanceId:
        raise HTTPException(status_code=404, detail=f"Role {roleId} not found in this instance")
    
    # Create the rule
    try:
        contextStr = ruleData.get("context", "UI")
        if isinstance(contextStr, str):
            contextEnum = AccessRuleContext(contextStr.upper())
        else:
            contextEnum = contextStr
        
        newRule = AccessRule(
            roleId=roleId,
            context=contextEnum,
            item=ruleData.get("item"),
            view=ruleData.get("view", False),
            read=ruleData.get("read"),
            create=ruleData.get("create"),
            update=ruleData.get("update"),
            delete=ruleData.get("delete"),
        )
        
        created = rootInterface.db.recordCreate(AccessRule, newRule.model_dump())
        return created
        
    except Exception as e:
        logger.error(f"Error creating AccessRule: {e}")
        raise HTTPException(status_code=400, detail=f"Failed to create rule: {str(e)}")


@router.put("/{instanceId}/instance-roles/{roleId}/rules/{ruleId}", response_model=Dict[str, Any])
@limiter.limit("10/minute")
def update_instance_role_rule(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    roleId: str = Path(..., description="Role ID"),
    ruleId: str = Path(..., description="Rule ID"),
    ruleData: Dict[str, Any] = Body(...),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """
    Update an AccessRule for an instance role.
    Only view, read, create, update, delete can be changed.
    Requires feature admin permission.
    """
    mandateId = _validateInstanceAdmin(instanceId, context)
    
    rootInterface = getRootInterface()
    
    # Verify role belongs to this instance (Pydantic model)
    role = rootInterface.getRole(roleId)
    if not role or str(role.featureInstanceId) != instanceId:
        raise HTTPException(status_code=404, detail=f"Role {roleId} not found in this instance")
    
    # Verify rule belongs to role (Pydantic model)
    existingRule = rootInterface.getAccessRule(ruleId)
    if not existingRule or str(existingRule.roleId) != roleId:
        raise HTTPException(status_code=404, detail=f"Rule {ruleId} not found for this role")
    
    # Update only allowed fields
    updateData = {}
    if "view" in ruleData:
        updateData["view"] = ruleData["view"]
    if "read" in ruleData:
        updateData["read"] = ruleData["read"]
    if "create" in ruleData:
        updateData["create"] = ruleData["create"]
    if "update" in ruleData:
        updateData["update"] = ruleData["update"]
    if "delete" in ruleData:
        updateData["delete"] = ruleData["delete"]
    
    if not updateData:
        return existingRule.model_dump()
    
    try:
        updated = rootInterface.db.recordModify(AccessRule, ruleId, updateData)
        return updated
    except Exception as e:
        logger.error(f"Error updating AccessRule: {e}")
        raise HTTPException(status_code=400, detail=f"Failed to update rule: {str(e)}")


@router.delete("/{instanceId}/instance-roles/{roleId}/rules/{ruleId}")
@limiter.limit("10/minute")
def delete_instance_role_rule(
    request: Request,
    instanceId: str = Path(..., description="Feature Instance ID"),
    roleId: str = Path(..., description="Role ID"),
    ruleId: str = Path(..., description="Rule ID"),
    context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """
    Delete an AccessRule for an instance role.
    Requires feature admin permission.
    """
    mandateId = _validateInstanceAdmin(instanceId, context)
    
    rootInterface = getRootInterface()
    
    # Verify role belongs to this instance (Pydantic model)
    role = rootInterface.getRole(roleId)
    if not role or str(role.featureInstanceId) != instanceId:
        raise HTTPException(status_code=404, detail=f"Role {roleId} not found in this instance")
    
    # Verify rule belongs to role (Pydantic model)
    existingRule = rootInterface.getAccessRule(ruleId)
    if not existingRule or str(existingRule.roleId) != roleId:
        raise HTTPException(status_code=404, detail=f"Rule {ruleId} not found for this role")
    
    try:
        rootInterface.db.recordDelete(AccessRule, ruleId)
        return {"message": f"Rule {ruleId} deleted"}
    except Exception as e:
        logger.error(f"Error deleting AccessRule: {e}")
        raise HTTPException(status_code=400, detail=f"Failed to delete rule: {str(e)}")
