# frozen_string_literal: true

#  Copyright (c) 2020-2024, CVP 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.

require "spec_helper"

describe Export::Pdf::Messages::Letter do
  include Households::SpecHelper

  let(:letter) do
    Message::Letter.create!(mailing_list: list,
      body: messages(:letter).body,
      subject: "Information")
  end
  let(:options) { {} }
  let(:layer) { groups(:bottom_layer_one) }
  let(:group) { groups(:bottom_group_one_one) }
  let(:list) { Fabricate(:mailing_list, group: group) }
  let(:analyzer) { PDF::Inspector::Text.analyze(subject.render) }

  subject { described_class.new(letter, options) }

  before do
    people(:top_leader).update!(street: "Funkystreet", housenumber: "42", zip_code: "4242")
  end

  it "sanitizes filename" do
    letter.subject = "Liebe Mitglieder"
    expect(subject.filename).to eq "liebe_mitglieder.pdf"
  end

  it "prepends arguments passed" do
    letter.subject = "Liebe Mitglieder"
    expect(subject.filename(:preview)).to eq "preview-liebe_mitglieder.pdf"
  end

  context "text" do
    context "single recipient" do
      before do
        people(:top_leader).update!(street: "Funkystreet", housenumber: "42", zip_code: "4242")
        Subscription.create!(mailing_list: list,
          subscriber: group,
          role_types: [Group::BottomGroup::Member])
        Fabricate(Group::BottomGroup::Member.name, group: group, person: people(:bottom_member))
        Messages::LetterDispatch.new(letter).run
      end

      it "renders text at positions without sender address" do
        expect(text_with_position).to match_array [
          [71, 651, "Bottom Member"],
          [71, 639, "Greatstreet 345"],
          [71, 626, "3456 Greattown"],
          [71, 528, "Information"],
          [71, 499, "Hallo"],
          [71, 475, "Wir laden "],
          [113, 475, "dich"],
          [132, 475, " ein! "],
          [71, 450, "Bis bald"]
        ]
      end

      it "renders text at positions with group sender address" do
        letter.update!(pp_post: "Group 11, Lakeview 42, 4242 Wanaka")
        expect(text_with_position).to match_array [
          [71, 669, "Group 11, Lakeview 42, 4242 Wanaka"],
          [71, 651, "Bottom Member"],
          [71, 639, "Greatstreet 345"],
          [71, 626, "3456 Greattown"],
          [71, 528, "Information"],
          [71, 499, "Hallo"],
          [71, 475, "Wir laden "],
          [113, 475, "dich"],
          [132, 475, " ein! "],
          [71, 450, "Bis bald"]
        ]
      end

      it "renders example letter" do
        options[:debug] = true
        group.letter_logo.attach(
          io: File.open("spec/fixtures/files/images/logo.png"),
          filename: "logo.png"
        )
        letter.update!(group: groups(:top_group))
        layer.update!(street: "Lakeview", housenumber: "42", zip_code: "4242", town: "Bern")
        IO.binwrite("/tmp/file.pdf", subject.render)
      end
    end

    context "multiple recipients" do
      it "renders pages ordered by recipient last_name" do
        Subscription.create!(mailing_list: list,
          subscriber: group,
          role_types: [Group::BottomGroup::Member])
        Fabricate(Group::BottomGroup::Member.name, group: group, person: people(:bottom_member))
        Fabricate(Group::BottomGroup::Member.name, group: group, person: people(:top_leader))

        Messages::LetterDispatch.new(letter).run
        analyzer = PDF::Inspector::Text.analyze(described_class.new(letter, options).render)

        expect(text_with_position(analyzer)).to eq [
          [71, 651, "Top Leader"],
          [71, 639, "Funkystreet 42"],
          [71, 626, "4242 Greattown"],
          [71, 528, "Information"],
          [71, 499, "Hallo"],
          [71, 475, "Wir laden "],
          [113, 475, "dich"],
          [132, 475, " ein! "],
          [71, 450, "Bis bald"],
          [71, 651, "Bottom Member"],
          [71, 639, "Greatstreet 345"],
          [71, 626, "3456 Greattown"],
          [71, 528, "Information"],
          [71, 499, "Hallo"],
          [71, 475, "Wir laden "],
          [113, 475, "dich"],
          [132, 475, " ein! "],
          [71, 450, "Bis bald"]
        ]

        people(:bottom_member).update!(last_name: "First")

        letter2 = Message::Letter.create!(mailing_list: list,
          body: messages(:letter).body,
          subject: "Information")
        Messages::LetterDispatch.new(letter2).run
        analyzer = PDF::Inspector::Text.analyze(described_class.new(letter2, options).render)

        expect(text_with_position(analyzer)).to eq [
          [71, 651, "Bottom First"],
          [71, 639, "Greatstreet 345"],
          [71, 626, "3456 Greattown"],
          [71, 528, "Information"],
          [71, 499, "Hallo"],
          [71, 475, "Wir laden "],
          [113, 475, "dich"],
          [132, 475, " ein! "],
          [71, 450, "Bis bald"],
          [71, 651, "Top Leader"],
          [71, 639, "Funkystreet 42"],
          [71, 626, "4242 Greattown"],
          [71, 528, "Information"],
          [71, 499, "Hallo"],
          [71, 475, "Wir laden "],
          [113, 475, "dich"],
          [132, 475, " ein! "],
          [71, 450, "Bis bald"]
        ]
      end
    end

    context "stamping" do
      let(:stamps) { subject.pdf.instance_variable_get(:@stamp_dictionary_registry) }

      before do
        Subscription.create!(mailing_list: list,
          subscriber: group,
          role_types: [Group::BottomGroup::Member])
        Fabricate(Group::BottomGroup::Member.name, group: group, person: people(:bottom_member))
        Fabricate(Group::BottomGroup::Member.name, group: group, person: people(:top_leader))
        Messages::LetterDispatch.new(letter).run
      end

      it "renders addresses and content" do
        expect(text_with_position).to eq [
          [71, 651, "Top Leader"],
          [71, 639, "Funkystreet 42"],
          [71, 626, "4242 Greattown"],
          [71, 528, "Information"],
          [71, 499, "Hallo"],
          [71, 475, "Wir laden "],
          [113, 475, "dich"],
          [132, 475, " ein! "],
          [71, 450, "Bis bald"],
          [71, 651, "Bottom Member"],
          [71, 639, "Greatstreet 345"],
          [71, 626, "3456 Greattown"],
          [71, 528, "Information"],
          [71, 499, "Hallo"],
          [71, 475, "Wir laden "],
          [113, 475, "dich"],
          [132, 475, " ein! "],
          [71, 450, "Bis bald"]
        ]
        expect(stamps).to be_nil
      end

      it "renders only addresses has stamps" do
        options[:stamped] = true
        expect(text_with_position).to eq [
          [71, 651, "Top Leader"],
          [71, 639, "Funkystreet 42"],
          [71, 626, "4242 Greattown"],
          [71, 651, "Bottom Member"],
          [71, 639, "Greatstreet 345"],
          [71, 626, "3456 Greattown"]
        ]
        expect(stamps.keys).to eq [:render_logo_right, :render_shipping_info, :render_subject,
          :render_content]
      end

      it "falls back to normal rendering if stamping fails because content is to big" do
        letter.body = Faker::Lorem.paragraphs(number: 100)
        options[:stamped] = true
        expect(text_with_position).to be_present
        expect(stamps).to be_nil
      end
    end
  end

  context "household addresses" do
    let(:housemate1) { Fabricate(:person_with_address, first_name: "Anton", last_name: "Abraham", country: "DE", zip_code: "12345") }
    let(:housemate2) { Fabricate(:person_with_address, first_name: "Zora", last_name: "Zaugg", country: "DE", zip_code: "12345") }
    let(:other_housemate) do
      Fabricate(:person_with_address, first_name: "Altra", last_name: "Mates", country: "DE", zip_code: "12345")
    end

    before do
      Subscription.create!(mailing_list: list,
        subscriber: group,
        role_types: [Group::BottomGroup::Member])
      Fabricate(Group::BottomGroup::Member.name, group: group, person: housemate1)
      Fabricate(Group::BottomGroup::Member.name, group: group, person: housemate2)
      Fabricate(Group::BottomGroup::Member.name, group: group, person: people(:bottom_member))
      Fabricate(Group::BottomGroup::Member.name, group: group, person: people(:top_leader))

      letter.update!(send_to_households: true)
    end

    it "creates only one letter per household" do
      create_household(housemate1, housemate2)
      Messages::LetterDispatch.new(letter).run

      expect(text_with_position).to match_array [
        [71, 651, "Anton Abraham, Zora Zaugg"],
        [71, 639, housemate1.address],
        [71, 626, "#{housemate1.zip_code} #{housemate1.town}"],
        [71, 614, housemate1.country],
        [71, 528, "Information"],
        [71, 499, "Hallo"],
        [71, 475, "Wir laden "],
        [113, 475, "dich"],
        [132, 475, " ein! "],
        [71, 450, "Bis bald"],
        [71, 651, "Bottom Member"],
        [71, 639, "Greatstreet 345"],
        [71, 626, "3456 Greattown"],
        [71, 528, "Information"],
        [71, 499, "Hallo"],
        [71, 475, "Wir laden "],
        [113, 475, "dich"],
        [132, 475, " ein! "],
        [71, 450, "Bis bald"],
        [71, 651, "Top Leader"],
        [71, 639, "Funkystreet 42"],
        [71, 626, "4242 Greattown"],
        [71, 528, "Information"],
        [71, 499, "Hallo"],
        [71, 475, "Wir laden "],
        [113, 475, "dich"],
        [132, 475, " ein! "],
        [71, 450, "Bis bald"]
      ]
    end

    it "adds all household peoples names to address and sorts them alphabetically" do
      Fabricate(Group::BottomGroup::Member.name, group: group, person: other_housemate)
      create_household(people(:bottom_member), housemate1, housemate2, other_housemate,
        people(:top_leader))

      letter.message_recipients.destroy_all
      Messages::LetterDispatch.new(letter).run

      expect(text_with_position).to match_array [
        [71, 651, "Anton Abraham, Top Leader, Altra"],
        [71, 639, "Mates, Bottom Member, Zora Zaugg"],
        [71, 626, "Greatstreet 345"],
        [71, 614, "3456 Greattown"],
        [71, 528, "Information"],
        [71, 499, "Hallo"],
        [71, 475, "Wir laden "],
        [113, 475, "dich"],
        [132, 475, " ein! "],
        [71, 450, "Bis bald"]
      ]
    end
  end

  context "preview" do
    let(:analyzer) { PDF::Inspector::Text.analyze(subject.render_preview) }

    before do
      Subscription.create!(mailing_list: list,
        subscriber: group,
        role_types: [Group::BottomGroup::Member])
      Fabricate(Group::BottomGroup::Member.name, group: group, person: people(:bottom_member))
    end

    it "creates preview without persisted message recipients" do
      expect(text_with_position).to match_array [
        [71, 651, "Bottom Member"],
        [71, 639, "Greatstreet 345"],
        [71, 626, "3456 Greattown"],
        [71, 528, "Information"],
        [71, 499, "Hallo"],
        [71, 475, "Wir laden "],
        [113, 475, "dich"],
        [132, 475, " ein! "],
        [71, 450, "Bis bald"]
      ]
      expect(letter.message_recipients.reload.count).to eq(0)
    end

    context "household addresses" do
      let(:housemate1) do
        Fabricate(:person_with_address, first_name: "Anton", last_name: "Abraham", country: "HM", zip_code: "7151")
      end
      let(:housemate2) do
        Fabricate(:person_with_address, first_name: "Zora", last_name: "Zaugg", country: "HM", zip_code: "7151")
      end
      let(:housemate3) do
        Fabricate(:person_with_address, first_name: "Bettina", last_name: "Büttel", country: "HM", zip_code: "7151")
      end
      let(:housemate4) do
        Fabricate(:person_with_address, first_name: "Carlo", last_name: "Colorado", country: "HM", zip_code: "7151")
      end
      let(:other_housemate) do
        Fabricate(:person_with_address, first_name: "Altra", last_name: "Mates", country: "HM", zip_code: "7151")
      end
      let(:top_leader) { people(:top_leader) }
      let(:bottom_member) { people(:bottom_member) }
      let(:single_person) do
        Fabricate(:person_with_address, first_name: "Dominik", last_name: "Dachs", country: "DE", zip_code: "67253")
      end

      before do
        Subscription.create!(mailing_list: list,
          subscriber: group,
          role_types: [Group::BottomGroup::Member])
        Fabricate(Group::BottomGroup::Member.name, group: group, person: housemate1)
        Fabricate(Group::BottomGroup::Member.name, group: group, person: housemate2)
        Fabricate(Group::BottomGroup::Member.name, group: group, person: housemate3)
        Fabricate(Group::BottomGroup::Member.name, group: group, person: housemate4)
        Fabricate(Group::BottomGroup::Member.name, group: group, person: top_leader)
        Fabricate(Group::BottomGroup::Member.name, group: group, person: bottom_member)
        Fabricate(Group::BottomGroup::Member.name, group: group, person: single_person)

        letter.update!(send_to_households: true, body: "Hallo", subject: "Brief")
      end

      it "creates preview with half normal half household recipients" do
        create_household(housemate1, housemate2)
        create_household(housemate3, housemate4)
        create_household(other_housemate, people(:top_leader))
        expect(text_with_position).to match_array [
          [71, 651, "Anton Abraham, Zora Zaugg"],
          [71, 639, housemate1.address],
          [71, 626, "#{housemate1.zip_code} #{housemate1.town}"],
          [71, 614, housemate1.country],
          [71, 528, "Brief"],
          [71, 499, "Hallo"],
          [71, 651, "Bettina Büttel, Carlo Colorado"],
          [71, 639, housemate3.address],
          [71, 626, "#{housemate3.zip_code} #{housemate3.town}"],
          [71, 614, housemate3.country],
          [71, 528, "Brief"],
          [71, 499, "Hallo"],
          [71, 651, "Bottom Member"],
          [71, 639, bottom_member.address],
          [71, 626, "#{bottom_member.zip_code} #{bottom_member.town}"],
          [71, 528, "Brief"],
          [71, 499, "Hallo"],
          [71, 651, "Dominik Dachs"],
          [71, 639, single_person.address],
          [71, 626, "#{single_person.zip_code} #{single_person.town}"],
          [71, 614, single_person.country],
          [71, 528, "Brief"],
          [71, 499, "Hallo"]
        ]
      end

      it "includes all housemates even when underlying people scope is limited for previewing" do
        create_household(housemate1, housemate2, housemate3, housemate4)
        create_household(people(:top_leader), other_housemate)

        expect(text_with_position).to match_array [
          [71, 651, "Anton Abraham, Bettina Büttel, Carlo"],
          [71, 639, "Colorado, Zora Zaugg"],
          [71, 626, housemate1.address],
          [71, 614, "#{housemate1.zip_code} #{housemate1.town}"],
          [71, 651, housemate1.country],
          [71, 528, "Brief"],
          [71, 499, "Hallo"],
          [71, 651, "Bottom Member"],
          [71, 639, bottom_member.address],
          [71, 626, "#{bottom_member.zip_code} #{bottom_member.town}"],
          [71, 528, "Brief"],
          [71, 499, "Hallo"],
          [71, 651, "Top Leader"],
          [71, 639, top_leader.address],
          [71, 626, "#{top_leader.zip_code} #{top_leader.town}"],
          [71, 528, "Brief"],
          [71, 499, "Hallo"],
          [71, 651, "Dominik Dachs"],
          [71, 639, single_person.address],
          [71, 626, "#{single_person.zip_code} #{single_person.town}"],
          [71, 614, single_person.country],
          [71, 528, "Brief"],
          [71, 499, "Hallo"]
        ]
      end
    end
  end

  private

  def text_with_position(a = analyzer)
    a.positions.each_with_index.collect do |p, i|
      p.collect(&:round) + [a.show_text[i]]
    end
  end
end
