{% skip_file if flag?(:api_only) %}

module Invidious::Routes::Watch
  def self.handle(env)
    locale = env.get("preferences").as(Preferences).locale
    region = env.params.query["region"]?

    if env.params.query.to_s.includes?("%20") || env.params.query.to_s.includes?("+")
      url = "/watch?" + env.params.query.to_s.gsub("%20", "").delete("+")
      return env.redirect url
    end

    if env.params.query["v"]?
      id = env.params.query["v"]

      if env.params.query["v"].empty?
        return error_template(400, "Invalid parameters.")
      end

      if id.size > 11
        url = "/watch?v=#{id[0, 11]}"
        env.params.query.delete_all("v")
        if env.params.query.size > 0
          url += "&#{env.params.query}"
        end

        return env.redirect url
      end
    else
      return env.redirect "/"
    end

    embed_link = "/embed/#{id}"
    if env.params.query.size > 1
      embed_params = HTTP::Params.parse(env.params.query.to_s)
      embed_params.delete_all("v")
      embed_link += "?"
      embed_link += embed_params.to_s
    end

    plid = env.params.query["list"]?.try &.gsub(/[^a-zA-Z0-9_-]/, "")
    continuation = process_continuation(env.params.query, plid, id)

    nojs = env.params.query["nojs"]?

    nojs ||= "0"
    nojs = nojs == "1"

    preferences = env.get("preferences").as(Preferences)

    user = env.get?("user").try &.as(User)
    if user
      subscriptions = user.subscriptions
      watched = user.watched
      notifications = user.notifications
    end
    subscriptions ||= [] of String

    params = process_video_params(env.params.query, preferences)
    env.params.query.delete_all("listen")

    begin
      video = get_video(id, region: params.region)
    rescue ex : VideoRedirect
      return env.redirect env.request.resource.gsub(id, ex.video_id)
    rescue ex : NotFoundException
      LOGGER.error("get_video not found: #{id} : #{ex.message}")
      return error_template(404, ex)
    rescue ex
      LOGGER.error("get_video: #{id} : #{ex.message}")
      return error_template(500, ex)
    end

    if preferences.annotations_subscribed &&
       subscriptions.includes?(video.ucid) &&
       (env.params.query["iv_load_policy"]? || "1") == "1"
      params.annotations = true
    end
    env.params.query.delete_all("iv_load_policy")

    if watched && preferences.watch_history && !watched.includes? id
      Invidious::Database::Users.mark_watched(user.as(User), id)
    end

    if notifications && notifications.includes? id
      Invidious::Database::Users.remove_notification(user.as(User), id)
      env.get("user").as(User).notifications.delete(id)
      notifications.delete(id)
    end

    if nojs
      if preferences
        source = preferences.comments[0]
        if source.empty?
          source = preferences.comments[1]
        end

        if source == "youtube"
          begin
            comment_html = JSON.parse(fetch_youtube_comments(id, nil, "html", locale, preferences.thin_mode, region))["contentHtml"]
          rescue ex
            if preferences.comments[1] == "reddit"
              comments, reddit_thread = fetch_reddit_comments(id)
              comment_html = template_reddit_comments(comments, locale)

              comment_html = fill_links(comment_html, "https", "www.reddit.com")
              comment_html = replace_links(comment_html)
            end
          end
        elsif source == "reddit"
          begin
            comments, reddit_thread = fetch_reddit_comments(id)
            comment_html = template_reddit_comments(comments, locale)

            comment_html = fill_links(comment_html, "https", "www.reddit.com")
            comment_html = replace_links(comment_html)
          rescue ex
            if preferences.comments[1] == "youtube"
              comment_html = JSON.parse(fetch_youtube_comments(id, nil, "html", locale, preferences.thin_mode, region))["contentHtml"]
            end
          end
        end
      else
        comment_html = JSON.parse(fetch_youtube_comments(id, nil, "html", locale, preferences.thin_mode, region))["contentHtml"]
      end

      comment_html ||= ""
    end

    fmt_stream = video.fmt_stream
    adaptive_fmts = video.adaptive_fmts

    if params.local
      fmt_stream.each { |fmt| fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).request_target) }
      adaptive_fmts.each { |fmt| fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).request_target) }
    end

    video_streams = video.video_streams
    audio_streams = video.audio_streams

    # Older videos may not have audio sources available.
    # We redirect here so they're not unplayable
    if audio_streams.empty? && !video.live_now
      if params.quality == "dash"
        env.params.query.delete_all("quality")
        env.params.query["quality"] = "medium"
        return env.redirect "/watch?#{env.params.query}"
      elsif params.listen
        env.params.query.delete_all("listen")
        env.params.query["listen"] = "0"
        return env.redirect "/watch?#{env.params.query}"
      end
    end

    captions = video.captions

    preferred_captions = captions.select { |caption|
      params.preferred_captions.includes?(caption.name) ||
        params.preferred_captions.includes?(caption.language_code.split("-")[0])
    }
    preferred_captions.sort_by! { |caption|
      (params.preferred_captions.index(caption.name) ||
        params.preferred_captions.index(caption.language_code.split("-")[0])).not_nil!
    }
    captions = captions - preferred_captions

    aspect_ratio = "16:9"

    thumbnail = "/vi/#{video.id}/maxres.jpg"

    if params.raw
      if params.listen
        url = audio_streams[0]["url"].as_s

        if params.quality.ends_with? "k"
          audio_streams.each do |fmt|
            if fmt["bitrate"].as_i == params.quality.rchop("k").to_i
              url = fmt["url"].as_s
            end
          end
        end
      else
        url = fmt_stream[0]["url"].as_s

        fmt_stream.each do |fmt|
          if fmt["quality"].as_s == params.quality
            url = fmt["url"].as_s
          end
        end
      end

      return env.redirect url
    end

    # Structure used for the download widget
    video_assets = Invidious::Frontend::WatchPage::VideoAssets.new(
      full_videos: fmt_stream,
      video_streams: video_streams,
      audio_streams: audio_streams,
      captions: video.captions
    )

    templated "watch"
  end

  def self.redirect(env)
    url = "/watch?v=#{env.params.url["id"]}"
    if env.params.query.size > 0
      url += "&#{env.params.query}"
    end

    return env.redirect url
  end

  def self.mark_watched(env)
    locale = env.get("preferences").as(Preferences).locale

    user = env.get? "user"
    sid = env.get? "sid"
    referer = get_referer(env, "/feed/subscriptions")

    redirect = env.params.query["redirect"]?
    redirect ||= "true"
    redirect = redirect == "true"

    if !user
      if redirect
        return env.redirect referer
      else
        return error_json(403, "No such user")
      end
    end

    user = user.as(User)
    sid = sid.as(String)
    token = env.params.body["csrf_token"]?

    id = env.params.query["id"]?
    if !id
      env.response.status_code = 400
      return
    end

    begin
      validate_request(token, sid, env.request, HMAC_KEY, locale)
    rescue ex
      if redirect
        return error_template(400, ex)
      else
        return error_json(400, ex)
      end
    end

    if env.params.query["action_mark_watched"]?
      action = "action_mark_watched"
    elsif env.params.query["action_mark_unwatched"]?
      action = "action_mark_unwatched"
    else
      return env.redirect referer
    end

    case action
    when "action_mark_watched"
      if !user.watched.includes? id
        Invidious::Database::Users.mark_watched(user, id)
      end
    when "action_mark_unwatched"
      Invidious::Database::Users.mark_unwatched(user, id)
    else
      return error_json(400, "Unsupported action #{action}")
    end

    if redirect
      env.redirect referer
    else
      env.response.content_type = "application/json"
      "{}"
    end
  end

  def self.clip(env)
    clip_id = env.params.url["clip"]?

    return error_template(400, "A clip ID is required") if !clip_id

    response = YoutubeAPI.resolve_url("https://www.youtube.com/clip/#{clip_id}")
    return error_template(400, "Invalid clip ID") if response["error"]?

    if video_id = response.dig?("endpoint", "watchEndpoint", "videoId")
      return env.redirect "/watch?v=#{video_id}&#{env.params.query}"
    else
      return error_template(404, "The requested clip doesn't exist")
    end
  end

  def self.download(env)
    if CONFIG.disabled?("downloads")
      return error_template(403, "Administrator has disabled this endpoint.")
    end

    title = env.params.body["title"]? || ""
    video_id = env.params.body["id"]? || ""
    selection = env.params.body["download_widget"]?

    if title.empty? || video_id.empty? || selection.nil?
      return error_template(400, "Missing form data")
    end

    download_widget = JSON.parse(selection)

    extension = download_widget["ext"].as_s
    filename = "#{title}-#{video_id}.#{extension}"

    # Delete the now useless URL parameters
    env.params.body.delete("id")
    env.params.body.delete("title")
    env.params.body.delete("download_widget")

    # Pass form parameters as URL parameters for the handlers of both
    # /latest_version and /api/v1/captions. This avoids an un-necessary
    # redirect and duplicated (and hazardous) sanity checks.
    if label = download_widget["label"]?
      # URL params specific to /api/v1/captions/:id
      env.params.url["id"] = video_id
      env.params.query["title"] = filename
      env.params.query["label"] = URI.decode_www_form(label.as_s)

      return Invidious::Routes::API::V1::Videos.captions(env)
    elsif itag = download_widget["itag"]?.try &.as_i
      # URL params specific to /latest_version
      env.params.query["id"] = video_id
      env.params.query["itag"] = itag.to_s
      env.params.query["title"] = filename
      env.params.query["local"] = "true"

      return Invidious::Routes::VideoPlayback.latest_version(env)
    else
      return error_template(400, "Invalid label or itag")
    end
  end
end
