class ApplicationController < ActionController::Base
  protect_from_forgery

  APP_DOMAIN = 'coderwall.com'

  helper_method :signed_in?
  helper_method :current_user
  helper_method :viewing_self?
  helper_method :is_admin?
  helper_method :is_moderator?
  helper_method :viewing_user
  helper_method :round

  before_action :ensure_domain
  before_action :apply_flash_message
  before_action :require_registration
  before_action :clear_expired_cookie_if_session_is_empty
  before_action :ensure_and_reconcile_tracking_code
  before_action :track_utm
  before_action :show_achievement

  after_action :record_visit
  after_action :record_location

  rescue_from ActiveRecord::RecordNotFound, with: :render_404

  protected

  def apply_flash_message
    flash[:notice] = params[:flash] unless params[:flash].blank?
  end

  def apply_cache_buster
    response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
    response.headers["Pragma"]        = "no-cache"
    response.headers["Expires"]       = "Fri, 01 Jan 1990 00:00:00 GMT"
  end

  def clear_expired_cookie_if_session_is_empty
    if !signed_in?
      cookies.delete(:signedin)
    end
  end

  def current_user
    if @current_user.nil? && session[:current_user]
      unless @current_user = User.find_by_id(session[:current_user])
        session[:current_user] = nil
        store_location!
        redirect_to signin_path
      end
    end

    @current_user
  end

  #TODO remove this
  def viewing_user
    @viewing_user ||= current_user || begin
      if cookies[:identity]
        User.find_by_username(cookies[:identity])
      end
    end
  end

  def signed_in?
    !current_user.nil?
  end

  def sign_in(user)
    user.track_signin!
    session[:current_user]       = user.id
    cookies[:signedin]           = user.username # this cookie is used by js to show loggedin or not on cached HTML
    cookies.permanent[:identity] = user.username
    current_user.increment!(:login_count)
    current_user.last_ip = request.remote_ip
    current_user.last_ua = request.user_agent
    current_user.save
    ensure_and_reconcile_tracking_code #updated tracking code if appropriate.
    current_user
  end

  def mixpanel_cookie
    mp_cookie = cookies["mp_#{ENV['MIXPANEL_TOKEN']}_mixpanel"]
    if mp_cookie.present?
      JSON.parse(mp_cookie)
    else
      {}
    end
  end

  def ensure_and_reconcile_tracking_code
    if cookies[:trc].blank?
      session[:new_visit] = true
      cookies.permanent[:trc] = mixpanel_cookie['distinct_id']
    end

    if viewing_user
      session[:new_visit] = false
      if viewing_user.tracking_code.blank?
        viewing_user.reload.update_attribute(:tracking_code, cookies[:trc])
      else
        cookies.permanent[:trc] = viewing_user.tracking_code
      end
    end
  end

  def sign_out
    record_event("signed out")
    cookies.delete(:signedin)
    reset_session
  end

  def store_location!(url = nil)
    session[:return_to] = url || request.url
  end

  def require_registration
    if signed_in? && not_on_pages?
      redirect_to(edit_user_url(current_user)) if !current_user.valid?
    end
  end

  def show_achievement
    if signed_in? && not_on_achievements? && current_user.oldest_achievement_since_last_visit
      redirect_to user_achievement_url(username: current_user.username, id: current_user.oldest_achievement_since_last_visit.id)
    end
  end

  def record_visit
    if viewing_user
      if viewing_user == current_user && (viewing_user.try(:last_request_at) || 1.week.ago) < 1.day.ago && viewing_user.active? && viewing_user.last_refresh_at < 2.days.ago
        RefreshUserJob.perform_async(current_user.id)
      end
      viewing_user.visited!
      Usage.page_view(viewing_user.id) unless viewing_user.admin?
    end
  end

  def record_location
    if viewing_user && viewing_user.ip_lat.nil? && deployment_environment?
      ReverseGeolocateUserJob.perform_async(viewing_user.username, request.ip)
    end
  end

  def deployment_environment?
    Rails.env.production? or Rails.env.staging?
  end

  def destination_url
    if session[:return_to]
      session.delete(:return_to)
    elsif signed_in?
      if current_user.oldest_achievement_since_last_visit
        user_achievement_url(username: current_user.username, id: current_user.oldest_achievement_since_last_visit.id)
      elsif current_user.created_at > 5.minutes.ago
        badge_url(username: current_user.username)
      else
        root_url
      end
    else
      root_url
    end
  end

  def access_required
    redirect_to(root_url) unless signed_in?
  end

  def viewing_self?
    signed_in? && @user && @user == current_user
  end

  def not_on_pages?
    params[:controller] != 'pages'
  end

  def not_on_achievements?
    params[:controller] != 'achievements'
  end

  def render_404
    respond_to do |format|
      format.any(:html, :json, :xml) { render 'errors/not_found', status: :not_found }
      format.all { render text: "Not Found", :content_type => Mime::TEXT, status: :not_found }
    end
  end

  def render_500
    respond_to do |type|
      type.html { render file: File.join(Rails.root, 'public', '500.html'), layout: nil, status: 500 }
      type.all { render nothing: true, status: 500 }
    end
  end

  def require_admin!
    return head(:forbidden) unless is_admin?
  end

  def is_admin?
    signed_in? && current_user.role == 'admin'
  end

  def is_moderator?
    signed_in? && current_user.role.in?(%w(admin moderator))
  end

  def require_moderator!
    return head(:forbidden) unless is_moderator?
  end

  def iphone_user_agent?
    request.env["HTTP_USER_AGENT"] && request.env["HTTP_USER_AGENT"][/(Mobile\/.+Safari)/]
  end

  def round(number)
    if number < 10
      number.round
    elsif number < 100
      number.round(-1)
    elsif number < 1000
      number.round(-2)
    elsif number < 10000
      number.round(-3)
    else
      number.round(-Math.log(number, 10))
    end
  end

  def record_event(action_name, options = {})
    if !signed_in? || !current_user.admin?
      options.merge!('mp_name_tag' => cookies[:identity]) unless cookies[:identity].blank?
      options.merge!('distinct_id' => cookies[:trc]) unless cookies[:trc].blank?
      unless current_user.nil?
        options.merge!({ 'score'           => current_user.score.round(-1),
                         'followers'       => round(current_user.followers_count),
                         'achievements'    => current_user.badges_count,
                         'on team'         => current_user.on_team?,
                         'premium team'    => (current_user.team && current_user.team.premium?) || false,
                         'signed in'       => true,
                         'member'          => true,
                         'first visit'     => false,
                         'visit frequency' => current_user.visit_frequency })
      else
        options.merge!({ 'signed in'   => false,
                         'member'      => cookies[:identity] && User.exists?(username: cookies[:identity]),
                         'first visit' => session[:new_visit]
        })
      end

      #options.merge!('signed up on' => current_user.created_at.to_formatted_s(:mixpanel),
      #               'achievements' => current_user.badges_count) if signed_in?

    TrackEventJob.perform_async(action_name, options, request.ip) if ENABLE_TRACKING
    end
  rescue Exception => ex
    Rails.logger.error("MIXPANEL: Swallowing error when trying to record #{action_name}, #{ex.message}")
  end

  def session_id
    request.session_options[:id]
  end

  def ensure_domain
    if Rails.env.production?
      if request.env['HTTP_HOST'] != APP_DOMAIN
        redirect_to request.url.sub("//www.", "//"), status: 301
      end
    end
  end

  def track_utm
    session[:utm_campaign] = params[:utm_campaign] unless params[:utm_campaign].blank?
  end

  def ajax_redirect_to(uri)
    respond_to do |format|
      format.json do
        render json: {
          status: :redirect,
          to:     uri
        }.to_json
      end
    end
  end

  def redirect_to_path(path)
    if request.xhr?
      ajax_redirect_to(path)
    else
      redirect_to path
    end
  end

  def redirect_to_signup_if_unauthenticated(return_to=request.referer, message = "You must be signed in to do that", &block)
    if signed_in?
      yield
    else
      flash[:notice] = message
      #This is called when someone tries to do an action while unauthenticated
      Rails.logger.info "WILL RETURN TO #{return_to}"
      store_location!(return_to)
      redirect_to_path(signin_path)
    end
  end

  unless ENV['HTTP_BASICAUTH_ON'].blank?
    before_action :require_http_basic

    def require_http_basic
      authenticate_or_request_with_http_basic do |username, password|
        username == ENV['HTTP_BASICAUTH_USERNAME'] && password == ENV['HTTP_BASICAUTH_PASSWORD']
      end
    end
  end

end
