# frozen_string_literal: true

#  Copyright (c) 2012-2013, Jungwacht Blauring Schweiz. This file is part of
#  hitobito and licensed under the Affero General Public License version 3
#  or later. See the COPYING file at the top-level directory or at
#  https://github.com/hitobito/hitobito.

# encoding:  utf-8

require "spec_helper"

describe PeopleController, type: :controller do
  let(:top_leader) { people(:top_leader) }
  let(:top_group) { groups(:top_group) }
  let(:bottom_group) { groups(:bottom_group_one_one) }
  let(:test_entry) { top_leader }
  let(:test_entry_attrs) { {first_name: "foo", last_name: "bar"} }
  let(:other) { Fabricate(Group::TopGroup::Member.name.to_sym, group: top_group).person }
  let(:dom) { Capybara::Node::Simple.new(response.body) }

  before { sign_in(top_leader) }

  def scope_params
    return {group_id: top_group.id} unless RSpec.current_example.metadata[:action] == :new

    {group_id: top_group.id, role: {type: "Group::TopGroup::Member", group_id: top_group.id}}
  end

  include_examples "crud controller", skip: [%w[new], %w[create], %w[destroy]]

  describe "#show" do
    let(:page_content) { ["Bearbeiten", "Info", "Verlauf", "Aktive Rollen", "Passwort ändern"] }
    let(:contactable_attrs) { [:company_name, :first_name, :last_name, :address_care_of, :postbox, :zip_code, :town] }

    it "cannot view person in uppper group" do
      sign_in(Fabricate(Group::BottomGroup::Leader.name.to_sym, group: bottom_group).person)
      expect do
        get :show, params: {group_id: top_group.id, id: top_leader.id}
      end.to raise_error(CanCan::AccessDenied)
    end

    it "renders my own page" do
      get :show, params: {group_id: top_group.id, id: top_leader.id}
      page_content.each { |text| expect(response.body).to match(/#{text}/) }
    end

    it "renders full contactable list" do
      top_leader.update!(company: true,
        company_name: "Hitobito AG",
        address_care_of: "Address care of",
        postbox: "Postbox")

      get :show, params: {group_id: top_group.id, id: top_leader.id}
      contactable_attrs.each { |attr| expect(response.body).to match(/#{top_leader.send(attr)}/) }
    end

    it "renders page of other group member" do
      sign_in(Fabricate(Group::TopGroup::Member.name.to_sym, group: top_group).person)
      get :show, params: {group_id: top_group.id, id: other.id}
      page_content.grep(/Info/).each { |text| expect(response.body).to match(/#{text}/) }
      page_content.grep(/[^Info]/).each { |text| expect(response.body).not_to match(/#{text}/) }
      expect(dom).not_to have_selector('a[data-method="delete"] i.fa-trash-alt')
    end

    it "leader can see link to remove role" do
      get :show, params: {group_id: top_group.id, id: other.id}
      expect(dom).to have_selector('a[data-method="delete"] i.fa-trash-alt')
    end

    it "leader can see created and updated info" do
      sign_in(top_leader)
      get :show, params: {group_id: top_group.id, id: other.id}
      expect(dom).to have_selector("dt", text: "Erstellt")
      expect(dom).to have_selector("dt", text: "Geändert")
    end

    it "member without permission to see details cannot see created or updated info" do
      person1 = Fabricate(Group::BottomLayer::Leader.name.to_sym,
        group: groups(:bottom_layer_one)).person
      person2 = Fabricate(Group::TopGroup::Secretary.name.to_sym,
        group: groups(:top_group)).person
      sign_in(person1)
      get :show, params: {group_id: person2.primary_group_id, id: person2}
      expect(dom).not_to have_selector("dt", text: "Erstellt")
      expect(dom).not_to have_selector("dt", text: "Geändert")
    end

    context "send_login tooltip" do
      before { sign_in(top_leader) }

      def tooltip_includes(text)
        expect(dom.all('a[data-bs-toggle="tooltip"]')[0][:title]).to include(text)
      end

      it "should hint for persons without login and token" do
        get :show, params: {group_id: top_group.id, id: other.id}
        tooltip_includes("sendet ihr einen Link")
      end

      it "should hint for persons without login but with token" do
        other.generate_reset_password_token!
        get :show, params: {group_id: top_group.id, id: other.id}
        tooltip_includes("sendet ihr den Link erneut")
      end

      it "should hint for persons with login" do
        other.password = "123456123456"
        other.save!
        get :show, params: {group_id: top_group.id, id: other.id}
        tooltip_includes("sendet ihr einen Link, damit sie ihr Passwort neu setzen kann")
      end
    end
  end

  describe "role section" do
    let(:params) { {group_id: top_group.id, id: top_leader.id} }
    let(:section) { dom.all("aside section")[1] }

    it "contains roles" do
      get :show, params: params
      expect(section.find("h2").text).to eq "Aktive Rollen"
      expect(section.all("tr").first.text).to include("TopGroup")
      expect(section).to have_css(".btn-sm")
      expect(section.find("tr:eq(1) table tr:eq(1)").text).to include("Leader")
      edit_role_path = edit_group_role_path(top_group, top_leader.roles.first)
      expect(section.find("tr:eq(1) table tr:eq(1) td:eq(2)").native.to_xml)
        .to include edit_role_path
    end
  end

  describe "household section" do
    let(:params) { {group_id: top_group.id, id: test_entry.id} }
    let(:member_one) { Fabricate(:person) }
    let(:member_two) { Fabricate(:person) }

    before do
      allow_any_instance_of(HouseholdAsideMemberComponent).to receive(:show_buttons?).and_return(true)
      create_household([top_leader, member_one, member_two])
    end

    before { create_household([member_one, member_two]) }

    it "renders household members" do
      get :show, params: params
      expect(dom).to have_selector("#household_aside table tr a", count: 3)
      expect(dom).to have_selector("#household_aside a", text: top_leader.full_name)
      expect(dom).to have_selector("#household_aside a", text: member_one.full_name)
      expect(dom).to have_selector("#household_aside a", text: member_two.full_name)
    end
  end

  describe "add requests section" do
    let(:section) { dom.all("aside section")[2] }

    it "contains requests" do
      _r1 = Person::AddRequest::Group.create!(
        person: top_leader,
        requester: Fabricate(:person),
        body: groups(:bottom_layer_one),
        role_type: Group::BottomLayer::Member.sti_name
      )
      _r2 = Person::AddRequest::Event.create!(
        person: top_leader,
        requester: Fabricate(:person),
        body: events(:top_course),
        role_type: Event::Role::Cook.sti_name
      )
      _r3 = Person::AddRequest::MailingList.create!(
        person: top_leader,
        requester: Fabricate(:person),
        body: mailing_lists(:leaders)
      )
      get :show, params: {group_id: top_group.id, id: top_leader.id}

      expect(section.find("h2").text).to eq "Anfragen"
      expect(section.all("tr")[0].text).to include("Bottom Layer Bottom One")
      expect(section.all("tr")[1].text).to include("Kurs Top Course")
      expect(section.all("tr")[2].text).to include("Abo Leaders")
    end

    it "is hidden if no pending requests exist" do
      Person::AddRequest::Group.create!(
        person: Fabricate(:person),
        requester: Fabricate(:person),
        body: groups(:bottom_layer_one),
        role_type: Group::BottomLayer::Member.sti_name
      )
      get :show, params: {group_id: top_group.id, id: top_leader.id}

      expect(section.find("h2").text).to eq "Qualifikationen Erstellen"
    end
  end

  describe "event sections" do
    let(:params) { {group_id: top_group.id, id: top_leader.id} }
    let(:header) { section.find("h2").text.strip }
    let(:dates) { section.find("tr:eq(1) td:eq(2)").text.strip }
    let(:label) { section.find("tr:eq(1) td:eq(1)") }
    let(:label_link) { label.all("a")[0] }
    let(:course) { Fabricate(:course, groups: [groups(:top_layer)], kind: event_kinds(:slk)) }

    context "pending applications" do
      let(:section) { dom.all("aside section")[2] }
      let(:date) { 1.week.from_now.to_date }

      it "is missing if we have no applications" do
        get :show, params: params
        expect(dom).to have_css("aside section", count: 4) # only tags, roles and qualification
      end

      it "lists application" do
        appl = create_application(date)
        get :show, params: params
        expect(header).to eq "Anmeldungen"
        expect(label_link[:href]).to eq(<<~URL.chomp)
          /groups/#{course.group_ids.first}/events/#{course.id}/participations/#{appl.participation.id}
        URL
        expect(label_link.text).to match(/Eventus/)
        expect(label.text).to match(/Top/)
        expect(dates).to eq "#{I18n.l(date,
          format: :with_day)} - #{I18n.l(date + 5.days,
            format: :with_day)}"
      end
    end

    context "upcoming events" do
      let(:section) { dom.all("aside section")[2] }
      let(:date) { 2.days.from_now }
      let(:participation_link) { label.all("a")[1] }
      let(:pretty_date) do
        "#{I18n.l(date,
          format: "%a %d.%m.%Y %H:%M")} - #{I18n.l(date + 5.days,
            format: "%a %d.%m.%Y %H:%M")}"
      end

      it "is missing if we have no events" do
        get :show, params: params
        expect(dom).to have_css("aside section", count: 4) # only tags, roles and qualification
      end

      it "is missing if we have no upcoming events" do
        create_participation(10.days.ago, active_participation: true)
        get :show, params: params
        expect(dom).to have_css("aside section", count: 4) # only tags, roles and qualification
      end

      it "lists event label, link and dates" do
        create_participation(date, active_participation: true)
        get :show, params: params
        expect(header).to eq "Meine nächsten Anlässe"
        expect(label_link[:href]).to eq group_event_path(course.groups.first, course)
        expect(label_link.text).to eq "Eventus"
        expect(label.text).to match(/Top/)
        expect(dates).to eq pretty_date
      end

      it "lists participation link" do
        participation = create_participation(date, active_participation: true)
        get :show, params: params
        expect(participation_link[:href]).to eq group_event_participation_path(course.groups.first,
          course, participation)
        expect(participation_link.text).to eq "Teilnahme"
      end
    end

    def create_application(date)
      Fabricate(:event_application,
        priority_1: course, # rubocop:disable Naming/VariableNumber
        participation: create_participation(date, active_participation: false))
    end

    def create_participation(date, active_participation: false)
      set_start_finish(course, date, date + 5.days)
      Fabricate(:event_participation, participant: top_leader, event: course,
        active: active_participation)
    end
  end

  describe_action :put, :update, id: true do
    let(:params) { {person: {birthday: "02.01.10000"}} }

    it "displays old value again" do
      is_expected.to render_template("edit")
      # expect(dom).to have_selector('.error')
      expect(dom).to have_selector("input.is-invalid")
      expect(dom).to have_selector("input#person_birthday.is-invalid")
      expect(dom).to have_selector('input[value="02.01.10000"].is-invalid')
    end
  end

  describe "redirect_url" do
    it "should adjust url if param redirect_url is given" do
      get :edit, params: {
        group_id: top_group.id,
        id: top_leader.id,
        return_url: "foo"
      }

      expect(dom.all("a", text: "Abbrechen").first[:href]).to eq "foo"
      expect(dom.find("input#return_url", visible: false).value).to eq "foo"
    end
  end

  private

  private

  def create_household(people)
    household = Household.new(test_entry)
    people.each { |person| household.add(person) }
    household.save
    household.reload
  end
end
