# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/

require 'rails_helper'

RSpec.describe 'Ticket views', authenticated_as: :authenticate, type: :system do
  def authenticate
    true
  end

  context 'when calling without session' do
    describe 'redirect to' do
      it 'login screen after certain overview was called', authenticated_as: false do
        visit '#ticket/view/all_open'

        expect(page).to have_css('#login')
      end

      it 'login screen after not overview was called', authenticated_as: false do
        visit '#ticket/view'

        expect(page).to have_css('#login')
      end
    end
  end

  context 'macros' do
    let(:group1)              { create(:group) }
    let(:group2)              { create(:group) }
    let(:macro_without_group) { create(:macro) }
    let(:macro_note)          { create(:macro, perform: { 'article.note'=>{ 'body' => 'macro body', 'internal' => 'true', 'subject' => 'macro note' } }) }
    let(:macro_group1)        { create(:macro, groups: [group1]) }
    let(:macro_group2)        { create(:macro, groups: [group2]) }
    let(:ticket1)             { create(:ticket, group: group1) }
    let(:ticket2)             { create(:ticket, group: group2) }

    describe 'group-dependent macros' do
      let(:agent) { create(:agent, groups: Group.all) }

      def authenticate
        ticket1 && ticket2
        macro_without_group && macro_group1 && macro_group2
        agent
      end

      it 'shows only non-group macro when ticket does not match any group macros' do
        visit '#ticket/view/all_open'

        within(:active_content) do
          display_macro_batches Ticket.first

          expect(page).to have_selector(:macro_batch, macro_without_group.id)
            .and(have_no_selector(:macro_batch, macro_group1.id))
            .and(have_no_selector(:macro_batch, macro_group2.id))
        end
      end

      it 'shows non-group and matching group macros for matching ticket' do
        visit '#ticket/view/all_open'

        within(:active_content) do
          display_macro_batches ticket1

          expect(page).to have_selector(:macro_batch, macro_without_group.id)
            .and(have_selector(:macro_batch, macro_group1.id))
            .and(have_no_selector(:macro_batch, macro_group2.id))
        end
      end
    end

    describe 'macro article creation' do
      def authenticate
        macro_note
        true
      end

      it 'can use macro to create article' do
        visit '#ticket/view/all_open'

        within(:active_content) do
          display_macro_batches Ticket.first

          move_mouse_to find(:macro_batch, macro_note.id)
          release_mouse

          expect do
            wait.until { Ticket.first.articles.last.subject == 'macro note' }
          end.not_to raise_error
        end
      end
    end

    context 'when saving is blocked by one of selected tickets' do
      let(:ticket)               { Ticket.first }
      let(:core_workflow_action) { { 'ticket.priority_id': { operator: 'remove_option', remove_option: '3' } } }
      let(:core_workflow)        { create(:core_workflow, :active_and_screen, :perform_action) }

      let(:macro_perform) do
        {
          'ticket.priority_id': { pre_condition: 'specific', value: 3.to_s }
        }
      end

      let(:macro_priority) { create(:macro, perform: macro_perform) }

      def authenticate
        core_workflow && macro_priority && ticket1

        true
      end

      it 'shows modal with blocking ticket title' do
        visit '#ticket/view/all_open'

        within(:active_content) do
          display_macro_batches ticket

          move_mouse_to find(:macro_batch, macro_priority.id)
          release_mouse

          in_modal do
            expect(page).to have_text(ticket.title)
          end
        end
      end
    end

    describe 'when agent cannot change some of the tickets' do
      let(:agent) { create(:agent) }

      def authenticate
        macro_without_group

        agent
          .tap { |user| user.user_groups.create! group: ticket1.group, access: 'full' }
          .tap { |user| user.user_groups.create! group: ticket2.group, access: 'overview' }
      end

      before do
        visit '#ticket/view/all_open'
      end

      it 'show macros if agent cannot change selected tickets' do
        display_macro_batches ticket1

        within(:active_content) do
          expect(page).to have_no_text(%r{No macros available}i)
            .and(have_selector(:macro_batch, macro_without_group.id))
        end
      end

      it 'show no macros if agent cannot change selected tickets' do
        display_macro_batches ticket2

        within(:active_content) do
          expect(page).to have_text(%r{No macros available}i)
            .and(have_text(%r{no change permission}i))
            .and(have_no_selector(:macro_batch, macro_without_group.id))
        end
      end
    end

    describe 'when user is agent-customer' do
      let(:agent_customer) { create(:agent_and_customer) }

      def authenticate
        ticket1.update!(customer: agent_customer)
        ticket2

        macro_without_group && macro_group1 && macro_group2

        agent_customer
          .tap { |user| user.groups << ticket2.group }
      end

      before do
        visit '#ticket/view/all_open'
      end

      it 'show no macros if the ticket is customer-like' do
        display_macro_batches ticket1

        within :active_content do
          expect(page).to have_text(%r{No macros available}i)
            .and(have_text(%r{no change permission}i))
            .and(have_no_selector(:macro_batch, macro_without_group.id))
            .and(have_no_selector(:macro_batch, macro_group1.id))
            .and(have_no_selector(:macro_batch, macro_group2.id))
        end
      end

      it 'show macros if tickets are only agent-like' do
        display_macro_batches ticket2

        within :active_content do
          expect(page).to have_no_text(%r{No macros available}i)
            .and(have_selector(:macro_batch, macro_without_group.id))
            .and(have_no_selector(:macro_batch, macro_group1.id))
            .and(have_selector(:macro_batch, macro_group2.id))
        end
      end
    end

    describe 'when user is customer' do
      let(:customer) { create(:customer) }

      def authenticate
        ticket1.update!(customer: customer)

        customer
      end

      before do
        visit '#ticket/view/my_tickets'
      end

      it 'shows no overlay' do
        display_macro_batches ticket1

        within :active_content do
          expect(page).to have_no_selector('.batch-overlay-backdrop')
        end
      end
    end

    context 'with macro batch overlay' do
      shared_examples "adding 'small' class to macro element" do
        it 'adds a "small" class to the macro element' do
          within(:active_content) do
            display_macro_batches Ticket.first

            expect(page).to have_css('.batch-overlay-macro-entry.small')
          end
        end
      end

      shared_examples "not adding 'small' class to macro element" do
        it 'does not add a "small" class to the macro element' do
          within(:active_content) do
            display_macro_batches Ticket.first

            expect(page).to have_no_selector('.batch-overlay-macro-entry.small')
          end
        end
      end

      shared_examples 'showing all macros' do
        it 'shows all macros' do
          within(:active_content) do
            display_macro_batches Ticket.first

            expect(page).to have_css('.batch-overlay-macro-entry', count: all)
          end
        end
      end

      shared_examples 'showing some macros' do |count|
        it 'shows all macros' do
          within(:active_content) do
            display_macro_batches Ticket.first

            expect(page).to have_css('.batch-overlay-macro-entry', count: count)
          end
        end
      end

      def authenticate
        Macro.destroy_all && create_list(:macro, all)
        true
      end

      before do
        visit '#ticket/view/all_open'
      end

      context 'with few macros' do
        let(:all) { 15 }

        context 'when on large screen', screen_size: :desktop do
          it_behaves_like 'showing all macros'
          it_behaves_like "not adding 'small' class to macro element"
        end

        context 'when on small screen', screen_size: :tablet do
          it_behaves_like 'showing all macros'
          it_behaves_like "not adding 'small' class to macro element"
        end

      end

      context 'with many macros' do
        let(:all) { 50 }

        context 'when on large screen', screen_size: :desktop do
          it_behaves_like 'showing some macros', 32
        end

        context 'when on small screen', screen_size: :tablet do
          it_behaves_like 'showing some macros', 24
          it_behaves_like "adding 'small' class to macro element"
        end
      end
    end
  end

  context 'when performing a Bulk action' do
    context 'when creating a Note', authenticated_as: :user do
      let(:group)    { create(:group) }
      let(:user)     { create(:admin, groups: [group]) }
      let(:ticket1)  { create(:ticket, state_name: 'open', owner: user, group: group) }
      let(:ticket2)  { create(:ticket, state_name: 'open', owner: user, group: group) }
      let(:note)     { Faker::Lorem.sentence }

      it 'adds note to all selected tickets' do
        ticket1 && ticket2

        visit 'ticket/view/my_assigned'

        within :active_content do
          all('.js-checkbox-field', count: 2).each(&:click)
          click '.js-confirm'
          find('.js-confirm-step textarea').fill_in with: note
          click '.js-submit'
        end

        expect do
          wait.until { [ ticket1.articles.last&.body, ticket2.articles.last&.body ] == [note, note] }
        end.not_to raise_error
      end
    end

    # https://github.com/zammad/zammad/issues/3568
    # We need a manual ticket creation to test the correct behaviour of the bulk functionality, because of some
    #   leftovers after the creation in the the javascript assets store.
    context 'when performed a manual Ticket creation', authenticated_as: :agent do
      let(:customer)  { create(:customer) }
      let(:group)     { Group.find_by(name: 'Users') }
      let(:agent)     { create(:agent, groups: [group]) }
      let!(:template) { create(:template, :dummy_data, group: group, owner: agent, customer: customer) }

      before do
        visit 'ticket/create'

        within(:active_content) do
          use_template(template)

          click('.js-submit')

          find('.ticket-article-item')
        end
      end

      it 'check that no duplicated article was created after usage of bulk action' do
        click('.menu-item[href="#ticket/view"]')

        created_ticket_id = Ticket.last.id

        within(:active_content) do
          click("tr[data-id='#{created_ticket_id}'] .js-checkbox-field")

          find('select[name="priority_id"] option[value="1"]').select_option

          click('.js-confirm')
          click('.js-submit')

          await_empty_ajax_queue

          # Check if still only one article exists on the ticket.
          click("tr[data-id='#{created_ticket_id}'] a")
          expect(page).to have_css('.ticket-article-item', count: 1)
        end
      end
    end

    context 'when saving is blocked by one of selected tickets', authenticated_as: :pre_authentication do
      let(:core_workflow) { create(:core_workflow, :active_and_screen, :perform_action) }
      let(:ticket1)       { create(:ticket, group: Group.first) }

      def pre_authentication
        core_workflow && ticket1

        true
      end

      it 'shows modal with blocking ticket title' do
        visit 'ticket/view/all_open'

        within(:active_content) do
          find("tr[data-id='#{ticket1.id}']").check('bulk', allow_label_click: true)
          select '3 high', from: 'priority_id'
          click '.js-confirm'
          click '.js-submit'

          in_modal do
            expect(page).to have_text(ticket1.title)
          end
        end
      end
    end
  end

  context 'Setting "ui_table_group_by_show_count"', authenticated_as: :authenticate, db_strategy: :reset do
    let(:custom_attribute) { create(:object_manager_attribute_select, name: 'grouptest') }
    let(:tickets) do
      [
        create(:ticket, group: Group.find_by(name: 'Users')),
        create(:ticket, group: Group.find_by(name: 'Users'), grouptest: 'key_1'),
        create(:ticket, group: Group.find_by(name: 'Users'), grouptest: 'key_2'),
        create(:ticket, group: Group.find_by(name: 'Users'), grouptest: 'key_1')
      ]
    end

    def authenticate
      custom_attribute
      ObjectManager::Attribute.migration_execute
      tickets
      Overview.find_by(name: 'Open Tickets').update(group_by: custom_attribute.name)
      Setting.set('ui_table_group_by_show_count', true)
      true
    end

    it 'shows correct ticket counts' do
      visit 'ticket/view/all_open'

      within(:active_content) do
        expect(page).to have_css('.js-tableBody td b', text: '(1)')
          .and(have_css('.js-tableBody td b', text: 'value_1 (2)'))
          .and(have_css('.js-tableBody td b', text: 'value_2 (1)'))
      end
    end
  end

  context 'Customer', authenticated_as: :authenticate do
    let(:customer) { create(:customer, :with_org) }
    let(:ticket)   { create(:ticket, customer: customer) }

    def authenticate
      ticket
      customer
    end

    it 'shows ticket in my tickets' do
      visit 'ticket/view/my_tickets'
      expect(page).to have_text(ticket.title)
    end

    it 'shows ticket in my organization tickets' do
      visit 'ticket/view/my_tickets'
      click_on 'My Organization Tickets'
      expect(page).to have_text(ticket.title)
    end
  end

  describe 'Grouping by custom attribute', authenticated_as: :authenticate, db_strategy: :reset do
    def authenticate
      custom_attribute
      ObjectManager::Attribute.migration_execute
      tickets
      Overview.find_by(link: 'all_unassigned').update(group_by: custom_attribute.name)
      true
    end

    context 'when sorted by custom object date' do
      let(:custom_attribute) { create(:object_manager_attribute_date, name: 'cdate') }

      let(:tickets) do
        [
          create(:ticket, group: Group.find_by(name: 'Users'), cdate: '2021-08-18'),
          create(:ticket, group: Group.find_by(name: 'Users'), cdate: '2019-01-19'),
          create(:ticket, group: Group.find_by(name: 'Users'), cdate: '2018-01-17'),
          create(:ticket, group: Group.find_by(name: 'Users'), cdate: '2018-08-19')
        ]
      end

      it 'does show the values grouped and sorted by date key value (yyy-mm-dd) instead of display value' do
        visit 'ticket/view/all_unassigned'

        headers = all('.js-tableBody td[colspan="6"]').map(&:text)

        expect(headers).to eq ['01/17/2018', '08/19/2018', '01/19/2019', '08/18/2021', '-']
      end
    end

    context 'when sorted by custom object select', authenticated_as: :authenticate, db_strategy: :reset do
      let(:custom_attribute) do
        create(:object_manager_attribute_select,
               name:                'cselect',
               data_option_options: {
                 'a' => 'Zzz a',
                 'b' => 'Yyy b',
                 'c' => 'Xxx c',
               })
      end

      let(:tickets) do
        [
          create(:ticket, group: Group.find_by(name: 'Users'), cselect: 'a'),
          create(:ticket, group: Group.find_by(name: 'Users'), cselect: 'b'),
          create(:ticket, group: Group.find_by(name: 'Users'), cselect: 'c')
        ]
      end

      it 'does show the values grouped and sorted by display value instead of key value' do
        visit 'ticket/view/all_unassigned'

        headers = all('.js-tableBody td[colspan="6"]').map(&:text)

        expect(headers).to eq ['-', 'Xxx c', 'Yyy b', 'Zzz a']
      end
    end
  end

  context 'Overview is not refreshing #5260', authenticated_as: :agent, sessions_jobs: true do
    let(:agent)       { create(:agent, groups: Group.all) }
    let(:ticket)      { Ticket.first }
    let(:new_title_1) { SecureRandom.uuid }
    let(:new_title_2) { SecureRandom.uuid }
    let(:new_title_3) { SecureRandom.uuid }

    before do
      visit '#ticket/view/all_open'
      ensure_websocket
    end

    it 'does refresh the ticket after the title has changed' do
      expect(page).to have_text(ticket.title)
      ticket.update(title: new_title_1)
      expect(page).to have_text(ticket.title)
      ticket.update(title: new_title_2)
      expect(page).to have_text(ticket.title)
      ticket.update(title: new_title_3)
      expect(page).to have_text(ticket.title)
    end
  end
end