require 'geocoder/calculations'
require 'rest_client'

class ConferencesController < ApplicationController

  def list
    @page_title = 'articles.conferences.headings.Conference_List'
    @conference_list = { future: [], passed: [] }
    Conference.all.order("start_date DESC").each do | conference |
      if conference.is_public || conference.host?(current_user)
        @conference_list[conference.over? ? :passed : :future] << conference
      end
    end
    @conference_list[:future].reverse!
  end

  def view
    set_conference
    do_403 unless @this_conference.is_public || @this_conference.host?(current_user)

    @workshops = Workshop.where(:conference_id => @this_conference.id)

    if @this_conference.workshop_schedule_published
      @event_dlg = true
      get_scheule_data(false)
    end

    if logged_in?
      if current_user.administrator?
        @links ||= []
        @links = [:edit]
      end
      
      if @this_conference.host? current_user
        @links ||= []
        @links = [:administrate, :register]
      end
    end
  end

  def register
    set_conference

    @register_template = nil

    if logged_in?
      set_or_create_conference_registration

      @name = current_user.firstname
      # we should phase out last names
      @name += " #{current_user.lastname}" if current_user.lastname

      @name ||= current_user.username

      @is_host = @this_conference.host? current_user
    else
      @register_template = :confirm_email
    end

    steps = nil
    return do_404 unless registration_steps.present?

    # @register_template = :administration if params[:admin_step].present?

    @errors = {}
    @warnings = []
    form_step = params[:button] ? params[:button].to_sym : nil

    # process any data that was passed to us
    if form_step
      if form_step.to_s =~ /^prev_(.+)$/
        steps = registration_steps
        @register_template = steps[steps.find_index($1.to_sym) - 1]
      elsif form_step == :paypal_confirm
        if @registration.present? && @registration.payment_confirmation_token == params[:confirmation_token]
          if Rails.env.test?
            @amount = params[:amount].to_f
            info = YAML.load(@registration.payment_info)
            info[:amount] = @amount
            @registration.payment_info = info.to_yaml
          else
            @amount = PayPal!.details(params[:token]).amount.total
            @registration.payment_info = {
                payer_id: params[:PayerID],
                token:    params[:token],
                amount:   @amount
              }.to_yaml
          end

          @amount = (@amount * 100).to_i.to_s.gsub(/^(.*)(\d\d)$/, '\1.\2')

          @registration.save!
        end

        @page_title = 'articles.conference_registration.headings.Payment'
        @register_template = :paypal_confirm
      elsif form_step == :paypal_confirmed
        info = YAML.load(@registration.payment_info)
        @amount = nil
        status = nil
        if Rails.env.test?
          status = info[:status]
          @amount = info[:amount]
        else
          paypal = PayPal!.checkout!(info[:token], info[:payer_id], PayPalRequest(info[:amount]))
          status = paypal.payment_info.first.payment_status
          @amount = paypal.payment_info.first.amount.total
        end
        if status == 'Completed'
          @registration.registration_fees_paid ||= 0
          @registration.registration_fees_paid += @amount

          # don't complete the step unless fees have been paid
          if @registration.registration_fees_paid > 0
            @registration.steps_completed << :payment
            @registration.steps_completed.uniq!
          end

          @registration.save!
        else
          @errors[:payment] = :incomplete
          @register_template = :payment
        end
        @page_title = 'articles.conference_registration.headings.Payment'
      else

        case form_step
        when :confirm_email
          return confirm_email(params[:email], params[:token], register_path(@this_conference.slug))
        when :contact_info
          if params[:name].present? && params[:name].gsub(/[\s\W]/, '').present?
            current_user.firstname = params[:name].squish
            current_user.lastname = nil
          else
            @errors[:name] = :empty
          end

          if params[:location].present? && params[:location].gsub(/[\s\W]/, '').present?
            city = City.search(params[:location])

            if city.present?
              @registration.city_id = city.id
              if params[:location].gsub(/[\s,]/, '').downcase != view_context.location(city).gsub(/[\s,]/, '').downcase
                @warnings << view_context._('warnings.messages.location_corrected', vars: {original: params[:location], corrected: view_context.location(city)})
              end
            else
              @errors[:location] = :unknown
            end
          else
            @errors[:location] = :empty
          end

          if params[:languages].present?
            current_user.languages = params[:languages].keys
          else
            @errors[:languages] = :empty
          end

          current_user.save! unless @errors.present?
        when :hosting
          @registration.can_provide_housing = params[:can_provide_housing].present?
          if params[:not_attending]
            @registration.is_attending = 'n'

            if current_user.is_subscribed.nil?
              current_user.is_subscribed = false
              current_user.save!
            end
          else
            @registration.is_attending = 'y'
          end

          @registration.housing_data = {
            address: params[:address],
            phone: params[:phone],
            space: {
              bed_space: params[:bed_space],
              floor_space: params[:floor_space],
              tent_space: params[:tent_space],
            },
            considerations: (params[:considerations] || {}).keys,
            availability: [ params[:first_day], params[:last_day] ],
            notes: params[:notes]
          }
        when :questions
          # create the companion's user account and send a registration link unless they have already registered
          generate_confirmation(User.create(email: params[:companion]), register_path(@this_conference.slug)) if params[:companion].present? && User.find_user(params[:companion]).nil?
          
          @registration.housing = params[:housing]
          @registration.arrival = params[:arrival]
          @registration.departure = params[:departure]
          @registration.housing_data = {
            companions: [ params[:companion] ]
          }
          @registration.bike = params[:bike]
          @registration.food = params[:food]
          @registration.allergies = params[:allergies]
          @registration.other = params[:other]
        when :payment
          amount = params[:amount].to_f

          if amount > 0
            # we can't really test paypal integration in our tests, so we'll fake it instead
            if Rails.env.test?
              @registration.payment_confirmation_token = 'token'
              @registration.payment_info = {amount: amount}.to_yaml
              @registration.save!
              redirect_to 'https://www.paypal.com'
            else
              @registration.payment_confirmation_token = Digest::SHA256.hexdigest(rand(Time.now.to_f * 1000000).to_i.to_s)
              @registration.save!
              pp = PayPal!
              response = pp.setup(
                PayPalRequest(amount),
                register_paypal_confirm_url(@this_conference.slug, :paypal_confirm, @registration.payment_confirmation_token),
                register_paypal_confirm_url(@this_conference.slug, :paypal_cancel, @registration.payment_confirmation_token),
                noshipping: true,
                version: 204
              )
              redirect_to response.redirect_uri
            end
            return
          end
        end

        if @errors.present?
          @register_template = form_step
        else
          unless @registration.nil?
            steps = registration_steps
            step_index = steps.find_index(form_step)
            @register_template = steps[step_index + 1] if step_index.present?

            # have we reached a new level?
            unless @registration.steps_completed.include? form_step.to_s
              # this step is only completed if a payment has been made
              if form_step != :payment || (@registration.registration_fees_paid || 0) > 0
                @registration.steps_completed ||= []
                @registration.steps_completed << form_step.to_s
                @registration.steps_completed.uniq!
              end
            end

            @registration.save!
          end
        end
      end
    end

    steps ||= registration_steps

    # make sure we're on a valid step
    @register_template ||= (params[:step] || view_context.current_step).to_sym

    if logged_in? && @register_template != :paypal_confirm
      # if we're logged in
      if !steps.include?(@register_template)
        # and we are not viewing a valid step
        return redirect_to register_path(@this_conference.slug)
      elsif @register_template != view_context.current_step && !registration_complete? && !@registration.steps_completed.include?(@register_template.to_s)
        # or the step hasn't been reached, registration is not yet complete, and we're not viewing the latest incomplete step
        return redirect_to register_path(@this_conference.slug)
      end
      # then we'll redirect to the current registration step
    end

    # prepare the form
    case @register_template
    when :questions
      # see if someone else has asked to be your companion
      if @registration.housing_data.blank?
        ConferenceRegistration.where(
          conference_id: @this_conference.id, can_provide_housing: [nil, false]
          ).where.not(housing_data: nil).each do | r |
          @registration.housing_data = {
              companions: [ r.user.email ]
            } if r.housing_data['companions'].present? && r.housing_data['companions'].include?(current_user.email)
        end
        
        @registration.housing_data ||= { }
      end
      @page_title = 'articles.conference_registration.headings.Registration_Info'
    when :payment
      @page_title = 'articles.conference_registration.headings.Payment'
    when :workshops
      @page_title = 'articles.conference_registration.headings.Workshops'
      
      # initialize our arrays
      @my_workshops = Array.new
      @requested_workshops = Array.new
      @workshops_in_need = Array.new
      @workshops = Array.new

      # put wach workshop into the correct array
      Workshop.where(conference_id: @this_conference.id).each do | workshop |
        if workshop.active_facilitator?(current_user)
          @my_workshops << workshop
        elsif workshop.requested_collaborator?(current_user)
          @requested_workshops << workshop
        elsif workshop.needs_facilitators
          @workshops_in_need << workshop
        else
          @workshops << workshop
        end
      end

      # sort the arrays by name
      @my_workshops.sort! { |a, b| a.title.downcase <=> b.title.downcase }
      @requested_workshops.sort! { |a, b| a.title.downcase <=> b.title.downcase }
      @workshops_in_need.sort! { |a, b| a.title.downcase <=> b.title.downcase }
      @workshops.sort! { |a, b| a.title.downcase <=> b.title.downcase }
    when :contact_info
      @page_title = 'articles.conference_registration.headings.Contact_Info'
    when :hosting
      @page_title = 'articles.conference_registration.headings.Hosting'
      @hosting_data = @registration.housing_data || {}
      @hosting_data['space'] ||= Hash.new
      @hosting_data['availability'] ||= Array.new
      @hosting_data['considerations'] ||= Array.new
    when :policy
      @page_title = 'articles.conference_registration.headings.Policy_Agreement'
    when :confirm_email
      @page_title = "articles.conference_registration.headings.#{@this_conference.registration_status == :open ? '': 'Pre_'}Registration_Details"
      @main_title = "articles.conference_registration.headings.#{@this_conference.registration_status == :open ? '': 'Pre_'}Register"
      @main_title_vars = { vars: { title: @this_conference.title } }
    end

  end

  # helper_method :registration_steps
  # helper_method :current_registration_steps
  helper_method :registration_complete?

  def registration_steps(conference = nil)
    conference ||= @this_conference || @conference
    status = conference.registration_status

    steps = status == :pre || status == :open ? [
        :policy,
        :contact_info,
        :questions,
        :hosting,
        :payment,
        :workshops
      ] : []
    
    steps -= [:questions] unless status == :open
    steps -= [:payment] unless status == :open && conference.paypal_email_address.present? && conference.paypal_username.present? && conference.paypal_password.present? && conference.paypal_signature.present?
    if @registration.present?
      if view_context.potential_provider(@registration)
        steps -= [:questions]
        
        # if this is a housing provider that is not attending the conference, remove these steps
        steps -= [:payment, :workshops] if @registration.is_attending == 'n'
      else
        steps -= [:hosting]
      end
    else
      steps -= [:hosting, :questions]
    end

    return steps
  end

  def required_steps(conference = nil)
    # return the intersection of current steps and required steps
    registration_steps(conference || @this_conference || @conference) & # current steps
      [:policy, :contact_info, :hosting, :questions] # all required steps
  end

  def registration_complete?(registration = @registration)
    completed_steps = registration.steps_completed || []
    required_steps(registration.conference).each do | step |
      return true if step == :workshops
      return false unless completed_steps.include?(step.to_s)
    end
    return true
  end

  rescue_from ActiveRecord::PremissionDenied do |exception|
    if !@this_conference.can_register?
      do_404
    elsif logged_in?
      redirect_to 'conferences/register'
    else
      @register_template = :confirm_email
      @page_title = "articles.conference_registration.headings.#{@this_conference.registration_status == :open ? '': 'Pre_'}Registration_Details"
      @main_title = "articles.conference_registration.headings.#{@this_conference.registration_status == :open ? '': 'Pre_'}Register"
      @main_title_vars = { vars: { title: @this_conference.title } }
      render 'conferences/register'
    end
  end

  rescue_from ActiveRecord::RecordNotFound do |exception|
    do_404
  end

  private

    def PayPal!
      Paypal::Express::Request.new(
        username:  @this_conference.paypal_username,
        password:  @this_conference.paypal_password,
        signature: @this_conference.paypal_signature
      )
    end

    def PayPalRequest(amount)
      Paypal::Payment::Request.new(
        currency_code: 'USD',                     # if nil, PayPal use USD as default
        description:   'Conference Registration', # item description
        quantity:      1,                         # item quantity
        amount:        amount.to_f,               # item value
        custom_fields: {
          CARTBORDERCOLOR: "00ADEF",
          LOGOIMG: "https://en.bikebike.org/assets/bblogo-paypal.png"
        }
      )
    end
end
