defmodule Mv.Membership.Import.HeaderMapperTest do
  use ExUnit.Case, async: true

  alias Mv.Membership.Import.HeaderMapper

  describe "normalize_header/1" do
    test "trims whitespace" do
      assert HeaderMapper.normalize_header("  email  ") == "email"
    end

    test "converts to lowercase" do
      assert HeaderMapper.normalize_header("EMAIL") == "email"
      assert HeaderMapper.normalize_header("E-Mail") == "e-mail"
    end

    test "normalizes Unicode characters" do
      # ß -> ss
      assert HeaderMapper.normalize_header("Straße") == "strasse"
      # Umlaute transliteration (ä -> ae, ö -> oe, ü -> ue)
      assert HeaderMapper.normalize_header("Müller") == "mueller"
      assert HeaderMapper.normalize_header("Köln") == "koeln"
      assert HeaderMapper.normalize_header("Grün") == "gruen"
    end

    test "compresses and removes whitespace" do
      # Whitespace is removed entirely to ensure "first name" == "firstname"
      assert HeaderMapper.normalize_header("first  name") == "firstname"
      assert HeaderMapper.normalize_header("email   address") == "emailaddress"
    end

    test "unifies hyphen variants" do
      # Different Unicode hyphen characters should become standard hyphen
      # en dash
      assert HeaderMapper.normalize_header("E–Mail") == "e-mail"
      # minus sign
      assert HeaderMapper.normalize_header("E−Mail") == "e-mail"
      # standard hyphen
      assert HeaderMapper.normalize_header("E-Mail") == "e-mail"
    end

    test "removes or unifies punctuation" do
      # Parentheses, slashes, etc. are removed (whitespace is also removed)
      assert HeaderMapper.normalize_header("E-Mail (privat)") == "e-mailprivat"
      assert HeaderMapper.normalize_header("Telefon / Mobil") == "telefonmobil"
    end

    test "handles empty strings" do
      assert HeaderMapper.normalize_header("") == ""
      assert HeaderMapper.normalize_header("   ") == ""
    end
  end

  describe "build_maps/2" do
    test "maps English email variant correctly" do
      headers = ["Email"]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, [])

      assert member_map[:email] == 0
      assert custom_map == %{}
      assert unknown == []
    end

    test "maps German email variant correctly" do
      headers = ["E-Mail"]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, [])

      assert member_map[:email] == 0
      assert custom_map == %{}
      assert unknown == []
    end

    test "maps multiple member fields" do
      headers = ["Email", "First Name", "Last Name"]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, [])

      assert member_map[:email] == 0
      assert member_map[:first_name] == 1
      assert member_map[:last_name] == 2
      assert custom_map == %{}
      assert unknown == []
    end

    test "handles Unicode and whitespace in headers" do
      headers = ["  E-Mail  ", "Straße", "  Telefon / Mobil  "]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, [])

      assert member_map[:email] == 0
      assert member_map[:street] == 1
      # "Telefon / Mobil" is not a known member field, so it should be unknown
      assert length(unknown) == 1
      assert custom_map == %{}
    end

    test "returns error when duplicate headers normalize to same field" do
      headers = ["Email", "E-Mail"]

      assert {:error, reason} = HeaderMapper.build_maps(headers, [])
      assert reason =~ "duplicate"
      assert reason =~ "email"
    end

    test "returns error when required field email is missing" do
      headers = ["First Name", "Last Name"]

      assert {:error, reason} = HeaderMapper.build_maps(headers, [])
      assert reason =~ "Missing required header"
      assert reason =~ "email"
      assert reason =~ "accepted"
    end

    test "collects unknown columns" do
      headers = ["Email", "FooBar", "UnknownColumn"]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, [])

      assert member_map[:email] == 0
      assert length(unknown) == 2
      assert "FooBar" in unknown or "foobar" in unknown
      assert "UnknownColumn" in unknown or "unknowncolumn" in unknown
      assert custom_map == %{}
    end

    test "ignores empty headers after normalization" do
      headers = ["Email", "   ", ""]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, [])

      assert member_map[:email] == 0
      assert custom_map == %{}
      assert unknown == []
    end

    test "maps custom field columns correctly" do
      headers = ["Email", "Lieblingsfarbe"]
      custom_fields = [%{id: "cf1", name: "Lieblingsfarbe"}]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, custom_fields)

      assert member_map[:email] == 0
      assert custom_map["cf1"] == 1
      assert unknown == []
    end

    test "custom field collision: member field wins" do
      headers = ["Email"]
      # Custom field with name "Email" should not override member field
      custom_fields = [%{id: "cf1", name: "Email"}]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, custom_fields)

      assert member_map[:email] == 0
      # Custom field should not be in custom_map because member field has priority
      assert custom_map == %{}
      assert unknown == []
    end

    test "handles custom field with Unicode normalization" do
      headers = ["Email", "Straße"]
      custom_fields = [%{id: "cf1", name: "Straße"}]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, custom_fields)

      assert member_map[:email] == 0
      # "Straße" is a member field (street), so it should be in member_map, not custom_map
      assert member_map[:street] == 1
      assert custom_map == %{}
      assert unknown == []
    end

    test "handles unknown custom field columns" do
      headers = ["Email", "UnknownCustomField"]
      custom_fields = [%{id: "cf1", name: "KnownField"}]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, custom_fields)

      assert member_map[:email] == 0
      assert custom_map == %{}
      # UnknownCustomField should be in unknown list
      assert length(unknown) == 1
    end

    test "handles duplicate custom field names after normalization" do
      headers = ["Email", "CustomField", "Custom Field"]
      custom_fields = [%{id: "cf1", name: "CustomField"}]

      # Both "CustomField" and "Custom Field" normalize to the same, so this should error
      assert {:error, reason} = HeaderMapper.build_maps(headers, custom_fields)
      assert reason =~ "duplicate"
    end

    test "maps all supported member fields" do
      headers = [
        "Email",
        "First Name",
        "Last Name",
        "Street",
        "Postal Code",
        "City"
      ]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, [])

      assert member_map[:email] == 0
      assert member_map[:first_name] == 1
      assert member_map[:last_name] == 2
      assert member_map[:street] == 3
      assert member_map[:postal_code] == 4
      assert member_map[:city] == 5
      assert custom_map == %{}
      assert unknown == []
    end

    test "maps German member field variants" do
      headers = ["E-Mail", "Vorname", "Nachname", "Straße", "PLZ", "Stadt"]

      assert {:ok, %{member: member_map, custom: custom_map, unknown: unknown}} =
               HeaderMapper.build_maps(headers, [])

      assert member_map[:email] == 0
      assert member_map[:first_name] == 1
      assert member_map[:last_name] == 2
      assert member_map[:street] == 3
      assert member_map[:postal_code] == 4
      assert member_map[:city] == 5
      assert custom_map == %{}
      assert unknown == []
    end
  end
end
