class HelpdeskWidgetController < ApplicationController
  unloadable
  layout false
  helper :custom_fields
  protect_from_forgery :except => [:widget, :load_form, :load_custom_fields, :avatar, :create_ticket, :iframe]
  skip_before_filter :check_if_login_required, :only => [:widget, :load_form, :load_custom_fields, :avatar, :create_ticket, :iframe]

  before_filter :prepare_data, :only => [:load_custom_fields, :create_ticket]
  after_filter :set_access_control_header

  def load_form
    render :json => schema.to_json
  end

  def load_custom_fields
    @issue = @project.issues.build(:tracker => @tracker) if @tracker
    @enabled_cf = HelpdeskSettings["helpdesk_widget_available_custom_fields", nil]
  end

  def avatar
    user = User.where(:login => params[:login]).first
    return render :nothing => true, :status => 404 unless user
    if user.try(:avatar).nil?
      avatar_thumb, avatar_type = gravatar_avatar(user) if Setting.gravatar_enabled?
    else
      avatar_thumb, avatar_type = local_avatar(user.avatar)
    end
    return render :nothing => true, :status => 404 unless avatar_thumb
    send_avatar(avatar_thumb, avatar_type)
  end

  def send_avatar(avatar_thumb, avatar_type)
    send_file avatar_thumb, :filename => (request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(params[:login]) : params[:login]),
                            :type => avatar_type,
                            :disposition => 'inline'
  end

  def gravatar_avatar(user)
    email = user.mail if user.respond_to?(:mail)
    email = user.to_s[/<(.+?)>/, 1] unless email
    return [nil, nil] unless email
    default = Setting.gravatar_default ? CGI::escape(Setting.gravatar_default) : ''
    temp_file = Tempfile.new([user.login, '.jpeg'])
    temp_file.binmode
    open("http://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(email)}?rating=PG&size=54&default=#{default}") do |url_file|
      temp_file.write(url_file.read)
    end
    temp_file.rewind
    [temp_file, 'image/jpeg']
  end

  def local_avatar(user_avatar)
    return nil unless user_avatar.readable? || user_avatar.thumbnailable?
    if (defined?(RedmineContacts::Thumbnail) == 'constant') && Redmine::Thumbnail.convert_available?
      target = File.join(user_avatar.class.thumbnails_storage_path, "#{user_avatar.id}_#{user_avatar.digest}_54x54.thumb")
      thumbnail = RedmineContacts::Thumbnail.generate(user_avatar.diskfile, target, '54x54')
    elsif Redmine::Thumbnail.convert_available?
      thumbnail = user_avatar.thumbnail(:size => '54x54')
    else
      thumbnail = user_avatar.diskfile
    end
    [thumbnail, detect_content_type(user_avatar)]
  end

  def create_ticket
    @issue = prepare_issue
    @issue.helpdesk_ticket = prepare_helpdesk_ticket
    result =
    if valid_email? && @issue.save
      save_attachment(@issue)
      HelpdeskMailer.auto_answer(@issue.helpdesk_ticket.customer, @issue).deliver if HelpdeskSettings["helpdesk_send_notification", @project].to_i > 0
      { :result => true, :errors => [] }
    else
      { :result => false, :errors => prepared_errors }
    end
    render :json => result
  end

  private

  def prepare_data
    @project = Project.find(params[:project_id])
    @tracker = @project.trackers.where(:id => params[:tracker_id]).first
  end

  def schema
    if HelpdeskSettings["helpdesk_widget_enable", nil].to_i > 0
      projects = Project.has_module('contacts_helpdesk').where(:id => HelpdeskSettings[:helpdesk_widget_available_projects, nil])
    else
      projects = []
    end
    data_schema = {}
    data_schema[:projects] = Hash[projects.map { |project| [project.name.capitalize, project.id] }]
    data_schema[:projects_data] = {}
    projects.each do |project|
      data_schema[:projects_data][project.id] = {}
      if HelpdeskSettings["helpdesk_tracker", project] && HelpdeskSettings["helpdesk_tracker", project] != 'all'
        data_schema[:projects_data][project.id][:trackers] = Hash[Tracker.where(id: HelpdeskSettings["helpdesk_tracker", project])
                                                                         .map { |tracker| [tracker.name, tracker.id] }]
      else
        data_schema[:projects_data][project.id][:trackers] = Hash[project.trackers.map { |tracker| [tracker.name, tracker.id] }]
      end
    end
    data_schema[:custom_fields] = Hash[IssueCustomField.where(id: HelpdeskSettings["helpdesk_widget_available_custom_fields", nil])
                                                       .map { |custom_field| [custom_field.name, custom_field.id] }]
    data_schema[:avatar] = HelpdeskSettings[:helpdesk_widget_avatar_login, nil]
    data_schema
  end

  def prepared_errors
    errors_hash = @issue.errors.dup
    # Username
    if errors_hash[:'helpdesk_ticket.customer.first_name'].present?
      @issue.errors.delete(:'helpdesk_ticket.customer.first_name')
      @issue.errors[:username] = errors_hash[:'helpdesk_ticket.customer.first_name'].collect { |error| ['Username', error].join(' ') }
    end

    # Subject
    if errors_hash[:subject].present?
      errors = errors_hash[:subject].collect { |error| ['Subject', error].join(' ') }
      @issue.errors[:subject].clear
      @issue.errors[:subject] = errors
    end

    # Description
    if params[:issue][:description].empty?
      @issue.errors[:description] = I18n.t(:label_helpdesk_widget_ticket_error_description)
    end

    # Nested objects
    if errors_hash[:'helpdesk_ticket.customer.projects'].present?
      @issue.errors.delete(:'helpdesk_ticket.customer.projects')
    end
    @issue.errors
  end

  def prepare_issue
    redmine_user = User.where(id: params[:redmine_user]).first
    author = redmine_user.present? && redmine_user.allowed_to?(:edit_helpdesk_tickets, @project) ? redmine_user : User.anonymous
    issue = @project.issues.build(:tracker => @tracker, :author => author)
    issue.safe_attributes = params[:issue].deep_dup
    issue.assigned_to = widget_contact.find_assigned_user(@project, HelpdeskSettings["helpdesk_assigned_to", @project])
    issue
  end

  def prepare_helpdesk_ticket
    HelpdeskTicket.new(:from_address => params[:email],
                       :ticket_date  => Time.now,
                       :customer => widget_contact,
                       :issue => @issue,
                       :source => HelpdeskTicket::HELPDESK_WEB_SOURCE)
  end

  def save_attachment(issue)
    return unless params[:attachment].present?
    attachment_hash = split_base64(params[:attachment])
    attachment = Attachment.new(file: Base64.decode64(attachment_hash[:data]))
    attachment.filename = params[:attachment_name] || [Redmine::Utils.random_hex(16), attachment_hash[:extension]].join('.')
    attachment.content_type = attachment_hash[:type]
    attachment.author = User.anonymous
    issue.attachments << attachment
    issue.save
  end

  def split_base64(uri)
    matcher = uri.match(/^data:(.*?)\;(.*?),(.*)$/)
    { type:      matcher[1],
      encoder:   matcher[2],
      data:      matcher[3],
      extension: matcher[1].split('/')[1] }
  end

  def widget_contact
    return @widget_contact if @widget_contact
    contacts = Contact.find_by_emails([params[:email]])
    return @widget_contact = contacts.first if contacts.any?
    @widget_contact = Contact.new(:email => params[:email])
    @widget_contact.first_name, @widget_contact.last_name = params[:username].split(' ')
    @widget_contact.projects << @project
    @widget_contact
  end

  def set_access_control_header
    headers['Access-Control-Allow-Origin'] = '*'
    headers['X-Frame-Options'] = '*'
  end

  def valid_email?
    if params[:email].empty?
      @issue.errors[:email] = 'Email cannot be empty'
      return false
    elsif params[:email].match(/\A([\w\.\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i).nil?
      @issue.errors[:email] = 'Email is incorrect'
      return false
    end
    true
  end

  def detect_content_type(attachment)
    content_type = attachment.content_type
    if content_type.blank?
      content_type = Redmine::MimeType.of(attachment.filename)
    end
    content_type.to_s
  end
end
