# frozen_string_literal: true

RSpec.describe UsernameValidator do
  def expect_valid(*usernames)
    usernames.each do |username|
      validator = UsernameValidator.new(username)

      aggregate_failures do
        expect(validator.valid_format?).to eq(true), "expected '#{username}' to be valid"
        expect(validator.errors).to be_empty
      end
    end
  end

  def expect_invalid(*usernames, error_message:)
    usernames.each do |username|
      validator = UsernameValidator.new(username)

      aggregate_failures do
        expect(validator.valid_format?).to eq(false), "expected '#{username}' to be invalid"
        expect(validator.errors).to include(error_message)
      end
    end
  end

  shared_examples "ASCII username" do
    it "is invalid when the username is blank" do
      expect_invalid("", error_message: I18n.t(:"user.username.blank"))
    end

    it "is invalid when the username is too short" do
      SiteSetting.min_username_length = 4

      expect_invalid("a", "ab", "abc", error_message: I18n.t(:"user.username.short", min: 4))
    end

    it "is valid when the username has the minimum length" do
      SiteSetting.min_username_length = 4

      expect_valid("abcd")
    end

    it "is invalid when the username is too long" do
      SiteSetting.max_username_length = 8

      expect_invalid("abcdefghi", error_message: I18n.t(:"user.username.long", max: 8))
    end

    it "is valid when the username has the maximum length" do
      SiteSetting.max_username_length = 8

      expect_valid("abcdefgh")
    end

    it "is valid when the username contains alphanumeric characters, dots, underscores and dashes" do
      expect_valid("ab-cd.123_ABC-xYz")
    end

    it "is invalid when the username contains non-alphanumeric characters other than dots, underscores and dashes" do
      expect_invalid("abc|", "a#bc", "abc xyz", error_message: I18n.t(:"user.username.characters"))
    end

    it "is valid when the username starts with a alphanumeric character or underscore" do
      expect_valid("abcd", "1abc", "_abc")
    end

    it "is invalid when the username starts with a dot or dash" do
      expect_invalid(
        ".abc",
        "-abc",
        error_message: I18n.t(:"user.username.must_begin_with_alphanumeric_or_underscore"),
      )
    end

    it "is valid when the username ends with a alphanumeric character" do
      expect_valid("abcd", "abc9")
    end

    it "is invalid when the username ends with an underscore, a dot or dash" do
      expect_invalid(
        "abc_",
        "abc.",
        "abc-",
        error_message: I18n.t(:"user.username.must_end_with_alphanumeric"),
      )
    end

    it "is invalid when the username contains consecutive underscores, dots or dashes" do
      expect_invalid(
        "a__bc",
        "a..bc",
        "a--bc",
        error_message: I18n.t(:"user.username.must_not_contain_two_special_chars_in_seq"),
      )
    end

    it "is invalid when the username ends with certain file extensions" do
      expect_invalid(
        "abc.json",
        "abc.png",
        error_message: I18n.t(:"user.username.must_not_end_with_confusing_suffix"),
      )
    end
  end

  context "when Unicode usernames are disabled" do
    before { SiteSetting.unicode_usernames = false }

    include_examples "ASCII username"

    it "is invalid when the username contains non-ASCII characters except dots, underscores and dashes" do
      expect_invalid("abcö", "abc象", error_message: I18n.t(:"user.username.characters"))
    end
  end

  context "when Unicode usernames are enabled" do
    before { SiteSetting.unicode_usernames = true }

    context "with ASCII usernames" do
      include_examples "ASCII username"
    end

    context "with Unicode usernames" do
      before { SiteSetting.min_username_length = 1 }

      it "is invalid when the username is too short" do
        SiteSetting.min_username_length = 3

        expect_invalid("鳥", "পাখি", error_message: I18n.t(:"user.username.short", min: 3))
      end

      it "is valid when the username has the minimum length" do
        SiteSetting.min_username_length = 2

        expect_valid("পাখি", "طائر")
      end

      it "is invalid when the username is too long" do
        SiteSetting.max_username_length = 8

        expect_invalid(
          "חוטב_עצים",
          "Holzfäller",
          error_message: I18n.t(:"user.username.long", max: 8),
        )
      end

      it "is valid when the username has the maximum length" do
        SiteSetting.max_username_length = 9

        expect_valid("Дровосек", "چوب-لباسی", "தமிழ்-தமிழ்")
      end

      it "is invalid when the username has too many Unicode codepoints" do
        SiteSetting.max_username_length = 30

        expect_invalid(
          "য়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়ায়া",
          error_message: I18n.t(:"user.username.too_long"),
        )
      end

      it "is valid when the username contains Unicode letters" do
        expect_valid(
          "鳥",
          "طائر",
          "թռչուն",
          "πουλί",
          "পাখি",
          "madár",
          "새",
          "پرنده",
          "птица",
          "fågel",
          "นก",
          "پرندے",
          "ציפור",
        )
      end

      it "is valid when the username contains numbers from the Nd or Nl Unicode category" do
        expect_valid("arabic٠١٢٣٤٥٦٧٨٩", "bengali০১২৩৪৫৬৭৮৯", "romanⅥ", "hangzhou〺")
      end

      it "is invalid when the username contains numbers from the No Unicode category" do
        expect_invalid("circled㊸", "fraction¾", error_message: I18n.t(:"user.username.characters"))
      end

      it "is invalid when the username contains symbols or emojis" do
        SiteSetting.min_username_length = 1

        expect_invalid(
          "©",
          "⇨",
          "“",
          "±",
          "‿",
          "😃",
          "🚗",
          error_message: I18n.t(:"user.username.characters"),
        )
      end

      it "is invalid when the username contains zero width join characters" do
        expect_invalid("ണ്‍", "র‌্যাম", error_message: I18n.t(:"user.username.characters"))
      end

      it "is valid when the username ends with a Unicode Mark" do
        expect_valid("தமிழ்")
      end

      it "allows all Unicode letters when the allowlist is empty" do
        expect_valid("鳥")
      end

      context "with Unicode allowlist" do
        before { SiteSetting.allowed_unicode_username_characters = "[äöüÄÖÜß]" }

        it "is invalid when username contains non-allowlisted letters" do
          expect_invalid("鳥", "francès", error_message: I18n.t(:"user.username.characters"))
        end

        it "is valid when username contains only allowlisted letters" do
          expect_valid("Löwe", "Ötzi")
        end

        it "is valid when username contains only ASCII letters and numbers regardless of allowlist" do
          expect_valid("a-z_A-Z.0-9")
        end

        it "is valid after resetting the site setting" do
          SiteSetting.allowed_unicode_username_characters = ""
          expect_valid("鳥")
        end
      end
    end
  end
end