package com.example.data.meta import com.example.BuildConfig import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Response import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.Header import retrofit2.http.POST import retrofit2.http.Path import retrofit2.http.Query import java.util.concurrent.TimeUnit object MetaConfig { const val BASE_URL = "https://graph.facebook.com/v21.0/" // Injected credentials with fallback to user's provided Meta API credentials val PHONE_NUMBER_ID: String = try { BuildConfig.META_PHONE_NUMBER_ID.ifEmpty { "913349665192073" } } catch (_: Throwable) { "913349665192073" } val WABA_ID: String = try { BuildConfig.META_WABA_ID.ifEmpty { "1378746380406848" } } catch (_: Throwable) { "1378746380406848" } val ACCESS_TOKEN: String = try { BuildConfig.META_ACCESS_TOKEN.ifEmpty { "EAAl6rWdth7YBSvrGsUqF53H26vetTv1BmfGssD9wZAFW9XkTcJIawKkKEdJDD6GSgqKhGlfOceMiZCR4ilLtszYanJhMRZBdrj9fyXcZAzvMHupNGD5VEyzAgwSs649OBBtYSLp5HVFU4no0ZBEfnm5c9cLepoZCZByZAS6yVpl9CUXJlzdU8kdxVlg4VgAK4C745C7Q5nQKn7zLF65h8O7jVZCIuu2yM2IGTIMJhn40gZC6mzfRGfmnOjNr3IJ3JeuzgroDsZD" } } catch (_: Throwable) { "EAAl6rWdth7YBSvrGsUqF53H26vetTv1BmfGssD9wZAFW9XkTcJIawKkKEdJDD6GSgqKhGlfOceMiZCR4ilLtszYanJhMRZBdrj9fyXcZAzvMHupNGD5VEyzAgwSs649OBBtYSLp5HVFU4no0ZBEfnm5c9cLepoZCZByZAS6yVpl9CUXJlzdU8kdxVlg4VgAK4C745C7Q5nQKn7zLF65h8O7jVZCIuu2yM2IGTIMJhn40gZC6mzfRGfmnOjNr3IJ3JeuzgroDsZD" } } @JsonClass(generateAdapter = true) data class MetaSendMessageRequest( @Json(name = "messaging_product") val messagingProduct: String = "whatsapp", @Json(name = "recipient_type") val recipientType: String = "individual", @Json(name = "to") val to: String, @Json(name = "type") val type: String = "template", @Json(name = "template") val template: MetaTemplatePayload? = null, @Json(name = "text") val text: MetaTextPayload? = null ) @JsonClass(generateAdapter = true) data class MetaTemplatePayload( @Json(name = "name") val name: String, @Json(name = "language") val language: MetaLanguage, @Json(name = "components") val components: List? = null ) @JsonClass(generateAdapter = true) data class MetaLanguage( @Json(name = "code") val code: String = "en_US" ) @JsonClass(generateAdapter = true) data class MetaComponent( @Json(name = "type") val type: String, // header, body, button @Json(name = "sub_type") val subType: String? = null, @Json(name = "index") val index: String? = null, @Json(name = "parameters") val parameters: List? = null ) @JsonClass(generateAdapter = true) data class MetaParameter( @Json(name = "type") val type: String, // text, image, video, document @Json(name = "text") val text: String? = null, @Json(name = "image") val image: MetaMediaLink? = null, @Json(name = "video") val video: MetaMediaLink? = null, @Json(name = "document") val document: MetaMediaLink? = null ) @JsonClass(generateAdapter = true) data class MetaMediaLink( @Json(name = "link") val link: String ) @JsonClass(generateAdapter = true) data class MetaTextPayload( @Json(name = "preview_url") val previewUrl: Boolean = true, @Json(name = "body") val body: String ) @JsonClass(generateAdapter = true) data class MetaSendMessageResponse( @Json(name = "messaging_product") val messagingProduct: String? = null, @Json(name = "contacts") val contacts: List? = null, @Json(name = "messages") val messages: List? = null, @Json(name = "error") val error: MetaApiError? = null ) @JsonClass(generateAdapter = true) data class MetaContactInfo( @Json(name = "input") val input: String?, @Json(name = "wa_id") val waId: String? ) @JsonClass(generateAdapter = true) data class MetaMessageId( @Json(name = "id") val id: String ) @JsonClass(generateAdapter = true) data class MetaPhoneNumberDetailsResponse( @Json(name = "id") val id: String?, @Json(name = "verified_name") val verifiedName: String?, @Json(name = "display_phone_number") val displayPhoneNumber: String?, @Json(name = "quality_rating") val qualityRating: String?, @Json(name = "code_verification_status") val codeVerificationStatus: String?, @Json(name = "error") val error: MetaApiError? = null ) @JsonClass(generateAdapter = true) data class MetaWabaDetailsResponse( @Json(name = "id") val id: String?, @Json(name = "name") val name: String?, @Json(name = "timezone_id") val timezoneId: String?, @Json(name = "message_template_namespace") val messageTemplateNamespace: String?, @Json(name = "error") val error: MetaApiError? = null ) @JsonClass(generateAdapter = true) data class MetaTemplatesResponse( @Json(name = "data") val data: List? = null, @Json(name = "error") val error: MetaApiError? = null ) @JsonClass(generateAdapter = true) data class MetaRemoteTemplateItem( @Json(name = "id") val id: String?, @Json(name = "name") val name: String?, @Json(name = "status") val status: String?, @Json(name = "category") val category: String?, @Json(name = "language") val language: String? ) @JsonClass(generateAdapter = true) data class MetaApiError( @Json(name = "message") val message: String?, @Json(name = "type") val type: String?, @Json(name = "code") val code: Int?, @Json(name = "error_subcode") val errorSubcode: Int? ) interface MetaCloudApiService { /** * Sends an authenticated WhatsApp message (Template, Text, Media, Interactive) * through Meta's Cloud API endpoint. */ @POST("{phoneNumberId}/messages") suspend fun sendMessage( @Path("phoneNumberId") phoneNumberId: String, @Header("Authorization") bearerToken: String, @Body request: MetaSendMessageRequest ): Response /** * Fetches registered Phone Number status & Quality Rating from Meta */ @GET("{phoneNumberId}") suspend fun getPhoneNumberDetails( @Path("phoneNumberId") phoneNumberId: String, @Header("Authorization") bearerToken: String ): Response /** * Fetches WhatsApp Business Account (WABA) details from Meta */ @GET("{wabaId}") suspend fun getWabaDetails( @Path("wabaId") wabaId: String, @Header("Authorization") bearerToken: String ): Response /** * Lists approved message templates for the WABA */ @GET("{wabaId}/message_templates") suspend fun getTemplates( @Path("wabaId") wabaId: String, @Header("Authorization") bearerToken: String, @Query("limit") limit: Int = 50 ): Response companion object { /** * Creates an authenticated Retrofit API client for Meta WhatsApp Cloud API. * If [defaultBearerToken] is supplied, an interceptor automatically adds * the Authorization header to all requests. */ fun create( baseUrl: String = MetaConfig.BASE_URL, defaultBearerToken: String = MetaConfig.ACCESS_TOKEN ): MetaCloudApiService { val logging = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } val authInterceptor = Interceptor { chain -> val original = chain.request() val requestBuilder = original.newBuilder() // Inject Bearer token if not already explicitly present if (original.header("Authorization") == null && defaultBearerToken.isNotBlank()) { val tokenHeader = if (defaultBearerToken.startsWith("Bearer ")) { defaultBearerToken } else { "Bearer $defaultBearerToken" } requestBuilder.header("Authorization", tokenHeader) } chain.proceed(requestBuilder.build()) } val client = OkHttpClient.Builder() .addInterceptor(authInterceptor) .addInterceptor(logging) .connectTimeout(20, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) .writeTimeout(20, TimeUnit.SECONDS) .build() return Retrofit.Builder() .baseUrl(baseUrl) .client(client) .addConverterFactory(MoshiConverterFactory.create()) .build() .create(MetaCloudApiService::class.java) } } }