///|
/// YouTube Data API v3 のレスポンス解釈エラー。
pub(all) suberror YouTubeError {
  VideoNotFound
  NotLive
  /// チャットが無効・視聴者限定など。画面に出る文言を持つ
  ChatUnavailable(String)
  TypeMismatch(String)
} derive(Debug, Eq)

///|
/// `liveChatMessages.list` の 1 ページ分。
pub(all) struct LiveChatPage {
  comments : Array[Comment]
  next_page_token : String?
  /// API が指示するポーリング間隔(ミリ秒)。無ければ既定値
  polling_interval_ms : Int
} derive(Debug)

///|
/// pollingIntervalMillis が無いときの既定値(クォータ節約のため長め)。
pub const DEFAULT_POLLING_INTERVAL_MS : Int = 5000

///|
/// `videos.list(part=liveStreamingDetails)` の URL。
pub fn videos_list_url(video_id : String, api_key : String) -> String {
  "https://www.googleapis.com/youtube/v3/videos?part=liveStreamingDetails&id=\{video_id}&key=\{api_key}"
}

///|
/// `liveChatMessages.list(part=snippet,authorDetails)` の URL。
pub fn live_chat_messages_url(
  live_chat_id : String,
  api_key : String,
  page_token : String?,
) -> String {
  let base = "https://www.googleapis.com/youtube/v3/liveChat/messages?liveChatId=\{live_chat_id}&part=snippet,authorDetails&maxResults=200&key=\{api_key}"
  match page_token {
    Some(token) => "\{base}&pageToken=\{token}"
    None => base
  }
}

///|
/// `videos.list` のレスポンスから activeLiveChatId を取り出す。
pub fn parse_live_chat_id(json : Json) -> String raise YouTubeError {
  guard json is Object(root) else { raise TypeMismatch("response") }
  let items = match root.get("items") {
    Some(Array(items)) => items
    _ => raise TypeMismatch("items")
  }
  guard items is [first, ..] else { raise VideoNotFound }
  match first {
    Object(
      {
        "liveStreamingDetails": Object({ "activeLiveChatId": String(id), .. }),
        ..
      }
    ) => id
    _ => raise NotLive
  }
}

///|
/// `liveChatMessages.list` のレスポンスを分解する。
/// displayMessage の無い項目(メンバー加入通知など)は読み飛ばす。
pub fn parse_live_chat_page(json : Json) -> LiveChatPage raise YouTubeError {
  guard json is Object(root) else { raise TypeMismatch("response") }
  let items = match root.get("items") {
    None => []
    Some(Array(items)) => items
    Some(_) => raise TypeMismatch("items")
  }
  let comments : Array[Comment] = []
  for item in items {
    match item {
      Object(
        {
          "snippet": Object({ "displayMessage": String(text), .. }),
          "authorDetails": Object(
            { "channelId": String(channel_id), .. } as author
          ),
          ..
        }
      ) => {
        let name = match author.get("displayName") {
          Some(String(name)) => name
          _ => ""
        }
        comments.push(
          Comment::new(participant_id("youtube", channel_id), text, author=name),
        )
      }
      _ => ()
    }
  }
  let next_page_token = match root.get("nextPageToken") {
    Some(String(token)) => Some(token)
    _ => None
  }
  let polling_interval_ms = match root.get("pollingIntervalMillis") {
    Some(Number(n, ..)) => n.to_int()
    _ => DEFAULT_POLLING_INTERVAL_MS
  }
  { comments, next_page_token, polling_interval_ms, }
}