require 'rails_helper'

RSpec.describe User, type: :model do
  let(:user) { create :user, cn: "philipp" }
  let(:dn) { "cn=philipp,ou=kosmos.org,cn=users,dc=kosmos,dc=org" }

  describe "#address" do
    let(:user) { build :user, cn: "jimmy", ou: "kosmos.org" }

    it "returns the user address" do
      expect(user.address).to eq("jimmy@kosmos.org")
    end
  end

  describe "#mastodon_address" do
    let(:user) { build :user, cn: "jimmy", ou: "kosmos.org" }

    context "Mastodon service not configured" do
      it "returns nil" do
        expect(user.mastodon_address).to be_nil
      end
    end

    context "Mastodon service configured" do
      before do
        Setting.mastodon_enabled = true
      end

      describe "domain is the same as primary domain" do
        it "returns the user address" do
          expect(user.mastodon_address).to eq("jimmy@kosmos.org")
        end
      end

      describe "domain is different from primary domain" do
        before do
          Setting.mastodon_address_domain = "kosmos.social"
        end

        it "returns the user address" do
          expect(user.mastodon_address).to eq("jimmy@kosmos.social")
        end
      end
    end
  end

  describe "#is_admin?" do
    it "returns true when admin flag is set in LDAP" do
      expect(Devise::LDAP::Adapter).to receive(:get_ldap_param)
        .with(user.cn, :admin)
        .and_return("true")

      expect(user.is_admin?).to be true
    end

    it "returns false when admin flag is not set in LDAP" do
      expect(Devise::LDAP::Adapter).to receive(:get_ldap_param)
        .with(user.cn, :admin)
        .and_return(nil)

      expect(user.is_admin?).to be false
    end
  end

  describe "#services_enabled" do
    it "returns the entries from the LDAP service attribute" do
      expect(user).to receive(:ldap_entry).and_return({
        uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
        service: ["discourse", "email", "gitea", "wiki", "xmpp"]
      })
      expect(user.services_enabled).to eq(["discourse", "email", "gitea", "wiki", "xmpp"])
    end
  end

  describe "#enable_service" do
    before do
      allow(user).to receive(:ldap_entry).and_return({
        uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
        service: ["discourse", "gitea"]
      })
      allow(user).to receive(:dn).and_return(dn)
    end

    it "adds the service to the LDAP entry" do
      expect_any_instance_of(LdapService).to receive(:replace_attribute)
        .with(dn, :service, ["discourse", "gitea", "wiki"]).and_return(true)

      user.enable_service(:wiki)
    end

    it "adds multiple service to the LDAP entry" do
      expect_any_instance_of(LdapService).to receive(:replace_attribute)
        .with(dn, :service, ["discourse", "gitea", "wiki", "xmpp"]).and_return(true)

      user.enable_service([:wiki, :xmpp])
    end
  end

  describe "#disable_service" do
    before do
      allow(user).to receive(:ldap_entry).and_return({
        uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
        service: ["discourse", "gitea", "xmpp"]
      })
      allow(user).to receive(:dn).and_return(dn)
    end

    it "removes the service from the LDAP entry" do
      expect_any_instance_of(LdapService).to receive(:replace_attribute)
        .with(dn, :service, ["discourse", "gitea"]).and_return(true)

      user.disable_service(:xmpp)
    end

    it "removes multiple services from the LDAP entry" do
      expect_any_instance_of(LdapService).to receive(:replace_attribute)
        .with(dn, :service, ["discourse"]).and_return(true)

      user.disable_service([:xmpp, "gitea"])
    end
  end

  describe "#disable_all_services" do
    before do
      allow(user).to receive(:dn).and_return(dn)
    end

    it "removes all services from the LDAP entry" do
      expect_any_instance_of(LdapService).to receive(:delete_attribute)
        .with(dn, :service).and_return(true)

      user.disable_all_services
    end
  end

  describe "#devise_after_confirmation" do
    include ActiveJob::TestHelper

    let(:user) { create :user, cn: "willherschel", ou: "kosmos.org", email: "will@hrsch.el" }

    before do
      allow(user).to receive(:ldap_entry).and_return({
        uid: "willherschel", ou: "kosmos.org", mail: "will@hrsch.el"
      })
    end

    after { clear_enqueued_jobs }

    it "enables default services" do
      expect(user).to receive(:enable_service).with(%w[ discourse gitea mediawiki xmpp ])
      user.send :devise_after_confirmation
    end

    it "enqueues a job to set default chatroom bookmarks for XMPP" do
      allow(user).to receive(:enable_service).and_return(true)
      user.send :devise_after_confirmation

      job = enqueued_jobs.select{|j| j['job_class'] == "XmppSetDefaultBookmarksJob"}.first
      expect(job['arguments'][0]['_aj_globalid']).to eq('gid://akkounts/User/1')
    end

    context "for invited user with xmpp enabled" do
      let(:guest) { create :user, id: 2, cn: "isaacnewton", ou: "kosmos.org", email: "newt@example.com" }

      before do
        Invitation.create! user: user, invited_user_id: guest.id, used_at: DateTime.now
        allow_any_instance_of(User).to receive(:enable_service)
        allow(guest).to receive(:ldap_entry).and_return({
          uid: "isaacnewton", ou: "kosmos.org", mail: "newt@example.com"
        })
      end

      it "enqueues jobs to exchange XMPP contacts between inviter and invitee" do
        guest.send :devise_after_confirmation

        job = enqueued_jobs.select{|j| j['job_class'] == "XmppExchangeContactsJob"}.first
        expect(job["arguments"][0]['_aj_globalid']).to eq('gid://akkounts/User/1')
        expect(job["arguments"][1]['_aj_globalid']).to eq('gid://akkounts/User/2')
      end
    end

    context "for email address update of existing account" do
      before do
        allow(user).to receive(:ldap_entry)
          .and_return({ uid: "willherschel", ou: "kosmos.org", mail: "willyboy@aol.com" })
        allow(user).to receive(:dn)
          .and_return("cn=willherschel,ou=kosmos.org,cn=users,dc=kosmos,dc=org")
        allow(LdapManager::UpdateEmail).to receive(:call)
      end

      it "updates the LDAP 'mail' attribute" do
        expect(LdapManager::UpdateEmail).to receive(:call)
          .with(dn: "cn=willherschel,ou=kosmos.org,cn=users,dc=kosmos,dc=org", address: "will@hrsch.el")
        user.send :devise_after_confirmation
      end

      it "does not re-enable default services" do
        expect(user).not_to receive(:enable_service)
        user.send :devise_after_confirmation
      end

      it "does not enqueue any delayed jobs" do
        user.send :devise_after_confirmation
        expect(enqueued_jobs).to be_empty
      end
    end
  end

  describe "#nostr_pubkey_bech32" do
    before do
      user.update! nostr_pubkey: "07e188a1ff87ce171d517b8ed2bb7a31b1d3453a0db3b15379ec07b724d232f3"
    end

    it "encodes the hexadecimal pubkey to bech32" do
      expect(user.nostr_pubkey_bech32).to eq("npub1qlsc3g0lsl8pw8230w8d9wm6xxcax3f6pkemz5measrmwfxjxteslf2hac")
    end
  end
end
