package ai.openclaw.app.tools

import android.content.Context
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.contentOrNull

@Serializable
private data class ToolDisplayActionSpec(
  val label: String? = null,
  val detailKeys: List<String>? = null,
)

@Serializable
private data class ToolDisplaySpec(
  val emoji: String? = null,
  val title: String? = null,
  val label: String? = null,
  val detailKeys: List<String>? = null,
  val actions: Map<String, ToolDisplayActionSpec>? = null,
)

@Serializable
private data class ToolDisplayConfig(
  val version: Int? = null,
  val fallback: ToolDisplaySpec? = null,
  val tools: Map<String, ToolDisplaySpec>? = null,
)

data class ToolDisplaySummary(
  val name: String,
  val emoji: String,
  val title: String,
  val label: String,
  val verb: String?,
  val detail: String?,
) {
  val detailLine: String?
    get() {
      val parts = mutableListOf<String>()
      if (!verb.isNullOrBlank()) parts.add(verb)
      if (!detail.isNullOrBlank()) parts.add(detail)
      return if (parts.isEmpty()) null else parts.joinToString(" · ")
    }

  val summaryLine: String
    get() = if (detailLine != null) "$emoji $label: $detailLine" else "$emoji $label"
}

object ToolDisplayRegistry {
  private const val CONFIG_ASSET = "tool-display.json"

  private val json = Json { ignoreUnknownKeys = true }

  @Volatile private var cachedConfig: ToolDisplayConfig? = null

  fun resolve(
    context: Context,
    name: String?,
    args: JsonObject?,
    meta: String? = null,
  ): ToolDisplaySummary {
    val trimmedName = name?.trim().orEmpty().ifEmpty { "tool" }
    val key = trimmedName.lowercase()
    val config = loadConfig(context)
    val spec = config.tools?.get(key)
    val fallback = config.fallback

    val emoji = spec?.emoji ?: fallback?.emoji ?: "🧩"
    val title = spec?.title ?: titleFromName(trimmedName)
    val label = spec?.label ?: trimmedName

    val actionRaw = args?.get("action")?.asStringOrNull()?.trim()
    val action = actionRaw?.takeIf { it.isNotEmpty() }
    val actionSpec = action?.let { spec?.actions?.get(it) }
    val verb = normalizeVerb(actionSpec?.label ?: action)

    var detail: String? = null
    if (key == "read") {
      detail = readDetail(args)
    } else if (key == "write" || key == "edit" || key == "attach") {
      detail = pathDetail(args)
    }

    val detailKeys = actionSpec?.detailKeys ?: spec?.detailKeys ?: fallback?.detailKeys ?: emptyList()
    if (detail == null) {
      detail = firstValue(args, detailKeys)
    }

    if (detail == null) {
      detail = meta
    }

    if (detail != null) {
      detail = shortenHomeInString(detail)
    }

    return ToolDisplaySummary(
      name = trimmedName,
      emoji = emoji,
      title = title,
      label = label,
      verb = verb,
      detail = detail,
    )
  }

  private fun loadConfig(context: Context): ToolDisplayConfig {
    val existing = cachedConfig
    if (existing != null) return existing
    return try {
      val jsonString =
        context.assets
          .open(CONFIG_ASSET)
          .bufferedReader()
          .use { it.readText() }
      val decoded = json.decodeFromString(ToolDisplayConfig.serializer(), jsonString)
      cachedConfig = decoded
      decoded
    } catch (_: Throwable) {
      val fallback = ToolDisplayConfig()
      cachedConfig = fallback
      fallback
    }
  }

  private fun titleFromName(name: String): String {
    val cleaned = name.replace("_", " ").trim()
    if (cleaned.isEmpty()) return "Tool"
    return cleaned
      .split(Regex("\\s+"))
      .joinToString(" ") { part ->
        val upper = part.uppercase()
        if (part.length <= 2 && part == upper) {
          part
        } else {
          upper.firstOrNull()?.toString().orEmpty() + part.lowercase().drop(1)
        }
      }
  }

  private fun normalizeVerb(value: String?): String? {
    val trimmed = value?.trim().orEmpty()
    if (trimmed.isEmpty()) return null
    return trimmed.replace("_", " ")
  }

  private fun readDetail(args: JsonObject?): String? {
    val path = args?.get("path")?.asStringOrNull() ?: return null
    val offset = args["offset"].asNumberOrNull()
    val limit = args["limit"].asNumberOrNull()
    return if (offset != null && limit != null) {
      val end = offset + limit
      "$path:${offset.toInt()}-${end.toInt()}"
    } else {
      path
    }
  }

  private fun pathDetail(args: JsonObject?): String? = args?.get("path")?.asStringOrNull()

  private fun firstValue(
    args: JsonObject?,
    keys: List<String>,
  ): String? {
    for (key in keys) {
      val value = valueForPath(args, key)
      val rendered = renderValue(value)
      if (!rendered.isNullOrBlank()) return rendered
    }
    return null
  }

  private fun valueForPath(
    args: JsonObject?,
    path: String,
  ): JsonElement? {
    var current: JsonElement? = args
    for (segment in path.split(".")) {
      if (segment.isBlank()) return null
      val obj = current as? JsonObject ?: return null
      current = obj[segment]
    }
    return current
  }

  private fun renderValue(value: JsonElement?): String? {
    if (value == null) return null
    if (value is JsonPrimitive) {
      if (value.isString) {
        val trimmed = value.contentOrNull?.trim().orEmpty()
        if (trimmed.isEmpty()) return null
        val firstLine =
          trimmed
            .lineSequence()
            .firstOrNull()
            ?.trim()
            .orEmpty()
        if (firstLine.isEmpty()) return null
        return if (firstLine.length > 160) "${firstLine.take(157)}…" else firstLine
      }
      val raw = value.contentOrNull?.trim().orEmpty()
      raw.toBooleanStrictOrNull()?.let { return it.toString() }
      raw.toLongOrNull()?.let { return it.toString() }
      raw.toDoubleOrNull()?.let { return it.toString() }
    }
    if (value is JsonArray) {
      val items = value.mapNotNull { renderValue(it) }
      if (items.isEmpty()) return null
      val preview = items.take(3).joinToString(", ")
      return if (items.size > 3) "$preview…" else preview
    }
    return null
  }

  private fun shortenHomeInString(value: String): String {
    val home =
      System.getProperty("user.home")?.takeIf { it.isNotBlank() }
        ?: System.getenv("HOME")?.takeIf { it.isNotBlank() }
    if (home.isNullOrEmpty()) return value
    return value
      .replace(home, "~")
      .replace(Regex("/Users/[^/]+"), "~")
      .replace(Regex("/home/[^/]+"), "~")
  }

  private fun JsonElement?.asStringOrNull(): String? {
    val primitive = this as? JsonPrimitive ?: return null
    return if (primitive.isString) primitive.contentOrNull else primitive.toString()
  }

  private fun JsonElement?.asNumberOrNull(): Double? {
    val primitive = this as? JsonPrimitive ?: return null
    val raw = primitive.contentOrNull ?: return null
    return raw.toDoubleOrNull()
  }
}
