def dashboard_if_signed_in
  redirect '/dashboard' if signed_in?
end

def csrf_safe?
  csrf_token == params[:csrf_token] || csrf_token == request.env['HTTP_X_CSRF_TOKEN']
end

def csrf_token
   session[:_csrf_token] ||= SecureRandom.base64(32)
end

def is_education?
  current_site && current_site.is_education
end

def require_login
  redirect '/' unless signed_in? && current_site
end

def signed_in?
  return false if current_site.nil?
  true
end

def signout
  @_site = nil
  @_parent_site = nil
  session[:id] = nil
  session.clear
  response.delete_cookie 'neocities', path: '/'
  request.env['rack.session.options'][:drop] = true
end

def current_site
  return nil if session[:id].nil?
  @_site ||= Site[id: session[:id]]
  @_parent_site ||= @_site.parent

  if @_site.is_banned || @_site.is_deleted || (@_parent_site && (@_parent_site.is_banned || @_parent_site.is_deleted))
    signout
  end

  @_site
end

def parent_site
  @_parent_site || current_site
end

def meta_robots(newtag=nil)
  if newtag
    @_meta_robots = newtag
  end

  @_meta_robots
end

def meta_description(new_description=nil)
  if new_description
    @_meta_description = new_description
  end

  description = @_meta_description || @description
  return nil if description.nil? || description.strip.empty?

  description
end

def title
  out = "Neocities"
  return out if request.path == '/'
  
  full_title = if @title
    "#{out} - #{@title}"
  else
    path_parts = request.path.split('/').reject(&:empty?)
    formatted_parts = path_parts.map do |part|
      part.split(/[_\-]/).map(&:capitalize).join(' ')
    end
    "#{out} - #{formatted_parts.join(' - ')}"
  end
  
  if full_title.length >= 70
    full_title[0..65] + "..."
  else
    full_title
  end
end

def encoding_fix(file)
  begin
    Rack::Utils.escape_html file
  rescue ArgumentError => e
    if e.message =~ /invalid byte sequence in UTF-8/ ||
       e.message =~ /incompatible character encodings/
      return Rack::Utils.escape_html(file.force_encoding('BINARY'))
    end
    fail
  end
end

def send_confirmation_email(site=current_site)
  # Child sites don't need email confirmation - they use parent site's email
  return if !site.parent?

  if site.email_confirmation_count > Site::MAXIMUM_EMAIL_CONFIRMATIONS
    flash[:error] = 'You sent too many email confirmation requests, cannot continue.'
    redirect request.referrer
  end

  DB['UPDATE sites set email_confirmation_count=email_confirmation_count+1 WHERE id=?', site.id].first

  EmailWorker.perform_async({
    from: 'web@neocities.org',
    reply_to: 'noreply@neocities.org',
    to: site.email,
    subject: "[Neocities] Confirm your email address",
    body: Tilt.new('./views/templates/email/confirm.erb', pretty: true).render(self, site: site)
  })
end

def dont_browser_cache
  headers['Cache-Control'] = 'private, no-store, max-age=0, no-cache, must-revalidate, post-check=0, pre-check=0'
  headers['Pragma'] = 'no-cache'
  headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT'
  @dont_browser_cache = true
end

def sanitize_comment(text)
  Rinku.auto_link Sanitize.fragment(text), :all, 'target="_blank" rel="nofollow"'
end

def flash_message_keys
  flash.keys.select {|k| [:success, :error, :errors].include?(k) && !flash[k].to_s.empty?}
end

def flash_message_entries
  entries = []

  Array(@error).each do |message|
    entries << {type: 'error', message: message} unless message.to_s.empty?
  end

  flash_message_keys.each do |key|
    type = key == :success ? 'success' : 'error'

    Array(flash[key]).each do |message|
      entries << {type: type, message: message} unless message.to_s.empty?
    end
  end

  entries
end

def normalize_comment_message(message)
  message.to_s.gsub(/\r\n?/, "\n").gsub(/\n{3,}/, "\n\n").strip
end

def comment_message_error(message)
  return 'Comment cannot be empty.' if message.empty?
  return "Comments must be #{Site::MAX_COMMENT_SIZE} characters or fewer." if message.length > Site::MAX_COMMENT_SIZE

  lines = message.split("\n")
  return "Comments must be #{Site::MAX_COMMENT_LINES} lines or fewer." if lines.length > Site::MAX_COMMENT_LINES

  return nil if lines.length < Site::MULTILINE_COMMENT_AVERAGE_LINE_THRESHOLD

  nonblank_lines = lines.map(&:strip).reject(&:empty?)
  return 'Comment cannot be empty.' if nonblank_lines.empty?

  average_line_length = nonblank_lines.sum(&:length).to_f / nonblank_lines.length
  return nil if average_line_length >= Site::MIN_MULTILINE_COMMENT_AVERAGE_LINE_LENGTH

  'Multiline comments need a little more text on each line.'
end

def valid_comment_message?(message)
  comment_message_error(message).nil?
end

def comment_unavailable_message(site=nil)
  return 'Comments are disabled for this site.' if site && site.profile_comments_enabled == false

  if current_site && !current_site.commenting_allowed?
    if current_site.commenting_too_much?
      return "To prevent spam, comments are limited to #{Site::MAX_COMMENTS_PER_DAY} per day. Please try again tomorrow."
    end

    return "To prevent spam, you cannot comment until you have updated your site #{Site::COMMENTING_ALLOWED_UPDATED_COUNT} times on separate days, and your account is one week old."
  end

  'You cannot comment on this right now.'
end

def flash_display(opts={})
  erb :'_flash', layout: false, locals: {opts: opts}
end

def hcaptcha_valid?
  return true if ENV['RACK_ENV'] == 'test' || ENV['CI']
  return false unless params[:'h-captcha-response']

  resp = HTTP.get('https://hcaptcha.com/siteverify', params: {
    secret: $config['hcaptcha_secret_key'],
    response: params[:'h-captcha-response']
  })

  resp = JSON.parse resp

  if resp['success'] == true
    true
  else
    false
  end
end

JS_ESCAPE_MAP = {"\\" => "\\\\", "</" => '<\/', "\r\n" => '\n', "\n" => '\n', "\r" => '\n', '"' => '\\"', "'" => "\\'", "`" => "\\`", "$" => "\\$"}

def escape_javascript(javascript)
  javascript = javascript.to_s
  if javascript.empty?
    result = ""
  else
    result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u, JS_ESCAPE_MAP)
  end
  result
end
