# Bootstrap seeds: run in all environments (dev, test, prod).
# Creates only data required for system startup: fee types, custom fields,
# roles, admin user, system user, global settings. No members, no groups.
#
# Safe to run from release (no Mix): env is taken from MIX_ENV when Mix.env/0 is not available.

mix_env =
  try do
    Mix.env()
  rescue
    UndefinedFunctionError -> (System.get_env("MIX_ENV") || "prod") |> String.to_atom()
  end

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

require Ash.Query

# 1. Membership fee types (authorize?: false for bootstrap)
# Names without interval to avoid duplication in UI; interval is shown separately.
fee_type_configs = [
  %{
    name: "Standard",
    amount: Decimal.new("120.00"),
    interval: :yearly,
    description: "Standard jährlicher Mitgliedsbeitrag"
  },
  %{
    name: "Ermäßigt",
    amount: Decimal.new("80.00"),
    interval: :yearly,
    description: "Ermäßigter jährlicher Mitgliedsbeitrag"
  },
  %{
    name: "Unterstützer",
    amount: Decimal.new("60.00"),
    interval: :half_yearly,
    description: "Unterstützerbeitrag halbjährlich"
  },
  %{
    name: "Fördermitglied",
    amount: Decimal.new("30.00"),
    interval: :quarterly,
    description: "Fördermitgliedschaft quartalsweise"
  },
  %{
    name: "Probemitgliedschaft",
    amount: Decimal.new("10.00"),
    interval: :monthly,
    description: "Probemitgliedschaft monatlich"
  }
]

for attrs <- fee_type_configs do
  MembershipFeeType
  |> Ash.Changeset.for_create(:create, attrs)
  |> Ash.create!(
    upsert?: true,
    upsert_identity: :unique_name,
    authorize?: false,
    domain: Mv.MembershipFees
  )
end

# Resolve default fee type (Standard, 120€ yearly) for settings
# Filter by name and interval to avoid ambiguity if multiple "Standard" types exist
default_fee_type =
  Mv.MembershipFees.MembershipFeeType
  |> Ash.Query.filter(name == "Standard" and interval == :yearly)
  |> Ash.read_one!(authorize?: false, domain: Mv.MembershipFees)

# 2. Custom fields (authorize?: false for bootstrap)
# Only Geburtsdatum is shown in overview by default; others hidden to avoid clutter.
custom_field_configs = [
  %{
    name: "Geburtsdatum",
    value_type: :date,
    description: "Geburtsdatum der/des Mitglieds",
    required: false,
    show_in_overview: true
  },
  %{
    name: "DSGVO",
    value_type: :boolean,
    description: "Angabe, ob die Datenschutzerklärung akzeptiert wurde",
    join_description:
      "Ich habe die [Datenschutzerklärung](https://example.org/datenschutz) gelesen und akzeptiere sie.",
    required: false,
    show_in_overview: false
  },
  %{
    name: "SEPA-Mandat",
    value_type: :boolean,
    description: "SEPA-Lastschriftmandat erteilt",
    required: false,
    show_in_overview: false
  },
  %{
    name: "Rechnungs-E-Mail",
    value_type: :email,
    description: "E-Mail-Adresse für Rechnungen",
    required: false,
    show_in_overview: false
  },
  %{
    name: "IBAN",
    value_type: :string,
    description: "IBAN für Lastschrift",
    required: false,
    show_in_overview: false
  },
  %{
    name: "Stunden ehrenamtlich",
    value_type: :integer,
    description: "Geleistete ehrenamtliche Stunden",
    required: false,
    show_in_overview: false
  }
]

for attrs <- custom_field_configs do
  Membership.create_custom_field!(
    attrs,
    upsert?: true,
    upsert_identity: :unique_name,
    authorize?: false
  )
end

# 3. Admin email and password fallback for dev/test
admin_email = System.get_env("ADMIN_EMAIL") || "admin@localhost"
System.put_env("ADMIN_EMAIL", admin_email)

if mix_env in [:dev, :test] and is_nil(System.get_env("ADMIN_PASSWORD")) and
     is_nil(System.get_env("ADMIN_PASSWORD_FILE")) do
  System.put_env("ADMIN_PASSWORD", "testpassword")
end

# 4. Authorization roles (German descriptions)
role_configs = [
  %{
    name: "Mitglied",
    description: "Standardrolle für Mitglieder mit Zugriff nur auf die eigenen Daten",
    permission_set_name: "own_data",
    is_system_role: true
  },
  %{
    name: "Vorstand",
    description: "Vorstandsmitglied mit Lesezugriff auf alle Mitgliederdaten",
    permission_set_name: "read_only",
    is_system_role: false
  },
  %{
    name: "Kassenwart",
    description: "Kassenwart mit voller Mitglieder- und Zahlungsverwaltung",
    permission_set_name: "normal_user",
    is_system_role: false
  },
  %{
    name: "Buchhaltung",
    description: "Buchhaltung mit Lesezugriff für Prüfungen",
    permission_set_name: "read_only",
    is_system_role: false
  },
  %{
    name: "Admin",
    description: "Administrator mit uneingeschränktem Zugriff",
    permission_set_name: "admin",
    is_system_role: false
  }
]

Enum.each(role_configs, fn role_data ->
  role_name = role_data.name

  case Mv.Authorization.Role
       |> Ash.Query.filter(name == ^role_name)
       |> Ash.read_one(authorize?: false, domain: Mv.Authorization) do
    {:ok, existing_role} when not is_nil(existing_role) ->
      if existing_role.permission_set_name != role_data.permission_set_name or
           existing_role.description != role_data.description do
        existing_role
        |> Ash.Changeset.for_update(:update_role, %{
          description: role_data.description,
          permission_set_name: role_data.permission_set_name
        })
        |> Ash.update!(authorize?: false, domain: Mv.Authorization)
      end

    {:ok, nil} ->
      Mv.Authorization.Role
      |> Ash.Changeset.for_create(:create_role_with_system_flag, role_data)
      |> Ash.create!(authorize?: false, domain: Mv.Authorization)

    {:error, error} ->
      IO.puts("Warning: Failed to check for role #{role_data.name}: #{inspect(error)}")
  end
end)

admin_role =
  case Mv.Authorization.Role
       |> Ash.Query.filter(name == "Admin")
       |> Ash.read_one(authorize?: false, domain: Mv.Authorization) do
    {:ok, role} when not is_nil(role) -> role
    _ -> nil
  end

if is_nil(admin_role) do
  raise "Failed to create or find admin role. Cannot proceed with bootstrap."
end

# 5. Admin user
Mv.Release.seed_admin()

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)

    {:ok, nil} ->
      raise "Admin user not found after creation/assignment"

    {:error, error} ->
      raise "Failed to load admin user: #{inspect(error)}"
  end

# 6. System user
system_user_email = Mv.Helpers.SystemActor.system_user_email()

case Accounts.User
     |> Ash.Query.filter(email == ^system_user_email)
     |> Ash.read_one(domain: Mv.Accounts, authorize?: false) do
  {:ok, existing_system_user} when not is_nil(existing_system_user) ->
    existing_system_user
    |> Ash.Changeset.for_update(:update_internal, %{})
    |> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
    |> Ash.update!(authorize?: false)

  {:ok, nil} ->
    Accounts.create_user!(%{email: system_user_email},
      upsert?: true,
      upsert_identity: :unique_email,
      authorize?: false
    )
    |> Ash.Changeset.for_update(:update_internal, %{})
    |> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
    |> Ash.update!(authorize?: false)

  {:error, error} ->
    IO.puts("Warning: Failed to create system user: #{inspect(error)}")
    IO.puts("SystemActor will fall back to admin user (#{admin_email})")
end

# 7. Global settings (with default membership fee type and default field visibility)
# By default hide exit_date, notes, country, membership_fee_start_date in overview (like exit_date).
default_club_name = System.get_env("ASSOCIATION_NAME") || "Club Name"

default_hidden_in_overview = %{
  "exit_date" => false,
  "notes" => false,
  "country" => false,
  "membership_fee_start_date" => false
}

# Default join form field selection (email + name + address + join_date); join form stays disabled.
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, existing_settings} ->
    updates =
      %{}
      |> then(fn acc ->
        if existing_settings.club_name != default_club_name,
          do: Map.put(acc, :club_name, default_club_name),
          else: acc
      end)
      |> then(fn acc ->
        if existing_settings.default_membership_fee_type_id != default_fee_type.id,
          do: Map.put(acc, :default_membership_fee_type_id, default_fee_type.id),
          else: acc
      end)
      |> then(fn acc ->
        visibility_config = existing_settings.member_field_visibility || %{}
        # Ensure default-hidden fields are set if not already present (string or atom keys)
        has_key = fn vis, k ->
          try do
            Map.has_key?(vis, k) or Map.has_key?(vis, String.to_existing_atom(k))
          rescue
            ArgumentError -> Map.has_key?(vis, k)
          end
        end

        merged =
          Enum.reduce(default_hidden_in_overview, visibility_config, fn {key, val}, vis ->
            if has_key.(vis, key), do: vis, else: Map.put(vis, key, val)
          end)

        if merged != visibility_config,
          do: Map.put(acc, :member_field_visibility, merged),
          else: acc
      end)

    if map_size(updates) > 0 do
      {:ok, _} = Membership.update_settings(existing_settings, updates)
    end

  {:ok, nil} ->
    {:ok, _} =
      Membership.Setting
      |> Ash.Changeset.for_create(:create, %{
        club_name: default_club_name,
        member_field_visibility: default_hidden_in_overview,
        default_membership_fee_type_id: default_fee_type.id,
        join_form_field_ids: default_join_form_field_ids,
        join_form_field_required: default_join_form_field_required
      })
      |> Ash.create!()
end

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

IO.puts(
  "   - Fee types: 5 (Standard, Ermäßigt, Unterstützer, Fördermitglied, Probemitgliedschaft)"
)

IO.puts("   - Custom fields: 6 (Geburtsdatum shown in overview; others hidden by default)")

IO.puts("   - Roles: 5 (Mitglied, Vorstand, Kassenwart, Buchhaltung, Admin)")
IO.puts("   - Default fee type: Standard (120€ yearly)")
