defmodule Towerops.Security.IpBlock do
  @moduledoc """
  Schema for tracking IP addresses that have been blocked due to brute force scanning behavior.

  ## Escalation Process

  1. First offense: 5-minute ban
  2. Second offense (within 30 days): 1-hour ban
  3. Third offense (within 30 days): Permanent ban + Cloudflare WAF push

  Offense count resets to 1 if 30 days pass without violations.
  """

  use Ecto.Schema

  import Ecto.Changeset

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

  schema "ip_blocks" do
    field :ip_address, :string
    field :offense_count, :integer, default: 1
    field :banned_until, :utc_datetime
    field :last_violation_at, :utc_datetime
    field :reason, :string

    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(ip_block, attrs) do
    ip_block
    |> cast(attrs, [:ip_address, :offense_count, :banned_until, :last_violation_at, :reason])
    |> validate_required([:ip_address, :offense_count, :last_violation_at])
    |> validate_number(:offense_count, greater_than: 0)
    |> unique_constraint(:ip_address)
  end

  @doc """
  Returns true if this is a permanent ban (offense_count >= 3 or banned_until is nil with offense_count > 1).
  """
  def permanent?(%__MODULE__{offense_count: count, banned_until: nil}) when count >= 3, do: true
  def permanent?(_), do: false

  @doc """
  Returns the ban duration as a human-readable string.
  """
  def ban_duration(%__MODULE__{banned_until: nil, offense_count: count}) when count >= 3 do
    "Permanent"
  end

  def ban_duration(%__MODULE__{banned_until: banned_until}) when not is_nil(banned_until) do
    now = DateTime.utc_now()

    if DateTime.before?(banned_until, now) do
      "Expired"
    else
      diff = DateTime.diff(banned_until, now, :second)

      cond do
        diff < 60 -> "#{diff}s"
        diff < 3600 -> "#{div(diff, 60)}m"
        diff < 86_400 -> "#{div(diff, 3600)}h"
        true -> "#{div(diff, 86_400)}d"
      end
    end
  end

  def ban_duration(_), do: "Unknown"
end
