package com.centralcloud.oncall.sms

import android.content.Context
import android.util.Log
import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.NetworkType
import androidx.work.WorkerParameters
import com.centralcloud.oncall.util.HttpUtil
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.TimeZone

/**
 * Forwards a single SMS to centralcloud-ops:
 *
 *   POST {baseUrl}/api/sms/inbound/phone-relay
 *   X-Device-Id: <device_id>
 *   Content-Type: application/json
 *   {
 *     "device_id":  "<token>",
 *     "from":        "<sender>",
 *     "body":        "<text>",
 *     "received_at": "<ISO-8601>",
 *     "client_message_id": "<uuid>"
 *   }
 *
 * Retries via WorkManager's exponential backoff on transient failures
 * (network, 5xx). Drops permanently on 4xx (server rejected it; retrying
 * would just loop).
 */
class SmsForwardWorker(appContext: Context, params: WorkerParameters) :
    CoroutineWorker(appContext, params) {

    override suspend fun doWork(): Result {
        val from = inputData.getString(KEY_FROM) ?: return Result.failure()
        val body = inputData.getString(KEY_BODY) ?: return Result.failure()
        val receivedAtMs = inputData.getLong(KEY_RECEIVED_AT_MS, 0L)
        val clientMessageId = inputData.getString(KEY_CLIENT_MESSAGE_ID) ?: return Result.failure()

        val prefs = SmsRelayPreferences(applicationContext)
        if (!prefs.enabled || prefs.baseUrl.isBlank() || prefs.deviceId.isBlank()) {
            Log.w(TAG, "relay disabled or unconfigured; dropping cid=$clientMessageId")
            return Result.failure()
        }

        return try {
            val payload = JSONObject().apply {
                put("device_id", prefs.deviceId)
                put("from", from)
                put("body", body)
                put("received_at", iso8601(receivedAtMs))
                put("client_message_id", clientMessageId)
            }.toString()

            val url = "${prefs.baseUrl}/api/sms/inbound/phone-relay"
            val client = HttpUtil.defaultClient(applicationContext, prefs.baseUrl)
            val request = HttpUtil.requestBuilder(url)
                .addHeader("X-Device-Id", prefs.deviceId)
                .post(payload.toRequestBody(JSON))
                .build()

            client.newCall(request).execute().use { response ->
                when {
                    response.isSuccessful -> {
                        prefs.lastForwardAtMs = System.currentTimeMillis()
                        Log.d(TAG, "forwarded cid=$clientMessageId code=${response.code}")
                        Result.success()
                    }

                    response.code in 400..499 -> {
                        // Permanent: bad device_id, bad payload, etc. Don't retry.
                        prefs.bumpFailureCount()
                        Log.w(TAG, "permanent reject cid=$clientMessageId code=${response.code}")
                        Result.failure()
                    }

                    else -> {
                        prefs.bumpFailureCount()
                        Log.w(TAG, "transient fail cid=$clientMessageId code=${response.code}")
                        Result.retry()
                    }
                }
            }
        } catch (t: Throwable) {
            prefs.bumpFailureCount()
            Log.w(TAG, "forward exception cid=$clientMessageId", t)
            Result.retry()
        }
    }

    private fun iso8601(ms: Long): String {
        val df = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
        df.timeZone = TimeZone.getTimeZone("UTC")
        return df.format(Date(if (ms > 0L) ms else System.currentTimeMillis()))
    }

    companion object {
        private const val TAG = "SmsForwardWorker"
        const val KEY_FROM = "from"
        const val KEY_BODY = "body"
        const val KEY_RECEIVED_AT_MS = "received_at_ms"
        const val KEY_CLIENT_MESSAGE_ID = "client_message_id"

        const val INITIAL_BACKOFF_SECONDS = 30L

        private val JSON = "application/json; charset=utf-8".toMediaType()

        fun connectedConstraints(): Constraints =
            Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build()
    }
}
