class UserController < ApplicationController
  layout "default"
  before_action :blocked_only, only: [ :authenticate, :update, :edit, :modify_blacklist ]
  before_action :janitor_only, only: [ :invites ]
  before_action :mod_only, only: [ :block, :unblock, :show_blocked_users ]
  before_action :post_member_only, only: [ :set_avatar ]
  before_action :no_anonymous, only: [ :change_password, :change_email, :show ]
  helper :post, :tag_subscription
  helper :avatar

  protected

  def save_cookies(user)
    @current_user = user
    session[:user_id] = user.id
  end

  public

  def autocomplete_name
    keyword = params[:term].to_s
    @users = User.where([ "name ILIKE ?", "*#{keyword}*".to_escaped_for_sql_like ]).order("LENGTH(name)", :name).limit(20).pluck(:name) if keyword.length >= 2
    respond_to do |format|
      format.json { render json: (@users || []) }
    end
  end

  # FIXME: this method is crap and only function as temporary workaround
  #        until I convert the controllers to resourceful version which is
  #        planned for 3.2 branch (at least 3.2.1).
  def remove_avatar
    # When removing other user's avatar, ensure current user is mod or higher.
    if @current_user.id != params[:id] && !@current_user.is_mod_or_higher?
      access_denied
      return
    end
    @user = User.find(params[:id])
    @user.avatar_post_id = nil
    if @user.save
      flash[:notice] = "Avatar removed"
    else
      flash[:notice] = "Failed removing avatar"
    end
    redirect_to action: :show, id: params[:id]
  end

  def change_password
    @title = "Change Password"
    respond_to { |format| format.html { render layout: "settings" } }
  end

  def change_email
    @title = "Change Email"
    @current_user.current_email = @current_user.email
    respond_to { |format| format.html { render layout: "settings" } }
  end

  def show
    if params[:name]
      @user = User.find_by_name(params[:name])
    else
      @user = User.find(params[:id])
    end

    if @user.nil?
      redirect_to "/404"
    end
    if @current_user.is_mod_or_higher?
      @user_ips = @user.user_logs.order("created_at DESC").pluck("ip_addr").uniq
    end
    respond_to do |format|
      format.html
    end
  end

  def invites
    if request.post?
      if params[:member]
        begin
          @current_user.invite!(params[:member][:name], params[:member][:level])
          flash[:notice] = "User was invited"

        rescue ActiveRecord::RecordNotFound
          flash[:notice] = "Account not found"

        rescue User::NoInvites
          flash[:notice] = "You have no invites for use"

        rescue User::HasNegativeRecord
          flash[:notice] = "This use has a negative record and must be invited by an admin"
        end
      end

      redirect_to action: "invites"
    else
      @invited_users = User.where(invited_by: @current_user.id).order("name_normalized")
    end
  end

  def home
  end

  def index
    @users = User.with_params(params).paginate(per_page: 20, page: page_number)
    respond_to_list("users")
  end

  def authenticate
    save_cookies(@current_user)

    if params[:url].is_a?(String) && params[:url][0] == "/"
      path = params[:url]
    end

    path ||= { action: "home" }

    respond_to_success("You are now logged in", path)
  end

  # TODO: merge with .authenticate
  def check
    user = User.find_by_name(params[:username])
    ret = { exists: false }
    ret[:name] = params[:username]

    unless user
      respond_to_success("User does not exist", {}, api: { response: "unknown-user" }.merge(ret))
      return
    end

    # Return some basic information about the user even if the password isn't given, for
    # UI cosmetics.
    ret[:exists] = true
    ret[:id] = user.id
    ret[:name] = user.name
    ret[:no_email] = user.email.blank?

    user = User.authenticate(params[:username], params[:password] || "")
    unless user
      respond_to_success("Wrong password", {}, api: { response: "wrong-password" }.merge(ret))
      return
    end

    save_cookies(user)
    ret[:pass_hash] = user.password_hash
    ret[:user_info] = user.user_info_cookie
    respond_to_success("Successful", {}, api: { response: "success" }.merge(ret))
  end

  def login
    respond_to { |format| format.html }
  end

  def create
    user = User.create(user_params_for_create)

    if user.errors.empty?
      save_cookies(user)

      ret = { exists: false }
      ret[:name] = user.name
      ret[:id] = user.id
      ret[:pass_hash] = user.password_hash
      ret[:user_info] = user.user_info_cookie

      respond_to_success("New account created", { action: "home" }, api: { response: "success" }.merge(ret))
    else
      error = user.errors.full_messages.join(", ")
      respond_to_success("Error: " + error, { action: "signup" }, api: { response: "error", errors: user.errors.full_messages })
    end
  end

  def signup
    @user = User.new
  end

  def logout
    reset_session

    flash[:notice] = "You are now logged out"

    respond_to do |format|
      format.js
    end
  end

  def update
    if params[:commit] == "Cancel"
      redirect_to action: "home"
      return
    end

    if @current_user.update(user_params_for_update)
      respond_to_success("Account settings saved", action: "edit")
    else
      if params[:render] && params[:render][:view]
        render get_view_name_for_edit(params[:render][:view])
      else
        respond_to_error(@current_user, action: "edit")
      end
    end
  end

  def modify_blacklist
    added_tags = params[:add] || []
    removed_tags = params[:remove] || []

    tags = @current_user.blacklisted_tags_array
    added_tags.each do |tag|
      tags << tag unless tags.include?(tag)
    end

    tags -= removed_tags

    if @current_user.update(blacklisted_tags: tags.join("\n"))
      respond_to_success("Tag blacklist updated", { action: "home" }, api: { result: @current_user.blacklisted_tags_array })
    else
      respond_to_error(@current_user, action: "edit")
    end
  end

  def remove_from_blacklist
  end

  def edit
    @user = @current_user
    render layout: "settings"
  end

  def reset_password
    if request.post?
      @user = User.find_by_name(params[:user][:name])

      if @user.nil?
        respond_to_error("That account does not exist", { action: "reset_password" }, api: { result: "unknown-user" })
        return
      end

      if @user.email.blank?
        respond_to_error("You never supplied an email address, therefore you cannot have your password automatically reset",
                         { action: "login" }, api: { result: "no-email" })
        return
      end

      if @user.email != params[:user][:email]
        respond_to_error("That is not the email address you supplied",
                         { action: "login" }, api: { result: "wrong-email" })
        return
      end

      begin
        User.transaction do
          # If the email is invalid, abort the password reset
          new_password = @user.reset_password
          UserMailer.new_password(@user, new_password).deliver_now
          respond_to_success("Password reset. Check your email in a few minutes.",
                             { action: "login" }, api: { result: "success" })
        end
      rescue Net::SMTPSyntaxError, Net::SMTPFatalError
        respond_to_success("Your email address was invalid",
                           { action: "login" }, api: { result: "invalid-email" })
      end
    else
      @user = User.new
      redirect_to root_path if params[:format] && params[:format] != "html"
    end
  end

  def block
    @user = User.find(params[:id])

    if request.post?
      if @user.is_mod_or_higher?
        flash[:notice] = "You can not ban other moderators or administrators"
        redirect_to action: "block"
        return
      end

      Ban.create(ban_params.merge(banned_by: @current_user.id, user_id: params[:id]))
      redirect_to action: "show_blocked_users"
    else
      @ban = Ban.new(user_id: @user.id, duration: "1")
    end
  end

  def unblock
    params[:user].keys.each do |user_id|
      Ban.where(user_id: user_id).destroy_all
    end

    redirect_to action: "show_blocked_users"
  end

  def show_blocked_users
    @users = User.includes(:ban).joins(:ban).order("bans.expires_at ASC")
    @ip_bans = IpBans.all
  end

  if CONFIG["enable_account_email_activation"]
    def resend_confirmation
      if request.post?
        user = User.find_by_email(params[:email])

        if user.nil?
          flash[:notice] = "No account exists with that email"
          redirect_to action: "home"
          return
        end

        if user.is_blocked_or_higher?
          flash[:notice] = "Your account is already activated"
          redirect_to action: "home"
          return
        end

        UserMailer.confirmation_email(user).deliver_now
        flash[:notice] = "Confirmation email sent"
        redirect_to action: "home"
      end
    end

    def activate_user
      flash[:notice] = "Invalid confirmation code"

      users = User.where(level: CONFIG["user_levels"]["Unactivated"])
      users.each do |user|
        if User.confirmation_hash(user.name) == params["hash"]
          user.update(level: CONFIG["starting_level"])
          flash[:notice] = "Account has been activated"
          break
        end
      end

      redirect_to action: "home"
    end
  end

  def set_avatar
    @user = @current_user
    if params[:user_id]
      @user = User.find(params[:user_id])
      respond_to_error("Not found", action: "index", status: 404) unless @user
    end

    if !@user.is_anonymous? && !@current_user.has_permission?(@user, :id)
      access_denied
      return
    end

    if request.post?
      if @user.set_avatar(params)
        redirect_to action: "show", id: @user.id
      else
        respond_to_error(@user, action: "home")
      end
    end

    if !@user.is_anonymous? && params[:id] == @user.avatar_post_id
      @old = params
    end

    @params = params
    @post = Post.find(params[:id])
  end

  private

  def get_view_name_for_edit(param)
    case param
    when "change_email"
      :change_email
    when "change_password"
      :change_password
    else
      :edit
    end
  end

  def ban_params
    params.require(:ban).permit(:reason, :duration)
  end

  def user_params_for_create
    params.require(:user).permit(:name, :email, :password, :password_confirmation, :blacklisted_tags, :always_resize_images, :receive_dmails, :show_samples, :use_browser, :show_advanced_editing, :pool_browse_mode)
  end

  def user_params_for_update
    params.require(:user).permit(:email, :current_password, :password, :password_confirmation, :blacklisted_tags, :always_resize_images, :receive_dmails, :show_samples, :use_browser, :show_advanced_editing, :pool_browse_mode)
  end
end
