# typed: strict
class IpAddressRole < ReduxApplicationRecord
  extend T::Sig

  # Only roles that exist in the User model are allowed
  # Only allow 'user' role for now, as it is the least privileged
  enum :role, { user: "user" }, default: :user

  validates :ip_address, presence: true, uniqueness: true
  validates :role, presence: true
  validate :no_overlapping_ip_ranges

  # Find an IpAddressRole for a given IP address
  # If no match is found, returns nil (similar to current_user behavior)
  sig { params(ip_addr: String).returns(T.nilable(IpAddressRole)) }
  def self.for_ip(ip_addr)
    begin
      # Find the first active role where the IP is contained in the range
      # Using PostgreSQL's >>= operator (contains or equals)
      where("ip_address >>= ?::inet", ip_addr).where(active: true).first
    rescue ActiveRecord::StatementInvalid
      # If the IP address is invalid, return nil
      nil
    end
  end

  sig { returns(T::Boolean) }
  def admin?
    false
  end

  sig { returns(T::Boolean) }
  def moderator?
    false
  end

  private

  # Custom validation to prevent overlapping IP ranges
  sig { void }
  def no_overlapping_ip_ranges
    return if ip_address.blank? # Skip if IP address is not provided

    # Skip validation if this record is already persisted and IP hasn't changed
    return if persisted? && !ip_address_changed?

    # Check for any existing IP ranges that overlap with this one
    # The PostgreSQL && operator returns true if there is any overlap between the CIDR ranges
    # Use string for the IP address to avoid the IPAddr casting issue
    overlapping_roles =
      IpAddressRole.where("ip_address && '#{ip_address}'::inet")

    # Exclude self if updating an existing record
    overlapping_roles = overlapping_roles.where.not(id: id) if persisted?

    if overlapping_roles.exists?
      errors.add(:ip_address, "overlaps with an existing IP range")
    end
  end
end
