#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++

require 'spec_helper'

describe 'My project page editing', type: :feature, js: true do
  let(:project) { FactoryBot.create :project }
  let(:overview) { FactoryBot.create :my_projects_overview, project: project }
  let(:mypage) { ::Pages::Page.new }

  let(:button_selector) { '.toolbar a.button' }

  let(:user) { FactoryBot.create :user,
                                  member_in_project: project,
                                  member_through_role: role }
  let(:role) { FactoryBot.create :role, permissions: [:view_project,
                                                      :view_work_packages,
                                                      :edit_work_packages,
                                                      :edit_project] }

  # Add block select
  let(:select) { find('#block-select') }

  # Save button
  let(:save_changes) { find('#save-block-form input[type=submit]') }

  let(:editor) { ::Components::WysiwygEditor.new '#block_a' }

  # Block positions
  # Do not use let since it occasionally causes stale refernces
  def top
    find('#list-top')
  end

  def left
    find('#list-left')
  end

  def right
    find('#list-right')
  end

  def hidden
    find('#list-hidden')
  end

  def find_block(el, name)
    el.find("#block_#{name}")
  end

  def expect_block(el, name)
    expect(el).to have_selector("#block_#{name}")
  end

  def drag_to(name, from, to)
    # Expect from
    expect_block(from, name)

    # Move
    # I've tried with Capybara's drag_to and Selenium's native click_and_hold,
    # but both simply fail to move the block ocassionally :/

    block_id = "#block_#{name}"
    to_id = "##{to['id']}"
    page.execute_script <<-SCRIPT
      var blockContent = jQuery('#{block_id}').remove();
      jQuery('#{to_id}').append(blockContent);
    SCRIPT

    # Expect to
    expect_block(to, name)
  end

  before do
    overview
    login_as(user)

    visit my_projects_overview_path(project)
  end

  it 'should provide block drag n drop' do
    # Test block positions
    MyProjectsOverview::DEFAULTS.each do |list, names|
      el = send(list)
      names.each { |name| expect_block(el, name) }
    end

    drag_to(:members, right, top)
    save_changes.click

    # scrolling so that the flash notice can be 'seen'
    wrapper = find('#wrapper')
    scroll_to_element(wrapper)
    expect(page).to have_selector('.flash.notice', text: I18n.t('js.notice_successful_update'))
    expect_block(top, :members)
    expect(page).to have_no_selector('#list-right #block_members')
  end

  it 'should add and remove fixed blocks' do
    # Expect existing blocks to be disabled
    MyProjectsOverview::DEFAULTS.each do |_, names|
      names.each { |name| expect(select).to have_selector("option[value=#{name}][disabled]") }
    end

    # Choosing the block disables the option
    select.find('option[value=calendar]').select_option
    expect(select).to have_selector("option[value=calendar][disabled]")

    # Moving once more
    drag_to(:calendar, hidden, left)
    save_changes.click

    expect(page).to have_selector('.flash.notice', text: I18n.t('js.notice_successful_update'))
    expect_block(left, :calendar)

    # Remove the block again
    find('#my-project-page-layout').click
    find('#block_calendar .remove-block').click
    expect(page).to have_no_selector('#list-left #block_calendar')

    save_changes.click

    expect(page).to have_selector('.flash.notice', text: I18n.t('js.notice_successful_update'))
    expect(page).to have_no_selector('#list-left #block_calendar')
  end

  it 'should allow to add and edit custom blocks' do
    select.find('option[value=custom_element]').select_option

    # edit form should be open
    find('#block_title_a').set 'My title'
    editor.click_and_type_slowly 'My **commonmark** content'
    find('#a-form-submit').click

    # expect changes to be saved
    expect(page).to have_selector('.widget-box--header', text: 'My title', wait: 20)

    custom_block = find_block(hidden, :a)

    expect(custom_block).to have_selector('.widget-box--header-title', text: 'My title')
    expect(custom_block).to have_selector('#a-text strong', text: 'commonmark')

    # edit form should be closed
    expect(custom_block).to have_no_selector('#block_title_a', visible: true)

    # Toggling the form works
    custom_block.find('.edit-textilizable').click
    expect(custom_block).to have_selector('#block_title_a', visible: true)
    custom_block.find('.textile-form .reset-textilizable').click
    expect(custom_block).to have_no_selector('#block_title_a', visible: true)

    # Moving to visible
    drag_to(:a, hidden, right)

    # Saving the block
    save_changes.click
    expect(page).to have_selector('.flash.notice', text: I18n.t('js.notice_successful_update'))
    expect_block(right, :a)

    # Go back to editing page
    find('#my-project-page-layout').click
    expect(page).to have_no_selector('#list-hidden #block_a')

    # Removing the block
    find('#block_a .remove-block').click
    page.driver.browser.switch_to.alert.accept
    save_changes.click

    expect(page).to have_selector('.flash.notice', text: I18n.t('js.notice_successful_update'))
    expect(page).to have_no_selector('#block_a')
  end

  describe 'macro elements' do
    let!(:work_package) { FactoryBot.create(:work_package, project: project) }

    let(:modal) { ::Components::WorkPackages::TableConfigurationModal.new }
    let(:filters) { ::Components::WorkPackages::TableConfiguration::Filters.new }
    let(:columns) { ::Components::WorkPackages::Columns.new }

    it 'should allow to add and edit macros (Regression test #28399)' do
      select.find('option[value=custom_element]').select_option

      expect(page).to have_selector('.op-ckeditor--wrapper')
      expect(page).to have_selector('.ck-content')

      editor.in_editor do |_container, editable|
        editor.insert_macro 'Embed work package table'

        editor.set_markdown 'test'

        modal.expect_open
        modal.switch_to 'Filters'
        filters.expect_filter_count 2
        filters.add_filter_by('Type', 'is', work_package.type.name)

        modal.switch_to 'Columns'
        columns.assume_opened
        columns.uncheck_all save_changes: false
        columns.add 'ID', save_changes: false
        columns.add 'Subject', save_changes: false
        columns.add 'Type', save_changes: false
        columns.expect_checked 'ID'
        columns.expect_checked 'Subject'
        columns.expect_checked 'Type'

        # Save widget
        modal.save

        # Find widget, click to show toolbar
        expect(editable).to have_selector('.ck-widget.macro.-embedded-table')
      end

      # Save textile block
      find('#a-form-submit').click
      save_changes.click

      # Revisit page
      visit my_projects_overview_path(project)
      expect(page).to have_selector('#block_a')
      find('.edit-textilizable').click

      editor.in_editor do |_container, editable|
        # Find widget, click to show toolbar
        macro = editable.find('.ck-widget.macro.-embedded-table')
        macro.click

        # Edit widget again
        page.find('.ck-balloon-panel .ck-button', visible: :all, text: 'Edit').click

        modal.expect_open
        modal.switch_to 'Filters'
        filters.expect_filter_count 3
        modal.switch_to 'Columns'
        columns.assume_opened
        columns.expect_checked 'ID'
        columns.expect_checked 'Subject'
        columns.expect_checked 'Type'
        modal.cancel
      end

      find('#block_a .textile-form .reset-textilizable').click
    end
  end

  context 'as regular user' do
    let(:permissions) { %i(view_project) }
    let(:role) { FactoryBot.create :role, permissions: permissions }
    let(:user) { FactoryBot.create(:user, member_in_project: project, member_through_role: role) }

    it 'shows a 403 error' do
      expect(page).to have_selector('h2', text: '403')
      expect(page).to have_no_selector('.widget-box')
    end
  end
end
