require_relative '../global_context'

class CmsContext < GlobalContext

  def initialize
    super
    create_users
  end

  # SETUP

  def create_fields(fields, id)
    fields.each do |field_type, type_hash|
      number_already_made_fields = Field.where(template_id: id).length
      type_hash['field_names'].each_with_index do |field_name, index|
        order = number_already_made_fields + index
        field_values = get_default_fields(field_name, field_type, type_hash['char_limit'], order)
        object_type = type_hash['object_type'] || ''
        field = Field.create!(field_values.merge({template_id: id, object_type: object_type, mandatory: type_hash['mandatory']}))
        create_menu_options(field_type, field.id, 'Field')
      end
    end
  end

  # GIVEN

  def given_page(name)
    unless Page.find_by(name: name)
      name = name.downcase
      Page.create!(
        name: name.capitalize,
        url: "/#{name}",
        active: true,
        title_text: "Teachable #{name}",
        description: "Meta description",
        canonical_url: "http://teachable.com/#{name}",
        creator_id: User.first.id
      )
    end
  end

  def given_user(user_name, user_credentials)
    team = user_credentials['team'] ? 'team' : 'team_id'
    permission = user_credentials['permission'] ? 'permission' : 'permission_id'
    team = Team.find_by(name: user_credentials[team]) || Team.create(name: user_credentials[team])
    permission = Permission.find_by(name: user_credentials[permission]) || create_permission(user_credentials[permission])
    User.create!(
      name: user_name,
      email: user_credentials['email'],
      password: "123456",
      team_id: team.id,
      permission_id: permission.id
    )
  end

  def given_partial(name)
    partial_info = @partials.select{|partial_info| partial_info['name'] == name}.first
    partial = Partial.create!(name: name, devs_only: partial_info['devs_only'])
    create_partial_fields(partial.id, get_partial_fields[name])
    partial
  end

  def given_template(name)
    template = Template.create!(name: name, plural: "#{name}s")
    fields = get_template_fields[name]
    create_fields(fields, template.id)
    template
  end

  def given_on_show_page(name, type)
    if ["tooltip", "feature"].include?(type.downcase)
      id = make_class_name(type).find_by(text: name).id
    else
      id = make_class_name(type).find_by(name: name).id
    end
    instance_variable_set("@#{type}_id", id)
    visit "admin/#{type}s/#{id}#{get_params}"
    wait_for_ajax
  end

  def given_field_value(value)
    @field_value = value
  end

  def given_page_partial(partial, page_name, parent_partial_field_id = nil)
    page_id = Page.find_by(name: page_name).try(:id)
    @partial_id = (Partial.find_by(name: partial) || given_partial(partial)).id
    create_page_partial(page_id, @partial_id, parent_partial_field_id)
  end

  def given_school_objekt
    school_template = Template.find_by(name: "School") || given_template('School')
    school_template_fields = school_template.fields
    school_objekt = Objekt.create!(template_id: school_template.id)
    creator_field = school_template_fields.select { |field| field.name == "Creator" }[0]
    creator_image_field = school_template_fields.select { |field| field.name == "Creator Image" }[0]
    madeline_image = create_image('object')
    creator_objekt_field = ObjektField.create!(
      objekt_id: school_objekt.id,
      field_id: creator_field.id,
      value: "Madeline"
    )
    creator_image_objekt_field = ObjektField.create!(
      objekt_id: school_objekt.id,
      field_id: creator_image_field.id,
      value: madeline_image.id
    )
  end

  # WHEN

  def visit_page(url, name)
    visit url + get_params
    @current_user ||= name
  end

  def select(button_text)
    find('li', text: button_text).click
    wait_for_ajax
  end

  def expand_page_partial(selector)
    within(selector) {find(".expand-toggle").click}
  end

  def add_field(name)
    starting_fields_length = all('.field').length - 1
    @field_values = get_step_fields[name].merge({ 'name' => name })
    click_a_tag('Add new field')
    row_number = starting_fields_length + 1
    within("#field-#{row_number}") do
      fill_in_row(@field_values)
    end
    @field_values['order'] = row_number.to_s
    click_and_wait('Save')
  end

  def change_field(field, value)
    set_original_field_values(field)
    @field_values[field] = value
    within("#field-#{@field_values['order']}") do
      fill_in_by_data_field(field, value)
    end
    click_and_wait('Save')
  end

  def delete
    click_a_tag('Delete')
    within('.confirm-delete') { click_and_wait('Yes') }
  end

  def preview_page
    click_a_tag('Preview')
    switch_to_new_tab
  end

  # EXPECT

  def expect_all_names_on_page(class_name)
    class_name.all.each { |instance| expect_content_on_page(instance.name) }
  end

  def expect_sorted_text(text)
    expect(text).to eq(text.sort)
  end

  def expect_same_field_values(db_field, expected_values)
    expected_values.each do |name, value|
      next if name == 'updated_at'
      db_value = db_field[make_snake_case(name)]
      expect(convert_to_frontend_format(db_value)).to eq(value)
    end
  end

  def expect_field_values_in_database(instance, field_values = @field_values)
    db_field_values_hash = instance.field_values_hash
    expect(db_field_values_hash).to eq(field_values)
  end

  def expect_field_not_on_page(name)
    all('div.field').each do |field|
      within(field) do
        expect(find_value_by_data_field('name')).not_to eq(name)
      end
    end
  end

  def expect_names_alphabetized
    names = all('.name-column').map {|element| element.text}
    expect_sorted_text(names)
  end

  def expect_one_instance(type)
    instances = make_class_name(type).where(name: @name)
    expect(instances.length).to eq(1)
  end

  def expect_changed_attribute(type, attribute, should_or_not)
    instance = make_class_name(type).find(instance_variable_get("@#{type}_id"))
    snake_case_attribute = make_snake_case(attribute)
    if should_or_not == 'should'
      expect(instance[snake_case_attribute]).to eq(@new_value)
    else
      expect(instance[snake_case_attribute]).to eq(@old_value)
    end
  end

  def expect_pages_alphabetized
    within('.list-page-instances', visible: false) do
      pages = all('p.name').map {|page| page.text}
      expect_sorted_text(pages)
    end
  end

  def expect_objekt_in_database(objekt_type, property, value)
    objekt = (
      Objekt
        .includes(:template)
        .where( templates: { name: objekt_type })
        .map(&:field_values_hash)
        .select { |field_values_hash| field_values_hash[property] == value }[0]
    )

    expect objekt
  end

  # HELPERS

  def make_class_name(text)
    text.capitalize.constantize
  end

  def click_and_wait(text, id = false)
    id ? find_by_id(text.to_s).click : click_a_tag(text)
    wait_for_ajax
  end

  def close_tab
    page.driver.browser.close
    page.driver.browser.switch_to.window (page.driver.browser.window_handles.first)
  end

  private

  # SETUP

  def create_permission(name)
    permissions = {'No access' => 0, 'User' => 1, 'Administrator' => 2, 'Super administrator' => 3}
    Permission.create!(name: name, level: permissions[name])
  end

  def create_users
    get_admin_login_credentials.each do |user_name, user_credentials|
      given_user(user_name, user_credentials)
    end
  end

  def create_image(category, reusable = false)
    Upload.create!(
      url: 'https://cdn.filepicker.io/api/file/kCve4yn2SueImwKdMnyG',
      upload_type: 'image',
      uploaded_by: User.first.id,
      category: category,
      reusable: reusable
    )
  end

  def create_file(category)
    Upload.create!(
      url: 'https://cdn.filepicker.io/api/file/kCve4yn2SueImwKdMnyG',
      upload_type: 'file',
      uploaded_by: User.first.id,
      category: category,
    )
  end

  def create_header_options
    ['Option 1', 'Option 2'].each_with_index do |option, index|
      HeaderType.create!({name: option, order: index})
    end
  end

  def get_admin_login_credentials(type = 'valid')
    credentials = { 'valid' => {
                        'Robb' => {
                          'email' => 'robb@teachable.com',
                          'password' => 'password',
                          'team' => 'Growth',
                          'permission' => 'Administrator'
                        },
                        'Jess' => {
                          'email' => 'jess@teachable.com',
                          'password' => 'password',
                          'team' => 'Growth',
                          'permission' => 'User'
                        }
                      },
                      'invalid' => {
                        'Robb' => {
                          'email' => 'fake@teachable.com',
                          'password' => 'password'
                        }
                      }
                    }
    credentials[type]
  end

  def get_template_fields
    {
      'School' => {
        'text' => {
          'field_names' => ['Creator'],
          'char_limit' => '100',
          'mandatory' => 'true'
        },
        'image' => {
          'field_names' => ['Creator Image'],
          'char_limit' => '0',
          'mandatory' => 'true'
        }
      }
    }.merge(get_default_all_fields_mandatory)
  end

  def get_partial_fields
    {
      'CTA' => {
        'text' => {
          'field_names' => ['Small Header', 'Large Header', 'Button Text', 'Button Url', 'Non-Featured Button Text', 'Non-Featured Button Url'],
          'char_limit' => '100',
          'mandatory' => 'false'
        },
        'number' => {
          'field_names' => ['Large Header Width', 'Computer Margin Bottom', 'Tablet Margin Bottom', 'Mobile Margin Bottom'],
          'char_limit' => '0',
          'mandatory' => 'false'
        }
      },
      'Sub Nav Menu - Toggle Visibility' => {
        'text' => {
          'field_names' => ['Nav Link Text 1', 'Nav Link Text 2', 'Nav Link Text 3', 'Nav Link Text 4', 'Nav Link Text 5', 'Nav Link Text 6'],
          'char_limit' => '50',
          'mandatory' => 'false'
        },
        'page-partial' => {
          'field_names' => ['Nav Partial 1', 'Nav Partial 2', 'Nav Partial 3', 'Nav Partial 4', 'Nav Partial 5', 'Nav Partial 6'],
          'char_limit' => '0',
          'mandatory' => 'false'
        },
        'number' => {
          'field_names' => ['Computer Margin Bottom', 'Tablet Margin Bottom', 'Mobile Margin Bottom'],
          'char_limit' => '0',
          'mandatory' => 'false'
        }
      },
      'Footer' => {},
      'Nav Menu' => {
        'number' => {
          'field_names' => ['Computer Margin Bottom', 'Tablet Margin Bottom', 'Mobile Margin Bottom'],
          'char_limit' => '0',
          'mandatory' => 'false'
        },
        'dropdown' => {
          'field_names' => ['Transparent?'],
          'char_limit' => '0',
          'mandatory' => 'false'
        }
      }
    }.merge(all_fields_mandatory_with_header)
  end

  def all_fields_mandatory_with_header
    defaults = get_default_all_fields_mandatory
    defaults['All Fields Mandatory'] = defaults['All Fields Mandatory'].merge({
      'header' => {
        'field_names' => ['Header'],
        'char_limit' => '500',
        'mandatory' => 'true'
      }
      })
    defaults
  end

  def get_default_types_and_fields
    {
      'input' => {
        'Number' => '9',
        'Text' => 'Testing'
      },
      'select' => {
        'Color' => 'Pale Green',
        'Drop Down' => 'Yes',
        'Object' => 'Madeline',
      },
      'image' => 'Image'
    }
  end

  def get_default_all_fields_mandatory
    {
      'All Fields Mandatory' => {
        'text' => {
          'field_names' => ['Text'],
          'char_limit' => '100',
          'mandatory' => 'true'
        },
        'image' => {
          'field_names' => ['Image'],
          'char_limit' => '0',
          'mandatory' => 'true'
        },
        'dropdown' => {
          'field_names' => ["Drop Down"],
          'char_limit' => '0',
          'mandatory' => 'true',
        },
        'object' => {
          'field_names' => ["Object"],
          'char_limit' => '0',
          'mandatory' => 'true',
          'object_type' => 'School'
        },
        'color' => {
          'field_names' => ['Color'],
          'char_limit' => '0',
          'mandatory' => 'true'
        },
        'number' => {
          'field_names' => ['Number'],
          'char_limit' => '0',
          'mandatory' => 'true'
        }
      }
    }
  end

  # HELPERS

  def get_params
    @current_user ? "?as=#{@current_user}" : ""
  end

  def find_by_data_field(text)
    find("[data-field='#{text}']", visible: false)
  end

  def find_value_by_data_field(type)
    find_by_data_field(type)['value']
  end

  def fill_in_by_data_field(data_field, text)
    find_by_data_field(data_field).set(text, {clear: :backspace})
  end

  def fill_in_row(field_values)
    field_values.each do |field, value|
      data_field = make_lazy_camel_case(field)
      fill_in_by_data_field(data_field, value) if fill_in_row_column?(field, data_field, value)
      change_nice_select_option(value, find_by_data_field(data_field)) if is_select_field?(field)
    end
  end

  def fill_in_row_column?(field, data_field, value)
    !(is_select_field?(field) || find_by_data_field(data_field).value == value)
  end

  def is_select_field?(field)
    ['mandatory', 'field_type', 'team_id', 'permission_id', 'object_type'].include?(field)
  end

  def change_nice_select_option(value, element)
    capitalized_value = value.split(' ').map(&:capitalize).join(' ')
    within(:xpath, get_ancestor_path(element.path)) do
      if find('span', visible: false).text.downcase != value.downcase
        nice_select = find('div.nice-select')
        list_item = select_option(nice_select, capitalized_value)
        return list_item
      else
        return find('li', text: capitalized_value, visible: false)
      end
    end
  end

  def delete_and_save(selector)
    click_trash_can(selector)
    click_and_wait('Save')
  end

  def delete_and_wait(selector)
    click_trash_can(selector)
    wait_for_ajax
  end

  def click_trash_can(selector)
    within(selector) { find(".trash-can").click }
  end

  def get_drop_zone_position(position, data_index, all_zones = nil)
    if position == 'last'
      @new_data_index = all_zones.length - 2
    else
      position = position.to_i
      is_up = move_up_or_down(data_index, position) == 'up'
      drop_zone_position = is_up ? (position - 2) : (position - 1)
      @new_data_index = is_up ? drop_zone_position + 1 : drop_zone_position
      drop_zone_position
    end
  end

  def move_up_or_down(data_index, new_position)
    old_position = data_index.to_i + 1
    new_position > old_position ? 'down' : 'up'
  end

  def perform_rearrange(drag_button, item, position)
    item_to_move = find(item)
    drop_zones = all('div.drop-zone')
    position = get_drop_zone_position(position, item_to_move['data-index'], drop_zones)
    drop_zone = drop_zones.select {|zone| zone['data-index'] == position.to_s}.first
    drag_button.drag_to(drop_zone)
    wait_for_ajax
  end

  def convert_to_frontend_format(value)
    if value == true
      return 'yes'
    elsif value == false
      return 'no'
    elsif value.class == Integer
      return value.to_s
    elsif value.nil?
      return ""
    end
    value
  end

  def get_order_and_id_hash(instances)
    hash = {}
    instances.each do |instance|
      hash[instance.order] = instance.id
    end
    hash
  end

  def switch_to_new_tab
    page.driver.browser.switch_to.window (page.driver.browser.window_handles.last)
  end

  def field_values_in_index?(db_field_values, index_values)
    index_values.each do |index_value_array|
      return true if db_field_values.all? {|value| index_value_array.include?(value)}
    end
    return false
  end

  def order_rearranged?(instances)
    instances.each do |instance|
      order_number = instance.order
      order_number += 1 unless order_number < @deleted_order
      return false unless @old_order_and_ids[order_number] == instance.id
    end
    true
  end

  def fill_in_all_fields
    get_types_and_fields.each do |field_type, fields|
      send("change_#{field_type}_field", fields)
    end
  end

  def change_input_field(fields)
    fields.each do |name, value|
      find("[data-fieldname='#{name}']").set(value)
      @field_values[name] = value
    end
  end

  def change_select_field(fields)
    fields.each do |name, value|
      @field_values[name] = change_nice_select_option(value, find("[data-fieldname='#{name}']", visible: false))['data-value']
    end
  end

  def change_image_field(name)
    images_in_database = Upload.images.length
    within('.page-partials-index, .admin-modal') { click_a_tag('Upload Image') }
    wait_until { Upload.images.length == images_in_database + 1 }
    @field_values[name] = Upload.last.id.to_s
  end

end
