// Home Assistant adapter.
//
// Mirrors Hermes `plugins/platforms/homeassistant/adapter.py`: outbound
// messages call the REST service API
// (`POST /api/services/{domain}/{service}`) with a bearer token, and inbound
// traffic arrives through an automation that POSTs to the gateway webhook.
// `chat_id` is the notify service or entity to target.

///|
/// HA base URL (`HASS_URL`).
pub fn homeassistant_url(cfg : PlatformConfig) -> String {
  let url = cfg.extra_str("url", "")
  if url.has_suffix("/") {
    url[:url.length() - 1].to_owned()
  } else {
    url
  }
}

///|
/// Notify domain, e.g. `notify` (`[homeassistant.extra] notify_domain`).
pub fn homeassistant_domain(cfg : PlatformConfig) -> String {
  cfg.extra_str("notify_domain", "notify")
}

///|
/// Service name; falls back to the chat id (a notify service or entity id).
pub fn homeassistant_service(cfg : PlatformConfig, chat_id : String) -> String {
  let configured = cfg.extra_str("notify_service", "")
  if configured.is_empty() {
    chat_id
  } else {
    configured
  }
}

///|
/// `services/{domain}/{service}` payload.
pub fn homeassistant_send_payload(
  message : String,
  title : String?,
  data : Json?,
) -> Json {
  let payload = json_obj([("message", Json::string(message))])
  let payload = obj_with_opt(payload, "title", title)
  match data {
    Some(d) => if payload is Object(m) { m.set("data", d) }
    None => ()
  }
  payload
}

///|
/// Send ``content`` through the HA service API.
pub async fn homeassistant_send(
  cfg : PlatformConfig,
  chat_id : String,
  content : String,
  reply_to? : String,
) -> SendResult {
  ignore(reply_to)
  let base = homeassistant_url(cfg)
  if base.is_empty() {
    return missing_config(HomeAssistant, "HASS_URL")
  }
  match cfg.token_of() {
    None => missing_config(HomeAssistant, "HASS_TOKEN")
    Some(token) => {
      let service = homeassistant_service(cfg, chat_id)
      let url = "\{base}/api/services/\{homeassistant_domain(cfg)}/\{service}"
      let title = match cfg.extra_first(["title"]) {
        Some(t) => Some(t)
        None => None
      }
      let payload = homeassistant_send_payload(content, title, None)
      let r = send_with_retry(() => {
        match post_json(url, payload, bearer_headers(token)) {
          Err(e) => send_failure("homeassistant", e)
          Ok(j) => send_ok(None, j)
        }
      })
      r
    }
  }
}

///|
/// Entity state (`GET /api/states/{entity_id}`).
pub async fn homeassistant_get_chat(
  cfg : PlatformConfig,
  chat_id : String,
) -> ChatInfo? {
  match cfg.token_of() {
    None => None
    Some(token) => {
      let url = "\{homeassistant_url(cfg)}/api/states/\{chat_id}"
      match get_json(url, bearer_headers(token)) {
        Err(_) => None
        Ok(j) => {
          let name = match json_field_obj(j, "attributes") {
            Some(a) => json_field_string(a, "friendly_name").unwrap_or(chat_id)
            None => chat_id
          }
          Some({
            name,
            chat_type: json_field_string(j, "state").unwrap_or("unknown"),
            chat_id,
          })
        }
      }
    }
  }
}

///|
/// Parse an inbound automation webhook:
/// `{"text":"…","chat_id":"…","user_id":"…"}` (or `message` for `text`).
pub fn homeassistant_parse_event(body : String) -> Array[MessageEvent] {
  let out : Array[MessageEvent] = []
  let j = @json.parse(body) catch { _ => return out }
  let text = or_opt(
    json_field_string(j, "text"),
    json_field_string(j, "message"),
  ).unwrap_or("")
  let chat_id = match json_field_string(j, "chat_id") {
    Some(c) => c
    None => "notify"
  }
  if text.is_empty() {
    return out
  }
  let mtype = if text.has_prefix("/") { Command } else { Text }
  out.push(
    MessageEvent::new(
      text,
      mtype,
      HomeAssistant,
      chat_id,
      user_id?=json_field_string(j, "user_id"),
      user_name?=json_field_string(j, "user_name"),
      media=[],
      timestamp=now_ms(),
    ),
  )
  out
}