defmodule DaProductApp.TerminalManagement.TerminalEventDispatcher do @moduledoc """ Event dispatcher for terminal changes that automatically triggers group rule evaluation. """ require Logger alias DaProductApp.TerminalManagement.TerminalGroupService alias Phoenix.PubSub @pubsub DaProductApp.PubSub @topic "terminal:events" # ============================================================================ # Event Publishing # ============================================================================ @doc "Dispatch event when terminal is created" def terminal_created(terminal) do Logger.info("Terminal created: #{terminal.serial_number}") event = %{ type: :terminal_created, terminal_id: terminal.id, terminal: terminal, timestamp: DateTime.utc_now() } publish_event(event) apply_rules_async(terminal) end @doc "Dispatch event when terminal is updated" def terminal_updated(old_terminal, new_terminal) do Logger.info("Terminal updated: #{new_terminal.serial_number}") # Check if relevant fields changed relevant_changes = detect_relevant_changes(old_terminal, new_terminal) if relevant_changes != [] do event = %{ type: :terminal_updated, terminal_id: new_terminal.id, old_terminal: old_terminal, new_terminal: new_terminal, changes: relevant_changes, timestamp: DateTime.utc_now() } publish_event(event) # Remove old assignments and reapply rules TerminalGroupService.remove_terminal_from_all_groups(new_terminal.id) apply_rules_async(new_terminal) end end @doc "Dispatch event when terminal status changes" def terminal_status_changed(terminal, old_status, new_status) do Logger.info("Terminal status changed: #{terminal.serial_number} #{old_status} -> #{new_status}") event = %{ type: :terminal_status_changed, terminal_id: terminal.id, terminal: terminal, old_status: old_status, new_status: new_status, timestamp: DateTime.utc_now() } publish_event(event) # Status changes always trigger rule re-evaluation TerminalGroupService.remove_terminal_from_all_groups(terminal.id) apply_rules_async(terminal) end @doc "Dispatch event when terminal is deleted" def terminal_deleted(terminal) do Logger.info("Terminal deleted: #{terminal.serial_number}") event = %{ type: :terminal_deleted, terminal_id: terminal.id, terminal: terminal, timestamp: DateTime.utc_now() } publish_event(event) # Clean up all group memberships TerminalGroupService.remove_terminal_from_all_groups(terminal.id) end # ============================================================================ # Rule Events # ============================================================================ @doc "Dispatch event when new rule is created" def rule_created(rule) do Logger.info("Rule created: #{rule.rule_name} for group #{rule.group_id}") event = %{ type: :rule_created, rule_id: rule.id, group_id: rule.group_id, rule: rule, timestamp: DateTime.utc_now() } publish_event(event) # Apply new rule to all existing terminals Task.start(fn -> TerminalGroupService.apply_rule_to_all_terminals(rule) end) end @doc "Dispatch event when rule is updated" def rule_updated(old_rule, new_rule) do Logger.info("Rule updated: #{new_rule.rule_name}") event = %{ type: :rule_updated, rule_id: new_rule.id, group_id: new_rule.group_id, old_rule: old_rule, new_rule: new_rule, timestamp: DateTime.utc_now() } publish_event(event) # Remove old assignments and reapply updated rule Task.start(fn -> TerminalGroupService.remove_rule_assignments(old_rule.id) TerminalGroupService.apply_rule_to_all_terminals(new_rule) end) end @doc "Dispatch event when rule is deleted" def rule_deleted(rule) do Logger.info("Rule deleted: #{rule.rule_name}") event = %{ type: :rule_deleted, rule_id: rule.id, group_id: rule.group_id, rule: rule, timestamp: DateTime.utc_now() } publish_event(event) # Clean up assignments created by this rule TerminalGroupService.remove_rule_assignments(rule.id) end # ============================================================================ # Private Functions # ============================================================================ defp publish_event(event) do # Publish to PubSub for real-time UI updates PubSub.broadcast(@pubsub, @topic, event) PubSub.broadcast(@pubsub, "tms:groups", {:group_change, event}) # Log event for audit trail Logger.info("Event published: #{event.type}", event: event) end defp apply_rules_async(terminal) do # Apply rules in background to avoid blocking the main process Task.start(fn -> case TerminalGroupService.apply_rules_to_terminal(terminal.id) do {:ok, count} -> Logger.info("Applied #{count} rules to terminal #{terminal.serial_number}") {:error, reason} -> Logger.error("Failed to apply rules to terminal #{terminal.serial_number}: #{inspect(reason)}") end end) end defp detect_relevant_changes(old_terminal, new_terminal) do # Fields that trigger rule re-evaluation relevant_fields = [ :status, :vendor, :model, :area, :location_code, :merchant_id, :deployment_type, :tier, :app_version, :system_version, :hardware_version ] Enum.filter(relevant_fields, fn field -> Map.get(old_terminal, field) != Map.get(new_terminal, field) end) end end