from typing import Annotated, Any, cast from anydi import Inject from fansifter_common.api.schemas import response_errors from fansifter_common.identifiers.types import ProfileToken from fastapi import APIRouter, Header, Path from starlette import status from preference_center.api import schemas from preference_center.profile.constants import ( ORIGIN_TYPE_AUTOMATED_EMAIL, ORIGIN_TYPE_EMAIL_CAMPAIGN, ) from preference_center.profile.exceptions import ( ProfileBirthdayUpdateNotAllowedError, ProfileDateOfBirthUpdateNotAllowedError, ProfileNotFoundError, ) from preference_center.profile.handlers import ( DeleteProfileHandler, DeleteProfileRequest, GetProfileHandler, GetProfileRequest, GetSettingsHandler, GetSettingsRequest, GetSubscriptionsHandler, GetSubscriptionsRequest, UnsubscribeAllByEmailHandler, UnsubscribeAllByEmailRequest, UnsubscribeAllHandler, UnsubscribeAllRequest, UpdateProfileHandler, UpdateProfileRequest, UpdateSubscriptionsHandler, UpdateSubscriptionsRequest, ) from preference_center.profile.types import ProfileValues IPCountryHeader = Annotated[str, Header(alias="X-IPCountry")] ProfileTokenPath = Annotated[ProfileToken, Path(description="Profile token")] router = APIRouter() @router.get( "/hello/", operation_id="getHello", tags=["infra"], include_in_schema=False, response_model=schemas.HelloResponse, ) async def hello() -> dict[str, Any]: return {"status": "ok"} @router.get( "/settings", operation_id="getSettings", description="Get settings", tags=["settings"], response_model=schemas.Settings, ) def get_settings( ip_country: IPCountryHeader, handler: Annotated[GetSettingsHandler, Inject()], ) -> Any: return handler.handle(GetSettingsRequest(ip_country=ip_country)) @router.get( "/profile/{token}", operation_id="getProfile", description="Get a profile", tags=["profile"], response_model=schemas.Profile, responses=response_errors( ProfileNotFoundError, ), ) def get_profile( ip_country: IPCountryHeader, token: ProfileTokenPath, handler: Annotated[GetProfileHandler, Inject()], ) -> Any: return handler.handle( GetProfileRequest(ip_country=ip_country, token=token), ) @router.patch( "/profile/{token}", operation_id="updateProfile", description="Update a profile", tags=["profile"], response_model=schemas.Profile, responses=response_errors( ProfileNotFoundError, ProfileBirthdayUpdateNotAllowedError, ProfileDateOfBirthUpdateNotAllowedError, ), ) def update_profile( ip_country: IPCountryHeader, token: ProfileTokenPath, data: schemas.UpdateProfileInput, handler: Annotated[UpdateProfileHandler, Inject()], ) -> Any: return handler.handle( UpdateProfileRequest( ip_country=ip_country, token=token, values=cast( ProfileValues, data.model_dump(exclude_unset=True), ), ) ) @router.delete( "/profile/{token}", operation_id="deleteProfile", description="Delete a profile", tags=["profile"], responses=response_errors( ProfileNotFoundError, ), status_code=status.HTTP_204_NO_CONTENT, ) def delete_profile( token: ProfileTokenPath, handler: Annotated[DeleteProfileHandler, Inject()], ) -> None: return handler.handle( DeleteProfileRequest(token=token), ) @router.get( "/profile/{token}/subscriptions", operation_id="getSubscriptions", description="Get a profile subscriptions", tags=["subscriptions"], response_model=list[schemas.Subscription], responses=response_errors( ProfileNotFoundError, ), ) def get_subscriptions( token: ProfileTokenPath, handler: Annotated[GetSubscriptionsHandler, Inject()], ) -> Any: return handler.handle(GetSubscriptionsRequest(token=token)) @router.put( "/profile/{token}/subscriptions", operation_id="updateSubscriptions", description="Update a profile subscriptions", tags=["subscriptions"], response_model=list[schemas.Subscription], responses=response_errors( ProfileNotFoundError, ), ) def update_subscriptions( token: ProfileTokenPath, data: schemas.UpdateSubscriptionsInput, handler: Annotated[UpdateSubscriptionsHandler, Inject()], ) -> Any: return handler.handle( UpdateSubscriptionsRequest( token=token, data={item.id: item.is_active for item in data.data}, ) ) @router.delete( "/profile/{token}/subscriptions", operation_id="unsubscribeAll", description="Unsubscribe all subscriptions", tags=["subscriptions"], responses=response_errors( ProfileNotFoundError, ), status_code=status.HTTP_204_NO_CONTENT, ) def unsubscribe_all( token: ProfileTokenPath, handler: Annotated[UnsubscribeAllHandler, Inject()], ) -> None: return handler.handle(UnsubscribeAllRequest(token=token)) @router.post( "/subscriptions/unsubscribe-all", operation_id="unsubscribeAllByEmail", description="Unsubscribe all subscriptions by email", tags=["subscriptions"], responses=response_errors( ProfileNotFoundError, ), status_code=status.HTTP_204_NO_CONTENT, ) def unsubscribe_all_by_email( data: schemas.UnsubscribeAllByEmailInput, handler: Annotated[UnsubscribeAllByEmailHandler, Inject()], ) -> None: if data.email_campaign_id is not None: origin_id: str | None = data.email_campaign_id origin_type: str | None = ORIGIN_TYPE_EMAIL_CAMPAIGN automated_email_trigger_id: str | None = None else: origin_id = data.origin_id origin_type = data.origin_type automated_email_trigger_id = data.automated_email_trigger_id if origin_type != ORIGIN_TYPE_AUTOMATED_EMAIL: automated_email_trigger_id = None return handler.handle( UnsubscribeAllByEmailRequest( email=data.email, origin_id=origin_id, origin_type=origin_type, automated_email_trigger_id=automated_email_trigger_id, ) )