// Platform dispatch: one entry point per gateway capability, routed to the
// right adapter. Mirrors Hermes' `Platform`-keyed registry in
// `gateway/platforms/__init__.py` — `send_message`, `send_typing`,
// `send_media` and `parse_inbound_event`.

///|
/// Webhook path for a platform (`/telegram`, `/whatsapp`, …).
pub fn webhook_path(platform : Platform) -> String {
  platform.value()
}

///|
/// Send a message through the platform's adapter. ``thread_id`` is forwarded
/// where the platform has a thread concept; ``context_token`` is the WeChat
/// iLink session token.
pub async fn send_message(
  cfg : PlatformConfig,
  chat_id : String,
  content : String,
  reply_to? : String,
  thread_id? : String,
  context_token? : String,
) -> SendResult {
  match cfg.platform {
    Telegram => telegram_send(cfg, chat_id, content, reply_to?, thread_id?)
    Discord => discord_send(cfg, chat_id, content, reply_to?)
    Slack => slack_send(cfg, chat_id, content, reply_to?, thread_ts?=thread_id)
    Whatsapp => whatsapp_send(cfg, chat_id, content, reply_to?)
    Signal => signal_send(cfg, chat_id, content, reply_to?)
    Email =>
      email_send(
        cfg,
        chat_id,
        cfg.extra_str("default_subject", "Message from imrelay"),
        content,
        in_reply_to?=reply_to,
      )
    Mattermost => mattermost_send(cfg, chat_id, content, reply_to?)
    Matrix => matrix_send(cfg, chat_id, content, reply_to?, thread_id?)
    Dingtalk => dingtalk_send(cfg, chat_id, content, reply_to?)
    Feishu => feishu_send(cfg, chat_id, content, reply_to?)
    Weixin => weixin_send(cfg, chat_id, content, reply_to?, context_token?)
    Wecom => wecom_send(cfg, chat_id, content, reply_to?)
    Bluebubbles => bluebubbles_send(cfg, chat_id, content, reply_to?)
    HomeAssistant => homeassistant_send(cfg, chat_id, content, reply_to?)
  }
}

///|
/// Send a typing indicator. Platforms without one are a silent no-op.
pub async fn send_typing(
  cfg : PlatformConfig,
  chat_id : String,
  thread_id? : String,
) -> Unit {
  match cfg.platform {
    Telegram => telegram_typing(cfg, chat_id, thread_id?)
    Discord => discord_typing(cfg, chat_id)
    Slack => slack_typing(cfg, chat_id, thread_ts?=thread_id)
    Signal => signal_typing(cfg, chat_id)
    Matrix => matrix_typing(cfg, chat_id)
    Weixin => weixin_typing(cfg, chat_id)
    Bluebubbles => bluebubbles_typing(cfg, chat_id)
    Whatsapp => ()
    Email => ()
    Mattermost => ()
    Dingtalk => ()
    Feishu => ()
    Wecom => ()
    HomeAssistant => ()
  }
}

///|
/// Send a media URL (or, for BlueBubbles, a local path).
pub async fn send_media(
  cfg : PlatformConfig,
  chat_id : String,
  kind : String,
  url : String,
  caption? : String,
) -> SendResult {
  match cfg.platform {
    Telegram => telegram_send_media(cfg, chat_id, kind, url, caption?)
    Discord => discord_send_media(cfg, chat_id, kind, url, caption?)
    Whatsapp => whatsapp_send_media(cfg, chat_id, kind, url, caption?)
    Bluebubbles => bluebubbles_send_attachment(cfg, chat_id, url)
    _ => {
      let text = match caption {
        Some(c) => "\{c}\n\{url}"
        None => url
      }
      send_message(cfg, chat_id, text)
    }
  }
}

///|
/// React to a message, where the platform supports reactions.
pub async fn send_reaction(
  cfg : PlatformConfig,
  chat_id : String,
  message_id : String,
  emoji : String,
) -> SendResult {
  match cfg.platform {
    Telegram => telegram_react(cfg, chat_id, message_id, emoji)
    Discord => discord_react(cfg, chat_id, message_id, emoji)
    Slack => slack_react(cfg, chat_id, message_id, emoji)
    Matrix => matrix_react(cfg, chat_id, message_id, emoji)
    _ =>
      SendResult::failed(
        "\{cfg.platform.value()}: reactions unsupported",
        kind=Forbidden,
      )
  }
}

///|
/// Edit a previously sent message, where the platform supports it.
pub async fn edit_message(
  cfg : PlatformConfig,
  chat_id : String,
  message_id : String,
  content : String,
) -> SendResult {
  match cfg.platform {
    Telegram => telegram_edit(cfg, chat_id, message_id, content)
    Discord => discord_edit(cfg, chat_id, message_id, content)
    Slack => slack_edit(cfg, chat_id, message_id, content)
    Matrix => matrix_edit(cfg, chat_id, message_id, content)
    _ =>
      SendResult::failed(
        "\{cfg.platform.value()}: edit unsupported",
        kind=Forbidden,
      )
  }
}

///|
/// Delete a message, where the platform supports it.
pub async fn delete_message(
  cfg : PlatformConfig,
  chat_id : String,
  message_id : String,
) -> SendResult {
  match cfg.platform {
    Telegram => telegram_delete(cfg, chat_id, message_id)
    Discord => discord_delete(cfg, chat_id, message_id)
    Slack => slack_delete(cfg, chat_id, message_id)
    _ =>
      SendResult::failed(
        "\{cfg.platform.value()}: delete unsupported",
        kind=Forbidden,
      )
  }
}

///|
/// Chat metadata, where the platform exposes it.
pub async fn get_chat_info(cfg : PlatformConfig, chat_id : String) -> ChatInfo? {
  match cfg.platform {
    Telegram => telegram_get_chat(cfg, chat_id)
    Discord => discord_get_chat(cfg, chat_id)
    Slack => slack_get_chat(cfg, chat_id)
    Matrix => matrix_get_chat(cfg, chat_id)
    Mattermost => mattermost_get_chat(cfg, chat_id)
    Feishu => feishu_get_chat(cfg, chat_id)
    Bluebubbles => bluebubbles_get_chat(cfg, chat_id)
    HomeAssistant => homeassistant_get_chat(cfg, chat_id)
    _ => None
  }
}

///|
/// Parse a webhook body into normalized events for ``platform``.
/// Unknown platforms and malformed bodies yield an empty array.
pub fn parse_inbound(platform : Platform, body : String) -> Array[MessageEvent] {
  match platform {
    Telegram => {
      let j = @json.parse(body) catch { _ => return [] }
      telegram_parse_update(j)
    }
    Discord =>
      if body.contains("\"type\"") &&
        (body.contains("\"MESSAGE_CREATE\"") || body.contains("\"t\"")) {
        discord_parse_message_event(body)
      } else {
        discord_parse_interaction(body)
      }
    Slack => slack_parse_event(body)
    Whatsapp => whatsapp_parse_webhook(body)
    Signal => signal_parse_receive(body)
    Email =>
      match email_parse_message(body) {
        Some(ev) => [ev]
        None => []
      }
    Mattermost => mattermost_parse_webhook(body)
    Matrix => matrix_parse_sync(body)
    Dingtalk => dingtalk_parse_callback(body)
    Feishu => feishu_parse_event(body)
    Weixin => {
      let j = @json.parse(body) catch { _ => return [] }
      let out : Array[MessageEvent] = []
      for msg in json_field_array(j, "msgs").unwrap_or([]) {
        match weixin_parse_message(msg) {
          Some(ev) => out.push(ev)
          None => ()
        }
      }
      out
    }
    Wecom => wecom_parse_callback(body)
    Bluebubbles => bluebubbles_parse_event(body)
    HomeAssistant => homeassistant_parse_event(body)
  }
}

///|
/// `true` when the platform has enough configuration to be usable.
pub fn platform_configured(cfg : PlatformConfig) -> Bool {
  match cfg.platform {
    Telegram | Discord | Slack | Weixin => cfg.token_of() is Some(_)
    Whatsapp =>
      cfg.token_of() is Some(_) && whatsapp_phone_number_id(cfg) is Some(_)
    Signal => signal_account(cfg) is Some(_) && !signal_http_url(cfg).is_empty()
    Email => !email_smtp_config(cfg).host.is_empty()
    Mattermost =>
      cfg.token_of() is Some(_) && !mattermost_api_base(cfg).is_empty()
    Matrix => cfg.token_of() is Some(_) || !matrix_homeserver(cfg).is_empty()
    Dingtalk =>
      dingtalk_robot_token(cfg) is Some(_) || dingtalk_app_key(cfg) is Some(_)
    Feishu =>
      !feishu_app_id(cfg).is_empty() && feishu_app_secret(cfg) is Some(_)
    Wecom => !wecom_corp_id(cfg).is_empty() && wecom_corp_secret(cfg) is Some(_)
    Bluebubbles =>
      !bluebubbles_api_base(cfg).is_empty() &&
      bluebubbles_api_base(cfg) != "/api/v1"
    HomeAssistant =>
      cfg.token_of() is Some(_) && !homeassistant_url(cfg).is_empty()
  }
}