# Dev/local seeds: run only in dev and test (Mix.env in [:dev, :test]).
# Creates 20 sample members, groups, and optional custom field values.
# Requires bootstrap seeds to have run first.

alias Mv.Accounts
alias Mv.Membership
alias Mv.MembershipFees.CycleGenerator
alias Mv.MembershipFees.MembershipFeeType

require Ash.Query

admin_email = System.get_env("ADMIN_EMAIL") || "admin@localhost"

admin_user_with_role =
  case Accounts.User
       |> Ash.Query.filter(email == ^admin_email)
       |> Ash.read_one(domain: Mv.Accounts, authorize?: false) do
    {:ok, user} when not is_nil(user) ->
      user |> Ash.load!(:role, authorize?: false)

    _ ->
      raise "Dev seeds require bootstrap: admin user not found (#{admin_email})"
  end

all_fee_types =
  MembershipFeeType
  |> Ash.Query.sort(name: :asc)
  |> Ash.read!(actor: admin_user_with_role, domain: Mv.MembershipFees)
  |> Enum.to_list()

# Countries: mostly Germany, 1–2 exceptions (index 7 = Österreich, index 14 = Schweiz)
countries_list =
  List.duplicate("Deutschland", 20)
  |> List.replace_at(7, "Österreich")
  |> List.replace_at(14, "Schweiz")

# 20 members: varied names, cities, join dates; fee types distributed over all members (round-robin)
member_configs = [
  %{
    first_name: "Anna",
    last_name: "Schmidt",
    city: "München",
    street: "Hauptstraße",
    house_number: "1",
    postal_code: "80331",
    join_date: ~D[2022-01-10]
  },
  %{
    first_name: "Bruno",
    last_name: "Müller",
    city: "Hamburg",
    street: "Lindenstraße",
    house_number: "5",
    postal_code: "20095",
    join_date: ~D[2022-03-15]
  },
  %{
    first_name: "Clara",
    last_name: "Fischer",
    city: "Berlin",
    street: "Kastanienallee",
    house_number: "12",
    postal_code: "10435",
    join_date: ~D[2022-05-20]
  },
  %{
    first_name: "David",
    last_name: "Weber",
    city: "Köln",
    street: "Rheinstraße",
    house_number: "8",
    postal_code: "50667",
    join_date: ~D[2022-07-01]
  },
  %{
    first_name: "Elena",
    last_name: "Wagner",
    city: "Frankfurt",
    street: "Goetheplatz",
    house_number: "3",
    postal_code: "60313",
    join_date: ~D[2022-09-12]
  },
  %{
    first_name: "Felix",
    last_name: "Becker",
    city: "Stuttgart",
    street: "Königstraße",
    house_number: "22",
    postal_code: "70173",
    join_date: ~D[2023-01-05]
  },
  %{
    first_name: "Greta",
    last_name: "Schulz",
    city: "Düsseldorf",
    street: "Schadowstraße",
    house_number: "14",
    postal_code: "40212",
    join_date: ~D[2023-02-14]
  },
  %{
    first_name: "Henrik",
    last_name: "Hoffmann",
    city: "Leipzig",
    street: "Nikolaistraße",
    house_number: "7",
    postal_code: "04109",
    join_date: ~D[2023-04-20]
  },
  %{
    first_name: "Ines",
    last_name: "Koch",
    city: "Dortmund",
    street: "Westenhellweg",
    house_number: "45",
    postal_code: "44137",
    join_date: ~D[2023-06-08]
  },
  %{
    first_name: "Jakob",
    last_name: "Richter",
    city: "Essen",
    street: "Kettwiger Straße",
    house_number: "2",
    postal_code: "45127",
    join_date: ~D[2023-08-11]
  },
  %{
    first_name: "Laura",
    last_name: "Klein",
    city: "Dresden",
    street: "Prager Straße",
    house_number: "9",
    postal_code: "01069",
    join_date: ~D[2023-10-01]
  },
  %{
    first_name: "Max",
    last_name: "Wolf",
    city: "Hannover",
    street: "Georgstraße",
    house_number: "50",
    postal_code: "30159",
    join_date: ~D[2023-11-15]
  },
  %{
    first_name: "Nina",
    last_name: "Schröder",
    city: "Nürnberg",
    street: "Königstraße",
    house_number: "73",
    postal_code: "90402",
    join_date: ~D[2024-01-20]
  },
  %{
    first_name: "Oliver",
    last_name: "Neumann",
    city: "Bremen",
    street: "Obernstraße",
    house_number: "31",
    postal_code: "28195",
    join_date: ~D[2024-03-10]
  },
  %{
    first_name: "Paula",
    last_name: "Schwarz",
    city: "Mannheim",
    street: "Planken",
    house_number: "11",
    postal_code: "68161",
    join_date: ~D[2024-05-22]
  },
  %{
    first_name: "Quirin",
    last_name: "Zimmermann",
    city: "Karlsruhe",
    street: "Kaiserstraße",
    house_number: "145",
    postal_code: "76133",
    join_date: ~D[2024-07-07]
  },
  %{
    first_name: "Rosa",
    last_name: "Braun",
    city: "Wiesbaden",
    street: "Wilhelmstraße",
    house_number: "6",
    postal_code: "65183",
    join_date: ~D[2024-09-01]
  },
  %{
    first_name: "Stefan",
    last_name: "Krüger",
    city: "Münster",
    street: "Ludgeristraße",
    house_number: "18",
    postal_code: "48143",
    join_date: ~D[2024-10-15]
  },
  %{
    first_name: "Thea",
    last_name: "Hartmann",
    city: "Augsburg",
    street: "Maximilianstraße",
    house_number: "4",
    postal_code: "86150",
    join_date: ~D[2024-11-20]
  },
  %{
    first_name: "Uwe",
    last_name: "Lange",
    city: "Bonn",
    street: "Remigiusstraße",
    house_number: "27",
    postal_code: "53111",
    join_date: ~D[2024-12-01]
  }
]

# Fee type index per member: 0..4 round-robin for all 20 (each type used 4 times)
# Cycle status: all_paid, all_unpaid, mixed (varied)
cycle_statuses = [
  :all_paid,
  :all_unpaid,
  :mixed,
  :all_paid,
  :mixed,
  :all_unpaid,
  :all_paid,
  :mixed,
  :all_unpaid,
  :all_paid,
  :mixed,
  :all_paid,
  :all_unpaid,
  :mixed,
  :all_paid,
  :mixed,
  :all_unpaid,
  :all_paid,
  :mixed,
  :all_paid
]

# Indices of members that get an exit date (5 distributed: 3, 7, 11, 15, 19)
exit_date_member_indices = [3, 7, 11, 15, 19]

Enum.with_index(member_configs)
|> Enum.each(fn {config, index} ->
  email = "mitglied#{index + 1}@example.de"
  fee_type_index = rem(index, length(all_fee_types))
  fee_type_id = Enum.at(all_fee_types, fee_type_index).id
  cycle_status = Enum.at(cycle_statuses, index)

  # Set fee type at create so cycles are generated with correct interval (no interval-change conflict)
  base_attrs = %{
    first_name: config.first_name,
    last_name: config.last_name,
    email: email,
    join_date: config.join_date,
    city: config.city,
    street: config.street,
    house_number: config.house_number,
    postal_code: config.postal_code,
    country: Enum.at(countries_list, index)
  }

  base_attrs =
    if fee_type_id,
      do: Map.put(base_attrs, :membership_fee_type_id, fee_type_id),
      else: base_attrs

  member =
    Membership.create_member!(base_attrs,
      upsert?: true,
      upsert_identity: :unique_email,
      actor: admin_user_with_role
    )

  if not is_nil(member.membership_fee_type_id) and not is_nil(cycle_status) do
    member_with_cycles =
      Ash.load!(member, :membership_fee_cycles, actor: admin_user_with_role)

    cycles =
      if Enum.empty?(member_with_cycles.membership_fee_cycles) do
        {:ok, new_cycles, _} =
          CycleGenerator.generate_cycles_for_member(member.id,
            skip_lock?: true,
            actor: admin_user_with_role
          )

        new_cycles
      else
        member_with_cycles.membership_fee_cycles
      end

    cycles
    |> Enum.sort_by(& &1.cycle_start, Date)
    |> Enum.with_index()
    |> Enum.each(fn {cycle, idx} ->
      status =
        case cycle_status do
          :all_paid ->
            :paid

          :all_unpaid ->
            :unpaid

          :mixed ->
            case rem(idx, 3) do
              0 -> :paid
              1 -> :unpaid
              2 -> :suspended
            end

          _ ->
            cycle.status
        end

      if cycle.status != status do
        cycle
        |> Ash.Changeset.for_update(:update, %{status: status})
        |> Ash.update!(actor: admin_user_with_role, domain: Mv.MembershipFees)
      end
    end)
  end

  if index in exit_date_member_indices do
    exit_date = Date.add(config.join_date, 365)
    Membership.update_member(member, %{exit_date: exit_date}, actor: admin_user_with_role)
  end
end)

# Groups (idempotent)
group_configs = [
  %{name: "Vorstand", description: "Gremium Vorstand"},
  %{name: "Jugend", description: "Jugendbereich"},
  %{name: "Newsletter", description: "Empfänger*innen Newsletter"}
]

existing_groups =
  case Membership.list_groups(actor: admin_user_with_role) do
    {:ok, list} -> list
    {:error, _} -> []
  end

existing_names_lower = MapSet.new(existing_groups, &String.downcase(&1.name))

seed_groups =
  Enum.reduce(group_configs, %{}, fn config, acc ->
    name = config.name

    if MapSet.member?(existing_names_lower, String.downcase(name)) do
      group = Enum.find(existing_groups, &(String.downcase(&1.name) == String.downcase(name)))
      Map.put(acc, name, group)
    else
      {:ok, group} =
        Membership.create_group(%{name: name, description: config.description},
          actor: admin_user_with_role
        )

      Map.put(acc, name, group)
    end
  end)

# Test users: create users linked to members (same email as member so sync is no-op),
# each with a different role for testing authorization.
all_members = Ash.read!(Membership.Member, actor: admin_user_with_role)

test_users_config = [
  {"mitglied1@example.de", "Mitglied"},
  {"mitglied2@example.de", "Vorstand"},
  {"mitglied3@example.de", "Kassenwart"},
  {"mitglied4@example.de", "Buchhaltung"}
]

roles_by_name =
  Mv.Authorization.Role
  |> Ash.read!(authorize?: false, domain: Mv.Authorization)
  |> Map.new(&{&1.name, &1})

Enum.each(test_users_config, fn {email, role_name} ->
  member = Enum.find(all_members, &(&1.email == email))
  role = roles_by_name[role_name]

  if not is_nil(member) and not is_nil(role) do
    user =
      Accounts.create_user!(
        %{email: email, member: %{id: member.id}},
        upsert?: true,
        upsert_identity: :unique_email,
        actor: admin_user_with_role
      )

    user
    |> Ash.Changeset.for_update(:update_user, %{role_id: role.id}, domain: Mv.Accounts)
    |> Ash.update!(actor: admin_user_with_role)
  end
end)

# Assign some members to groups (mitglied1–5 to Vorstand/Newsletter etc.)
member_group_assignments = [
  {"mitglied1@example.de", ["Vorstand", "Newsletter"]},
  {"mitglied2@example.de", ["Jugend", "Newsletter"]},
  {"mitglied3@example.de", ["Vorstand"]},
  {"mitglied4@example.de", ["Newsletter"]},
  {"mitglied5@example.de", ["Newsletter"]}
]

find_member = fn email -> Enum.find(all_members, &(&1.email == email)) end

Enum.each(member_group_assignments, fn {email, group_names} ->
  member = find_member.(email)

  if member do
    Enum.each(group_names, fn group_name ->
      group = seed_groups[group_name]

      if group do
        Membership.create_member_group(
          %{member_id: member.id, group_id: group.id},
          actor: admin_user_with_role
        )
      end
    end)
  end
end)

# Custom field values for ~80% of members (16 of 20): most of the 6 fields filled per member
all_custom_fields = Ash.read!(Membership.CustomField, actor: admin_user_with_role)
find_field = fn name -> Enum.find(all_custom_fields, &(&1.name == name)) end

# 16 members with 4–6 custom field values each (Geburtsdatum, DSGVO, SEPA, Rechnungs-E-Mail, IBAN, Stunden)
custom_value_assignments =
  Enum.map(1..16, fn n ->
    email = "mitglied#{n}@example.de"
    # Vary birth dates and values per index
    base_date = Date.add(~D[1970-01-01], 365 * (n + 10) + rem(n * 31, 365))

    values = [
      {"Geburtsdatum", %{"_union_type" => "date", "_union_value" => base_date}},
      {"DSGVO",
       %{"_union_type" => "boolean", "_union_value" => n in [1, 2, 3, 5, 7, 9, 11, 13, 15]}},
      {"SEPA-Mandat", %{"_union_type" => "boolean", "_union_value" => rem(n, 3) != 0}},
      {"Rechnungs-E-Mail",
       %{"_union_type" => "email", "_union_value" => "rechnung#{n}@example.de"}},
      {"IBAN",
       %{
         "_union_type" => "string",
         "_union_value" =>
           "DE8937040044#{String.pad_leading(to_string(rem(532_013_000 + n, 1_000_000_000)), 10, "0")}"
       }},
      {"Stunden ehrenamtlich",
       %{"_union_type" => "integer", "_union_value" => 5 + rem(n * 3, 25)}}
    ]

    # Drop 0–2 fields per member so not all have 6 (still ~80% overall filled)
    drop_count = rem(n, 3)
    {email, Enum.take(values, 6 - drop_count)}
  end)

for {email, values} <- custom_value_assignments do
  member = find_member.(email)

  if member do
    Enum.each(values, fn {field_name, value} ->
      field = find_field.(field_name)

      if field do
        Membership.CustomFieldValue
        |> Ash.Changeset.for_create(:create, %{
          member_id: member.id,
          custom_field_id: field.id,
          value: value
        })
        |> Ash.create!(
          upsert?: true,
          upsert_identity: :unique_custom_field_per_member,
          actor: admin_user_with_role
        )
      end
    end)
  end
end

# Join form: enable so membership application list is visible in dev; default field list includes address + join_date
default_join_form_field_ids = [
  "email",
  "first_name",
  "last_name",
  "street",
  "house_number",
  "postal_code",
  "city",
  "country",
  "join_date"
]

default_join_form_field_required = %{"email" => true}

case Membership.get_settings() do
  {:ok, settings} ->
    unless settings.join_form_enabled do
      Membership.update_settings(settings, %{
        join_form_enabled: true,
        join_form_field_ids: settings.join_form_field_ids || default_join_form_field_ids,
        join_form_field_required:
          settings.join_form_field_required || default_join_form_field_required
      })
    end

  _ ->
    :ok
end

# Membership applications (join requests) for UI testing: 4 submitted, 1 with extra form_data
join_request_configs = [
  %{
    email: "antrag1@example.de",
    first_name: "Sandra",
    last_name: "Meier",
    form_data: %{"city" => "Berlin"}
  },
  %{email: "antrag2@example.de", first_name: "Thomas", last_name: "Bauer", form_data: %{}},
  %{
    email: "antrag3@example.de",
    first_name: "Julia",
    last_name: "Krause",
    form_data: %{"city" => "Hamburg", "notes" => "Interesse an Jugendgruppe"}
  },
  %{
    email: "antrag4@example.de",
    first_name: "Michael",
    last_name: "Schmitt",
    form_data: %{"city" => "München"}
  }
]

for config <- join_request_configs do
  attrs = %{
    email: config.email,
    first_name: config.first_name,
    last_name: config.last_name,
    form_data: config.form_data || %{},
    schema_version: 1
  }

  Mv.Membership.JoinRequest
  |> Ash.Changeset.for_create(:create_submitted, attrs)
  |> Ash.create!(authorize?: false, domain: Mv.Membership)
end

IO.puts("✅ Dev seeds completed.")

IO.puts(
  "   - Members: 20 with country (mostly Deutschland, 1 Österreich, 1 Schweiz), fee types distributed, 5 with exit date"
)

IO.puts(
  "   - Test users: 4 linked to mitglied1–4 with roles Mitglied, Vorstand, Kassenwart, Buchhaltung"
)

IO.puts("   - Groups: Vorstand, Jugend, Newsletter (with assignments)")
IO.puts("   - Custom field values: ~80% filled (16 members, 4–6 fields each)")
IO.puts("   - Join form enabled; 4 membership applications (join requests) for UI testing")
