defmodule Towerops.Contexts.ConfigChangeTracker do
  @moduledoc """
  Tracks configuration changes for SNMP and MikroTik settings across
  organizations and sites to trigger device propagation.

  Provides functions to capture configuration state before updates and
  detect which configuration fields changed after updates.
  """

  @config_fields [
    :snmp_community,
    :mikrotik_username,
    :mikrotik_password,
    :mikrotik_port,
    :mikrotik_use_ssl,
    :mikrotik_enabled
  ]

  @doc """
  Captures the current configuration state from a struct.

  Returns a map with all tracked configuration fields.

  ## Examples

      iex> org = %Organization{snmp_community: "public", mikrotik_username: "admin"}
      iex> ConfigChangeTracker.capture_config_state(org)
      %{
        snmp_community: "public",
        mikrotik_username: "admin",
        mikrotik_password: nil,
        mikrotik_port: nil,
        mikrotik_use_ssl: nil,
        mikrotik_enabled: nil
      }
  """
  @spec capture_config_state(struct()) :: map()
  def capture_config_state(struct) do
    Map.new(@config_fields, fn field ->
      {field, Map.get(struct, field)}
    end)
  end

  @doc """
  Checks if the SNMP community string changed.

  ## Examples

      iex> old_state = %{snmp_community: "public"}
      iex> updated = %Organization{snmp_community: "private"}
      iex> ConfigChangeTracker.snmp_changed?(updated, old_state)
      true
  """
  @spec snmp_changed?(struct(), map()) :: boolean()
  def snmp_changed?(updated_struct, old_state) do
    Map.get(updated_struct, :snmp_community) != old_state.snmp_community
  end

  @doc """
  Checks if any MikroTik configuration field changed.

  Compares all MikroTik-related fields (username, password, port, ssl, enabled)
  except snmp_community.

  ## Examples

      iex> old_state = %{mikrotik_username: "admin", mikrotik_password: "pass"}
      iex> updated = %Organization{mikrotik_username: "root", mikrotik_password: "pass"}
      iex> ConfigChangeTracker.mikrotik_changed?(updated, old_state)
      true
  """
  @spec mikrotik_changed?(struct(), map()) :: boolean()
  def mikrotik_changed?(updated_struct, old_state) do
    Enum.any?(@config_fields, fn field ->
      case field do
        :snmp_community -> false
        field -> Map.get(updated_struct, field) != old_state[field]
      end
    end)
  end

  @doc """
  Extracts MikroTik configuration attributes from a struct.

  Returns a map with all MikroTik-related fields suitable for device propagation.

  ## Examples

      iex> site = %Site{mikrotik_username: "admin", mikrotik_port: 8729}
      iex> ConfigChangeTracker.extract_mikrotik_attrs(site)
      %{
        mikrotik_username: "admin",
        mikrotik_password: nil,
        mikrotik_port: 8729,
        mikrotik_use_ssl: nil,
        mikrotik_enabled: nil
      }
  """
  @spec extract_mikrotik_attrs(struct()) :: map()
  def extract_mikrotik_attrs(struct) do
    %{
      mikrotik_username: Map.get(struct, :mikrotik_username),
      mikrotik_password: Map.get(struct, :mikrotik_password),
      mikrotik_port: Map.get(struct, :mikrotik_port),
      mikrotik_use_ssl: Map.get(struct, :mikrotik_use_ssl),
      mikrotik_enabled: Map.get(struct, :mikrotik_enabled)
    }
  end
end
