class EasyAttendancesController < ApplicationController

  before_action :find_easy_attendance, :only => [:show, :edit, :update, :destroy, :departure]
  before_action :authorize_global, :except => [:change_activity]
  before_action :ensure_bulk_create, :only => [:create]
  before_action :build_easy_attendance, :only => [:create, :update, :change_activity]
  before_action :enabled_this
  before_action :load_journals, :only => [:update, :edit]

  accept_api_auth :show, :create, :index, :update, :destroy
  accept_rss_auth :index

  helper :easy_query
  include EasyQueryHelper
  helper :sort
  include SortHelper
  helper :journals
  include JournalsHelper
  helper :easy_journal
  include EasyJournalHelper

  include EasyIcalHelper

  helper :easy_attendances
  include EasyAttendancesHelper

  include EasyUtils::DateUtils

  def overview
    render_action_as_easy_page(EasyPage.find_by(page_name: 'easy-attendances-overview'), nil, nil, easy_attendances_overview_path(t: params[:t]), false, {})
  end

  def layout
    render_action_as_easy_page(EasyPage.find_by(page_name: 'easy-attendances-overview'), nil, nil, easy_attendances_overview_path(t: params[:t]), true, {})
  end

  # POST => :create; GET => :list
  def index
    retrieve_query(EasyAttendanceQuery, { :use_session_store => true })
    if in_mobile_view? && (params[:tab].nil? || params[:tab] == 'calendar')

      params[:tab] = 'list'

    elsif !@query.new_record? && params[:tab].nil?
      params[:tab] = @query.outputs.include?('list') ? 'list' : 'calendar'
    end

    @user_ids = @query.filters['user_id'].try(:[], :values).to_a
    @query.output = params[:tab]

    if params[:tab] == 'list' || api_request?
      @query.default_list_columns << 'user'
      @query.display_outputs_select_on_index = false

      sort_init(@query.sort_criteria_init)
      sort_update(@query.sortable_columns)

      @attendances = prepare_easy_query_render

      respond_to do |format|
        format.html {
          unless @entities
            render_404
            return false
          end

          render_easy_query_html
        }
        format.csv  { send_data(export_to_csv(@attendances, @query), :filename => get_export_filename(:csv, @query))}
        format.pdf  { send_file_headers! :type => 'application/pdf', :filename => get_export_filename(:pdf, @query) }
        format.xlsx { send_data(export_to_xlsx(@attendances, @query), :filename => get_export_filename(:xlsx, @query))}
        format.atom { render_feed(@entities, { template: 'easy_attendances/attendance_feed', title: "#{Setting.app_title}: #{l(:label_easy_attendance_plural)}" } ) }
        format.ics  { send_data(easy_attendances_to_ical(@entities), :filename => get_export_filename(:ics, @query), :type => Mime[:ics].to_s+'; charset=utf-8') }
        format.api  {
          @entity_count = @query.entity_count
          @offset, @limit = api_offset_and_limit
        }
      end
    elsif params[:tab].nil? || params[:tab] == 'calendar'
      @query.display_filter_columns_on_index = false
      @query.display_filter_group_by_on_index = false
      @query.display_filter_sort_on_index = false
      @query.display_filter_settings_on_index = false

      unless params[:start_date].blank?
        @start_date = begin; params[:start_date].to_date; rescue; end
      end

      if @start_date
        @query.filters.delete('departure')
        @query.filters['arrival'] = HashWithIndifferentAccess.new(:operator => 'date_period_2', :values => HashWithIndifferentAccess.new(:from => @start_date.beginning_of_month, :to => @start_date.end_of_month, :period => 'current_month'))
      end

      @query.export_formats = {}

      @entities = @query.entities(:order => :arrival)
      @start_date ||= @entities.last.arrival.localtime.to_date  if @entities.any?
      @start_date ||= User.current.today

      @calendar = EasyAttendances::Calendar.new(@start_date, current_language, :month)

      @query.group_by = nil
      if @query.valid?
        @calendar.events = @entities
      end
      if @user_ids.any?
        if @user_ids.size == 1
          user_id = @user_ids.first
        end
        user_id = User.current.id if @user_ids.include?('me')

        @easy_user_working_time_calendar = user_id && EasyUserWorkingTimeCalendar.where(:user_id => user_id).first || EasyUserWorkingTimeCalendar.default
      end
      respond_to do |format|
        format.html
      end
    else
      render_406
    end
  end

  def report
    @modul_uniq_id = params[:uuid] || 'main'
    @hide_form = params[:hide_form] == '1' || false

    params[:tab] = 'report'

    @saved_params = params[:report] || {}
    @saved_params[:period_type] ||= '2'
    @saved_params[:from] ||= Date.today.beginning_of_month
    @saved_params[:to] ||= Date.today

    date_range = get_date_range(@saved_params[:period_type], @saved_params[:period], @saved_params[:from], @saved_params[:to], @saved_params[:period_days])
    @from, @to = date_range[:from], date_range[:to]

    @activities = EasyAttendanceActivity.sorted
    @user_ids = @saved_params[:users]

    @assignable_users_and_groups = []
    if User.current.allowed_to?(:view_easy_attendance_other_users, nil, :global => true)
      @assignable_users = User.active.non_system_flag.sorted
      if @assignable_users.include?(User.current)
        @assignable_users_and_groups << ["<< #{l(:label_me)} >>".html_safe, User.current.id]
      end
      @assignable_users_and_groups.concat(@assignable_users.collect{|m| [m.name, m.id]})
      @assignable_users_and_groups.concat(Group.active.non_system_flag.order(:lastname).collect{|m| [m.name, m.id]})
    end

    if !@saved_params[:users].blank? && User.current.allowed_to?(:view_easy_attendance_other_users, nil, :global => true)
      @selected_user_ids = Array.new
      @saved_params[:users].each do |id|
        p = Principal.find_by_id(id)
        if p.is_a?(User)
          @selected_user_ids << p.id
        elsif p.is_a?(Group)
          @selected_user_ids.concat(p.users.pluck(:id))
        end
      end
      @selected_user_ids.uniq!
    end

    @reports = Array.new
    User.active.non_system_flag.sorted.where(:id => @selected_user_ids || User.current.id).each do |user|
      @reports << EasyAttendanceReport.new(user, @from, @to)
    end

    respond_to do |format|
      format.html
      format.js
    end
  end

  def detailed_report
    retrieve_query(EasyAttendanceUserQuery)
    sort_init(@query.sort_criteria_init)
    sort_update(@query.sortable_columns)
    @query.output = 'list'
    @query.display_outputs_select_on_index = false

    prepare_easy_query_render(nil, limit: nil)

    if request.xhr? && @entity_pages.last_page.to_i < params['page'].to_i
      render_404
      return false
    end
    respond_to do |format|
      format.html {
        render_easy_query
      }
      format.api
      format.csv  { send_data(export_to_csv(@entities, @query), :filename => get_export_filename(:csv, @query))}
      format.pdf  { send_data(export_to_pdf(@entities, @query), :filename => get_export_filename(:pdf, @query))}
      format.xlsx { send_data(export_to_xlsx(@entities, @query), :filename => get_export_filename(:xlsx, @query))}
    end
  end

  def show
    respond_to do |format|
      format.html {render :nothing => true}
      format.api
    end
  end

  def new
    arrival = begin params[:arrival_at].to_date; rescue; User.current.today end

    @easy_attendance = EasyAttendance.new(:user => User.current)
    @easy_attendance.attendance_date = arrival
    @easy_attendance.arrival = @easy_attendance.morning(arrival)
    @easy_attendance.departure = @easy_attendance.evening(arrival)
    @easy_attendance.easy_attendance_activity = EasyAttendanceActivity.default
    @easy_attendance.range = EasyAttendance::RANGE_FULL_DAY

    @easy_attendance_activities = EasyAttendanceActivity.sorted.all

    respond_to do |format|
      format.js
      format.html
    end
  end

  def arrival
    @only_arrival = true
    @easy_attendance = EasyAttendance.new(:user => User.current)
    @easy_attendance.arrival = Time.now
    @easy_attendance.easy_attendance_activity = EasyAttendanceActivity.default
    @easy_attendance_activities = EasyAttendanceActivity.where(:at_work => true).sorted

    respond_to do |format|
      format.js
    end
  end

  def departure
    if EasyAttendance.create_departure(@easy_attendance, current_user_ip)
      flash[:notice] = l(:notice_easy_attendance_departured, :at => format_time(@easy_attendance.departure))
      redirect_back_or_default easy_attendances_path
    else
      @easy_attendance_activities = EasyAttendanceActivity.where(:at_work => true).sorted
      render 'edit'
    end
  end

  def create
    @easy_attendance.current_user_ip = current_user_ip
    ensure_easy_attendance_non_work_activity

    if @easy_attendance.errors.blank? && @easy_attendance.save
      @easy_attendance.after_create_send_mail
      respond_to do |format|
        format.html do
          flash[:notice] = l(:notice_successful_create)
          redirect_back_or_default({:controller => 'easy_attendances', :action => 'index'})
        end
        format.api {render :action => 'show'}
      end
    else
      @easy_attendance_activities = EasyAttendanceActivity.sorted.all
      respond_to do |format|
        format.html {render :action => 'new'}
        format.api {render_validation_errors(@easy_attendance)}
      end
    end
  end

  def bulk_create
    @user_ids = params[:easy_attendance][:user_id]
    error = false
    EasyAttendance.transaction do
      @user_ids.each do |user_id|
        params[:easy_attendance][:user_id] = user_id
        build_easy_attendance
        @easy_attendance.current_user_ip = current_user_ip
        ensure_easy_attendance_non_work_activity
        stash_for_delivery = @easy_attendance.dup
        if @easy_attendance.errors.blank? && @easy_attendance.save
          @easy_attendance.after_create_send_mail
          @easy_attendance = nil
        else
          error = true
          raise ActiveRecord::Rollback
          break
        end
      end
    end

    if error
      @easy_attendance_activities = EasyAttendanceActivity.sorted.all
      respond_to do |format|
        format.html {render :action => 'new'}
      end
    else
      respond_to do |format|
        format.html do
          flash[:notice] = l(:notice_successful_create)
          redirect_back_or_default({:controller => 'easy_attendances', :action => 'index'})
        end
      end
    end
  end

  def edit
    @easy_attendance.arrival = Time.now if @easy_attendance.arrival.blank?
    @easy_attendance_activities = EasyAttendanceActivity.sorted.all
    if @easy_attendance.departure.blank?
      if @easy_attendance.arrival.nil?
        @easy_attendance.departure = Time.now
      else
        arrival = @easy_attendance.arrival.localtime
        @easy_attendance.departure = Time::local(arrival.year, arrival.month, arrival.day, Time.now.hour, Time.now.min)
      end
    end
  end

  def update
    @easy_attendance.init_journal(User.current, params[:notes])
    @easy_attendance.current_user_ip = current_user_ip
    ensure_easy_attendance_non_work_activity

    if @easy_attendance.errors.blank? && @easy_attendance.save
      @easy_attendance.after_update_send_mail if @easy_attendance.previous_changes.any?
      respond_to do |format|
        format.html do
          flash[:notice] = l(:notice_successful_update)
          redirect_back_or_default({:controller => 'easy_attendances', :action => 'index', :tab => params[:tab]})
        end
        format.api  { render_api_ok }
      end
    else
      respond_to do |format|
        format.html do
          load_journals
          @easy_attendance_activities = EasyAttendanceActivity.sorted.all
          render :action => 'edit'
        end
        format.api { render_validation_errors(@easy_attendance) }
      end
    end
  end

  def bulk_update
    @easy_attendances = EasyAttendance.where(:id => params[:ids])
    @easy_attendance_activities = EasyAttendanceActivity.sorted.all
    attributes = parse_params_for_bulk_entity_attributes(params[:easy_attendance])
    errors = Array.new
    approval_stash = []
    @easy_attendances.each do |easy_attendance|
      easy_attendance.safe_attributes = attributes
      if easy_attendance.save
        approval_stash << easy_attendance if easy_attendance.easy_attendance_activity.approval_required? && easy_attendance.previous_changes.any?
      else
        errors << "##{easy_attendance.id} : #{easy_attendance.errors.full_messages.join(', ')}"
      end
    end
    if errors.blank?
      flash[:notice] = l(:notice_successful_update)
    else
      flash[:error] = (l(:error_bulk_update_save, :count => errors.size) + '<br>' + errors.join('<br>')).html_safe
    end
    EasyAttendance.deliver_pending_attendances(approval_stash)
    redirect_back_or_default({:controller => 'easy_attendances', :action => 'index', :tab => 'list'})
  end

  def bulk_cancel
    @easy_attendances = EasyAttendance.where(:id => params[:ids])
    errors = []
    cancel_requests_email_queue = []
    canceled_attendances_email_queue = []

    @easy_attendances.each do |easy_attendance|
      direct_cancel = easy_attendance.direct_cancel?
      cancel_requested = easy_attendance.cancel_request

      if cancel_requested && !direct_cancel
        cancel_requests_email_queue << easy_attendance
      elsif cancel_requested && direct_cancel && easy_attendance.user != User.current
        canceled_attendances_email_queue << easy_attendance
      elsif !cancel_requested
        errors << "##{easy_attendance.id} : #{l(:error_cannot_cancel_attendance)}"
      end
    end
    EasyAttendance.deliver_pending_attendances(cancel_requests_email_queue) if cancel_requests_email_queue.present?
    EasyAttendance.deliver_approval_response(canceled_attendances_email_queue, nil) if canceled_attendances_email_queue.present?

    if errors.blank?
      flash[:notice] = l(:notice_successful_update)
    else
      flash[:error] = (l(:error_bulk_update_save, :count => errors.size) + '<br>' + errors.join('<br>')).html_safe
    end
    redirect_back_or_default({:controller => 'easy_attendances', :action => 'index', :tab => params[:tab]})
  end

  def destroy
    EasyAttendance.delete_easy_attendances([@easy_attendance])

    respond_to do |format|
      format.html do
        flash[:notice] = l(:notice_successful_delete)
        redirect_back_or_default({:controller => 'easy_attendances', :action => 'index', :tab => params[:tab]})
      end
      format.api  { render_api_ok }
    end
  end

  def bulk_destroy
    @easy_attendances = EasyAttendance.where(:id => params[:ids])
    EasyAttendance.delete_easy_attendances(@easy_attendances)

    flash[:notice] = l(:notice_successful_delete)
    redirect_back_or_default({:controller => 'easy_attendances', :action => 'index', :tab => 'list'})
  end

  # RESTFUL END


  def load_journals
    @journals = @easy_attendance.journals.preload(:journalized, :user, :details).reorder("#{Journal.table_name}.id ASC").to_a
    @journals.reverse! if User.current.wants_comments_in_reverse_order?
  end

  def change_activity
    @activity = @easy_attendance.easy_attendance_activity
    @easy_attendance.range ||= EasyAttendance::RANGE_FULL_DAY
    @easy_attendance.attendance_date ||= (@easy_attendance.arrival || User.current.today).to_date
    respond_to do |format|
      format.js
    end
  end

  def new_notify_after_arrived
    @me = User.current
    @user = User.find(params[:user_id])
    @easy_attendance_notify_count = EasyAttendanceUserArrivalNotify.where(:user_id => @user.id, :notify_to_id => @me.id).count
  end

  def create_notify_after_arrived
    @me = User.current
    @user = User.find(params[:user_id])
    EasyAttendanceUserArrivalNotify.create(:user_id => @user.id, :notify_to_id => @me.id, :message => params[:notify_message])

    redirect_to @user, :notice => l(:notice_successful_create)
  end

  # This should be part of EasyBackgroundService now
  def statuses
    result = {}
    if EasyAttendance.enabled?
      users = User.where(id: Array(params[:user_ids])).to_a

      User.load_current_attendance(users)
      User.load_last_today_attendance_to_now(users)

      users.each do |user|
        result[user.id] = easy_attendance_user_status_indicator(user)
      end
    end

    render json: result
  end

  def approval
    @easy_attendances = EasyAttendance.joins(:easy_attendance_activity).where(id: params[:ids], easy_attendance_activities: {approval_required: true})
    @approve = params[:approve].present? ? params[:approve].to_i : 1
    @title = l("approval-#{@approve}", :scope => :easy_attendance)

    respond_to do |format|
      format.js
      format.html
    end
  rescue ActiveRecord::RecordNotFound
    render_404
  end

  def approval_save
    attendances = EasyAttendance.approve_attendances(params[:ids], params[:approve], params[:notes])
    if attendances[:saved].empty?
      if attendances[:unsaved].empty?
        flash[:error] = l(:not_possible_to_approve_all_attendances, scope: :easy_attendance).html_safe
      else
        flash[:error] = attendances[:unsaved].map{|a| a.errors.full_messages}.flatten.join('<br>').html_safe
      end
    end
    redirect_back_or_default(easy_attendances_path(:tab => 'list'))
  rescue ActiveRecord::RecordNotFound
    render_404
  end

  private

  def ensure_bulk_create
    if params[:easy_attendance] && params[:easy_attendance][:user_id].is_a?(Array)
      if params[:easy_attendance][:user_id].count > 1
        return bulk_create
      else
        params[:easy_attendance][:user_id] = params[:easy_attendance][:user_id].first
      end
    end
  end

  def find_easy_attendance
    @easy_attendance = EasyAttendance.find(params[:id])
  rescue ActiveRecord::RecordNotFound
    render_404
  end

  def build_easy_attendance
    @easy_attendance ||= EasyAttendance.new
    @easy_attendance.safe_attributes = params[:easy_attendance]
    @easy_attendance.user_id ||= User.current.id
    @easy_attendance.arrival = params[:arrival] if params[:arrival].present?
    @easy_attendance.departure = params[:departure] if params[:departure].present?
    @easy_attendance.non_work_start_time = params[:non_work_start_time].presence
    @easy_attendance.approval_status ||= params[:approval_status] if params[:approval_status].present?
  end

  def enabled_this
    unless EasyAttendance.enabled?
      render_403
    end
  end

  def authorize_my_attendance
    return @easy_attendance.can_edit?
  end

  def validate_attendance_attribute(attr)
    i = @easy_attendance.send(attr)
    i.nil? ? (@easy_attendance.errors.add(attr, :blank); nil) : i
  end

  def shift_working_day(time, uwtc, reverse = false, max_shift = 66)
    max_shift.times do
      if uwtc.working_day?(time.to_date)
        return time
      else
        reverse ? time -= 1.day : time += 1.day
      end
    end
    @easy_attendance.errors.add(:base, l(:error_working_days))
    nil
  end

  def ensure_easy_attendance_non_work_activity
    if @easy_attendance.range && @easy_attendance.easy_attendance_activity && (user = @easy_attendance.user)
      uwtc = user.current_working_time_calendar
      return unless (ta = validate_attendance_attribute(:arrival))
      return unless (td = validate_attendance_attribute(:departure))
      ta, td = user.user_time_in_zone(ta), user.user_time_in_zone(td)
      if td > ta
        working_days_between = uwtc.working_days(ta.to_date, td.to_date)
      else
        working_days_between = uwtc.working_days(td.to_date, ta.to_date)
      end
      if working_days_between == 0
        return @easy_attendance.errors.add(:base, l(:error_not_a_working_day, :scope => :easy_attendance))
      end
      ta, td = shift_working_day(ta, uwtc), shift_working_day(td, uwtc, true)
      return if ta.nil? || td.nil?

      ta = user.user_civil_time_in_zone(ta.year, ta.month, ta.day, uwtc.time_from.hour, uwtc.time_from.min)
      @easy_attendance.arrival = ta
      @easy_attendance.departure = user.user_civil_time_in_zone(td.year, td.month, td.day, uwtc.time_to.hour, uwtc.time_to.min)

      case @easy_attendance.range
      when EasyAttendance::RANGE_FULL_DAY
        na = ta + uwtc.working_hours(ta.to_date).hours
      when EasyAttendance::RANGE_FORENOON, EasyAttendance::RANGE_AFTERNOON
        na = if @easy_attendance.non_work_start_time
          @easy_attendance.arrival = user.user_time_in_zone(@easy_attendance.non_work_start_time)
        elsif @easy_attendance.range == EasyAttendance::RANGE_AFTERNOON
          @easy_attendance.arrival = ta + (uwtc.working_hours(ta.to_date) / 2.0).hours
        else
          ta
        end
        na += (uwtc.working_hours(na.to_date) / 2.0).hours
      else
        raise 'Invalid EasyAttendance RANGE !!!'
      end
      @easy_attendance.departure = user.user_civil_time_in_zone(td.year, td.month, td.day, na.hour, na.min)
    end
  end

end
