defmodule Towerops.Security.IpWhitelist do
  @moduledoc """
  Schema for tracking IP addresses and CIDR ranges that are exempt from brute force protection.
  """

  use Ecto.Schema

  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id

  schema "ip_whitelist" do
    field :ip_or_cidr, :string
    field :description, :string

    belongs_to :added_by, Towerops.Accounts.User

    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(ip_whitelist, attrs) do
    ip_whitelist
    |> cast(attrs, [:ip_or_cidr, :description, :added_by_id])
    |> validate_required([:ip_or_cidr])
    |> validate_ip_or_cidr()
    |> unique_constraint(:ip_or_cidr)
  end

  defp validate_ip_or_cidr(changeset) do
    validate_change(changeset, :ip_or_cidr, fn :ip_or_cidr, value ->
      validate_ip_or_cidr_value(value)
    end)
  end

  defp validate_ip_or_cidr_value(value) do
    # Check if it's a CIDR range
    if String.contains?(value, "/") do
      case InetCidr.parse_cidr(value) do
        {:ok, _} -> []
        {:error, _} -> [ip_or_cidr: "is not a valid CIDR range"]
      end
    else
      # Check if it's a valid IP address
      case :inet.parse_address(String.to_charlist(value)) do
        {:ok, _} -> []
        {:error, _} -> [ip_or_cidr: "is not a valid IP address or CIDR range"]
      end
    end
  end
end
