package il.co.organicai.agent

import org.json.JSONArray
import org.json.JSONObject
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit

/**
 * The app's link to the OrganicAI Cloudflare Worker. Speaks the SAME device
 * protocol the Chrome extension uses, so the phone shows up as just another agent
 * in the fleet (ADR-44) and the AI drives it with the same MCP browser.* tools.
 *
 * Auth is the 2FA flow (ADR-47): password -> emailed code -> session token.
 */
class WorkerClient(
    private val endpoint: String = "https://fb-poster-sync.idandada.workers.dev",
    var token: String? = null,
) {
    private val http = OkHttpClient.Builder()
        .callTimeout(30, TimeUnit.SECONDS)
        .build()
    private val JSON = "application/json; charset=utf-8".toMediaType()

    private fun post(path: String, body: JSONObject, auth: Boolean): JSONObject {
        val b = Request.Builder()
            .url("$endpoint$path")
            .post(body.toString().toByteArray(Charsets.UTF_8).toRequestBody(JSON))
        if (auth) token?.let { b.header("X-Session-Token", it) }
        http.newCall(b.build()).execute().use { r ->
            val text = r.body?.string() ?: "{}"
            return runCatching { JSONObject(text) }.getOrElse { JSONObject() }
        }
    }

    // --- 2FA login ---------------------------------------------------------
    /** Factor 1. Returns true if a code was emailed (caller then collects it). */
    fun loginPassword(identifier: String, password: String): Boolean {
        val r = post("/login", JSONObject().put("email", identifier).put("password", password), auth = false)
        return r.optBoolean("otpRequired", false)
    }

    /** Factor 2. On success stores the session token and returns true. */
    fun verifyOtp(identifier: String, code: String): Boolean {
        val r = post("/auth/otp/verify", JSONObject().put("identifier", identifier).put("code", code), auth = false)
        if (r.optBoolean("ok") && r.has("token")) { token = r.getString("token"); return true }
        return false
    }

    // --- Heartbeat + command poll -----------------------------------------
    /**
     * One heartbeat. Registers/refreshes this agent (platform + capabilities per
     * ADR-44) and returns any pending commands the worker handed back.
     */
    fun heartbeat(): JSONArray {
        val caps = JSONArray(listOf("browser", "screenshot", "publish", "facebook", "scan"))
        val body = JSONObject()
            .put("version", "0.1.0")
            .put("platform", "android-app")
            .put("capabilities", caps)
        val r = post("/devices/heartbeat", body, auth = true)
        return r.optJSONArray("commands") ?: JSONArray()
    }

    /** Report a command's outcome. Status contract: ok -> 'completed', else 'failed'. */
    fun reportResult(commandId: String, ok: Boolean, result: JSONObject?, error: String?) {
        val body = JSONObject().put("ok", ok)
        if (result != null) body.put("result", result)
        if (error != null) body.put("error", error)
        post("/devices/commands/$commandId/result", body, auth = true)
    }

    /** Upload a screenshot data URL -> R2, returns the hosted URL (or null). */
    fun uploadScreenshot(dataUrl: String): String? {
        val r = post("/api/v1/screenshots", JSONObject().put("dataUrl", dataUrl), auth = true)
        return if (r.optBoolean("ok")) r.optStringOrNull("url") else null
    }
}
