# SPDX-License-Identifier: AGPL-3.0-only
import asyncdispatch, strutils, sequtils, uri, options, times
import jester, karax/vdom

import router_utils
import ".."/[types, redis_cache, formatters, query, api]
import ../views/[general, profile, timeline, status, search, about_account]

export vdom
export uri, sequtils
export router_utils
export redis_cache, formatters, query, api
export profile, timeline, status, about_account

proc getQuery*(request: Request; tab, name: string; prefs: Prefs): Query =
  let view = request.params.getOrDefault("view")
  case tab
  of "with_replies":
    result = getReplyQuery(name)
  of "media":
    result = getMediaQuery(name)
    result.view =
      if view in ["timeline", "grid", "gallery"]: view
      else: prefs.mediaView.toLowerAscii
  of "search":
    result = initQuery(params(request), name=name)
  else:
    result = Query(fromUser: @[name])

template skipIf[T](cond: bool; default; body: Future[T]): Future[T] =
  if cond:
    let fut = newFuture[T]()
    fut.complete(default)
    fut
  else:
    body

proc fetchProfile*(after: string; query: Query; skipRail=false): Future[Profile] {.async.} =
  let
    name = query.fromUser[0]
    userId = await getUserId(name)

  if userId.len == 0:
    return Profile(user: User(username: name))
  elif userId == "suspended":
    return Profile(user: User(username: name, suspended: true))

  # temporary fix to prevent errors from people browsing
  # timelines during/immediately after deployment
  var after = after
  if query.kind in {posts, replies} and after.startsWith("scroll"):
    after.setLen 0

  let
    rail =
      skipIf(skipRail or query.kind == QueryKind.media, @[]):
        getCachedPhotoRail(userId)

    user = getCachedUser(name)
    info = getCachedAccountInfo(name, fetch=false)

  result =
    case query.kind
    of posts: await getGraphUserTweets(userId, TimelineKind.tweets, after)
    of replies: await getGraphUserTweets(userId, TimelineKind.replies, after)
    of media: await getGraphUserTweets(userId, TimelineKind.media, after)
    else: Profile(tweets: await getGraphTweetSearch(query, after))

  result.user = await user
  result.photoRail = await rail
  result.accountInfo = await info

  result.tweets.query = query

proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs;
                   rss, after: string): Future[string] {.async.} =
  if query.fromUser.len != 1:
    let
      timeline = await getGraphTweetSearch(query, after)
      html = renderTweetSearch(timeline, prefs, getPath())
    return renderMain(html, request, cfg, prefs, "Multi", rss=rss)

  var profile = await fetchProfile(after, query)
  template u: untyped = profile.user

  if u.suspended:
    return showError(getSuspended(u.username), cfg)

  if profile.user.id.len == 0: return

  let pHtml = renderProfile(profile, prefs, getPath())
  result = renderMain(pHtml, request, cfg, prefs, pageTitle(u), pageDesc(u),
                      rss=rss, images = @[u.getUserPic("_400x400")],
                      banner=u.banner)

template respTimeline*(timeline: typed) =
  let t = timeline
  if t.len == 0:
    resp Http404, showError("User \"" & @"name" & "\" not found", cfg)
  resp t

template respUserId*() =
  cond @"user_id".len > 0
  let username = await getCachedUsername(@"user_id")
  if username.len > 0:
    redirect("/" & username)
  else:
    resp Http404, showError("User not found", cfg)

proc createTimelineRouter*(cfg: Config) =
  router timeline:
    get "/i/user/@user_id":
      respUserId()

    get "/intent/user":
      respUserId()

    get "/intent/follow/?":
      let username = request.params.getOrDefault("screen_name")
      if username.len == 0:
        resp Http400, showError("Missing screen_name parameter", cfg)
      redirect("/" & username)

    get "/@name/about/?":
      cond @"name".allCharsInSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})
      let
        prefs = requestPrefs()
        name = @"name"
        info = await getCachedAccountInfo(name)
      if info.suspended:
        resp showError(getSuspended(name), cfg)
      if info.username.len == 0:
        resp Http404, showError("User \"" & name & "\" not found", cfg)
      let aboutHtml = renderAboutAccount(info)
      resp renderMain(aboutHtml, request, cfg, prefs,
                      "About @" & info.username)

    get "/@name/@kind/?":
      cond @"kind" in ["followers", "following"]
      cond '.' notin @"name"
      cond @"name".allCharsInSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})
      let
        prefs = requestPrefs()
        name = @"name"
        userId = await getUserId(name)

      if userId.len == 0:
        resp Http404, showError("User \"" & name & "\" not found", cfg)
      if userId == "suspended":
        resp showError(getSuspended(name), cfg)

      let
        cursor = getCursor()
        isFollowers = @"kind" == "followers"
        user = await getCachedUser(name)
        results = if isFollowers: await getGraphFollowers(userId, cursor)
                  else: await getGraphFollowing(userId, cursor)

      if user.protected:
        resp renderMain(renderProtected(user.username), request, cfg, prefs,
                        "Protected account @" & name)

      let
        tab = if isFollowers: "Followers" else: "Following"
        title = (if isFollowers: "People following @" else: "People followed by @") & name
        html = renderUserList(user, results, prefs, request.path, tab)

      resp renderMain(html, request, cfg, prefs, title,
                      images = @[user.getUserPic("_400x400")])

    get "/@name/?@tab?/?":
      cond '.' notin @"name"
      cond @"name" notin ["pic", "gif", "video", "search", "settings", "login", "intent", "i"]
      cond @"name".allCharsInSet({'a'..'z', 'A'..'Z', '0'..'9', '_', ','})
      cond @"tab" in ["with_replies", "media", "search", ""]
      let
        prefs = requestPrefs()
        after = getCursor()
        names = getNames(@"name")

      var query = request.getQuery(@"tab", @"name", prefs)
      if names.len != 1:
        query.fromUser = names

      # used for the infinite scroll feature
      if @"scroll".len > 0:
        if query.fromUser.len != 1:
          var timeline = await getGraphTweetSearch(query, after)
          if timeline.content.len == 0: 
            resp Http204
          timeline.beginning = true
          resp $renderTweetSearch(timeline, prefs, getPath())
        else:
          var profile = await fetchProfile(after, query, skipRail=true)
          if profile.tweets.content.len == 0: resp Http404
          profile.tweets.beginning = true
          resp $renderTimelineTweets(profile.tweets, prefs, getPath())

      let rssEnabled =
        if @"tab".len == 0: cfg.enableRSSUserTweets
        elif @"tab" == "with_replies": cfg.enableRSSUserReplies
        elif @"tab" == "media": cfg.enableRSSUserMedia
        elif @"tab" == "search": cfg.enableRSSSearch
        else: false

      let rss =
        if not rssEnabled: 
          ""
        elif @"tab".len == 0:
          "/$1/rss" % @"name"
        elif @"tab" == "search":
          "/$1/search/rss?$2" % [@"name", genQueryUrl(query)]
        else:
          "/$1/$2/rss" % [@"name", @"tab"]

      respTimeline(await showTimeline(request, query, cfg, prefs, rss, after))
