# frozen_string_literal: true
class ThemeJavascriptsController < ApplicationController
  DISK_CACHE_PATH = "#{Rails.root}/tmp/javascript-cache"
  TESTS_DISK_CACHE_PATH = "#{Rails.root}/tmp/javascript-cache/tests"

  skip_before_action(
    :check_xhr,
    :handle_theme,
    :preload_json,
    :redirect_to_login_if_required,
    :verify_authenticity_token,
    only: [:show, :show_tests]
  )

  before_action :is_asset_path, :no_cookies, :apply_cdn_headers, only: [:show, :show_tests]

  def show
    raise Discourse::NotFound unless last_modified.present?
    return render body: nil, status: 304 if not_modified?

    # Security: safe due to route constraint
    cache_file = "#{DISK_CACHE_PATH}/#{params[:digest]}.js"

    unless File.exist?(cache_file)
      content = query.pluck_first(:content)
      raise Discourse::NotFound if content.nil?

      FileUtils.mkdir_p(DISK_CACHE_PATH)
      File.write(cache_file, content)
    end

    # this is only required for NGINX X-SendFile it seems
    response.headers["Content-Length"] = File.size(cache_file).to_s
    set_cache_control_headers
    send_file(cache_file, disposition: :inline)
  end

  def show_tests
    digest = params[:digest]
    raise Discourse::NotFound if !digest.match?(/^\h{40}$/)

    theme = Theme.find_by(id: params[:theme_id])
    raise Discourse::NotFound if theme.blank?

    content, content_digest = theme.baked_js_tests_with_digest
    raise Discourse::NotFound if content.blank? || content_digest != digest

    @cache_file = "#{TESTS_DISK_CACHE_PATH}/#{digest}.js"
    return render body: nil, status: 304 if not_modified?

    if !File.exist?(@cache_file)
      FileUtils.mkdir_p(TESTS_DISK_CACHE_PATH)
      File.write(@cache_file, content)
    end

    response.headers["Content-Length"] = File.size(@cache_file).to_s
    set_cache_control_headers
    send_file(@cache_file, disposition: :inline)
  end

  private

  def query
    @query ||= JavascriptCache.where(digest: params[:digest]).limit(1)
  end

  def last_modified
    @last_modified ||= begin
      if params[:action].to_s == "show_tests"
        File.exist?(@cache_file) ? File.ctime(@cache_file) : nil
      else
        query.pluck_first(:updated_at)
      end
    end
  end

  def not_modified?
    cache_time =
      begin
        Time.rfc2822(request.env["HTTP_IF_MODIFIED_SINCE"])
      rescue ArgumentError
        nil
      end

    cache_time && last_modified && last_modified <= cache_time
  end

  def set_cache_control_headers
    if Rails.env.development?
      response.headers['Last-Modified'] = Time.zone.now.httpdate
      immutable_for(1.second)
    else
      response.headers['Last-Modified'] = last_modified.httpdate if last_modified
      immutable_for(1.year)
    end
  end
end