This file is a merged representation of the entire codebase, combined into a single document by Repomix.
The content has been processed where content has been compressed (code blocks are separated by ⋮---- delimiter).

<file_summary>
This section contains a summary of this file.

<purpose>
This file contains a packed representation of the entire repository's contents.
It is designed to be easily consumable by AI systems for analysis, code review,
or other automated processes.
</purpose>

<file_format>
The content is organized as follows:
1. This summary section
2. Repository information
3. Directory structure
4. Repository files (if enabled)
5. Multiple file entries, each consisting of:
  - File path as an attribute
  - Full contents of the file
</file_format>

<usage_guidelines>
- This file should be treated as read-only. Any changes should be made to the
  original repository files, not this packed version.
- When processing this file, use the file path to distinguish
  between different files in the repository.
- Be aware that this file may contain sensitive information. Handle it with
  the same level of security as you would the original repository.
</usage_guidelines>

<notes>
- Some files may have been excluded based on .gitignore rules and Repomix's configuration
- Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files
- Files matching patterns in .gitignore are excluded
- Files matching default ignore patterns are excluded
- Content has been compressed - code blocks are separated by ⋮---- delimiter
- Files are sorted by Git change count (files with more changes are at the bottom)
</notes>

</file_summary>

<directory_structure>
foundation/
  contracts/
    configurable.ex
    event_store.ex
    telemetry.ex
  infrastructure/
    pool_workers/
      http_worker.ex
    circuit_breaker.ex
    connection_manager.ex
    infrastructure.ex
    rate_limiter.ex
  logic/
    config_logic.ex
    event_logic.ex
  services/
    config_server.ex
    event_store.ex
    telemetry_service.ex
  types/
    config.ex
    error.ex
    event.ex
  validation/
    config_validator.ex
    event_validator.ex
  application.ex
  config.ex
  error_context.ex
  error.ex
  events.ex
  graceful_degradation.ex
  process_registry.ex
  service_registry.ex
  telemetry.ex
  utils.ex
foundation.ex
</directory_structure>

<files>
This section contains the contents of the repository's files.

<file path="foundation/contracts/configurable.ex">
defmodule Foundation.Contracts.Configurable do
  @moduledoc """
  Behaviour contract for configuration providers.

  Defines the interface that all configuration implementations must follow.
  Ensures consistent API across different configuration backends.
  """

  alias Foundation.Types.{Config, Error}

  @type config_path :: [atom()]
  @type config_value :: term()

  @doc """
  Get the complete configuration.
  """
  @callback get() :: {:ok, Config.t()} | {:error, Error.t()}

  @doc """
  Get a configuration value by path.
  """
  @callback get(config_path()) :: {:ok, config_value()} | {:error, Error.t()}

  @doc """
  Update a configuration value at the given path.
  """
  @callback update(config_path(), config_value()) :: :ok | {:error, Error.t()}

  @doc """
  Validate a configuration structure.
  """
  @callback validate(Config.t()) :: :ok | {:error, Error.t()}

  @doc """
  Get the list of paths that can be updated at runtime.
  """
  @callback updatable_paths() :: [config_path()]

  @doc """
  Reset configuration to defaults.
  """
  @callback reset() :: :ok | {:error, Error.t()}

  @doc """
  Check if the configuration service is available.
  """
  @callback available?() :: boolean()
end
</file>

<file path="foundation/contracts/event_store.ex">
defmodule Foundation.Contracts.EventStore do
  @moduledoc """
  Behaviour contract for event storage implementations.

  Defines the interface for event persistence, retrieval, and management.
  Supports different storage backends (memory, disk, distributed).
  """

  alias Foundation.Types.{Event, Error}

  @type event_id :: Event.event_id()
  @type correlation_id :: Event.correlation_id()
  @type event_filter :: keyword()
  @type event_query :: map()

  @doc """
  Store a single event.
  """
  @callback store(Event.t()) :: {:ok, event_id()} | {:error, Error.t()}

  @doc """
  Store multiple events atomically.
  """
  @callback store_batch([Event.t()]) :: {:ok, [event_id()]} | {:error, Error.t()}

  @doc """
  Retrieve an event by ID.
  """
  @callback get(event_id()) :: {:ok, Event.t()} | {:error, Error.t()}

  @doc """
  Query events with filters and pagination.
  """
  @callback query(event_query()) :: {:ok, [Event.t()]} | {:error, Error.t()}

  @doc """
  Get events by correlation ID.
  """
  @callback get_by_correlation(correlation_id()) :: {:ok, [Event.t()]} | {:error, Error.t()}

  @doc """
  Delete events older than the specified timestamp.
  """
  @callback prune_before(integer()) :: {:ok, non_neg_integer()} | {:error, Error.t()}

  @doc """
  Get storage statistics.
  """
  @callback stats() :: {:ok, map()} | {:error, Error.t()}

  @doc """
  Check if event store is available.
  """
  @callback available?() :: boolean()

  @doc """
  Initialize the event store service.
  """
  @callback initialize() :: :ok | {:error, Error.t()}

  @doc """
  Get event store service status.
  """
  @callback status() :: {:ok, map()} | {:error, Error.t()}
end
</file>

<file path="foundation/contracts/telemetry.ex">
defmodule Foundation.Contracts.Telemetry do
  @moduledoc """
  Behaviour contract for telemetry implementations.

  Defines the interface for metrics collection, event emission,
  and monitoring across different telemetry backends.
  """

  alias Foundation.Types.Error

  @type event_name :: [atom()]
  @type measurements :: map()
  @type metadata :: map()
  @type metric_value :: number()

  @doc """
  Execute telemetry event with measurements.
  """
  @callback execute(event_name(), measurements(), metadata()) :: :ok

  @doc """
  Measure execution time and emit results.
  """
  @callback measure(event_name(), metadata(), (-> result)) :: result when result: var

  @doc """
  Emit a counter metric.
  """
  @callback emit_counter(event_name(), metadata()) :: :ok

  @doc """
  Emit a gauge metric.
  """
  @callback emit_gauge(event_name(), metric_value(), metadata()) :: :ok

  @doc """
  Get collected metrics.
  """
  @callback get_metrics() :: {:ok, map()} | {:error, Error.t()}

  @doc """
  Attach event handlers for specific events.
  """
  @callback attach_handlers([event_name()]) :: :ok | {:error, Error.t()}

  @doc """
  Detach event handlers.
  """
  @callback detach_handlers([event_name()]) :: :ok

  @doc """
  Check if telemetry is available.
  """
  @callback available?() :: boolean()

  @doc """
  Initialize the telemetry service.
  """
  @callback initialize() :: :ok | {:error, Error.t()}

  @doc """
  Get telemetry service status.
  """
  @callback status() :: {:ok, map()} | {:error, Error.t()}
end
</file>

<file path="foundation/infrastructure/pool_workers/http_worker.ex">
defmodule Foundation.Infrastructure.PoolWorkers.HttpWorker do
  @moduledoc """
  Sample HTTP connection pool worker for demonstrating connection pooling patterns.

  This worker maintains persistent HTTP connections and provides a reusable
  template for implementing custom pool workers for different resource types.

  ## Usage

      # Start pool with HTTP workers
      ConnectionManager.start_pool(:http_pool, [
        size: 10,
        max_overflow: 5,
        worker_module: Foundation.Infrastructure.PoolWorkers.HttpWorker,
        worker_args: [base_url: "https://api.example.com", timeout: 30_000]
      ])

      # Use pooled connection
      ConnectionManager.with_connection(:http_pool, fn worker ->
        HttpWorker.get(worker, "/users/123")
      end)

  ## Worker Configuration

  - `:base_url` - Base URL for HTTP requests
  - `:timeout` - Request timeout in milliseconds
  - `:headers` - Default headers for all requests
  - `:max_redirects` - Maximum number of redirects to follow
  """

  use GenServer
  require Logger

  @type worker_config :: [
          base_url: String.t(),
          timeout: timeout(),
          headers: [{String.t(), String.t()}],
          max_redirects: non_neg_integer()
        ]

  @default_config [
    timeout: 30_000,
    headers: [{"User-Agent", "Foundation/1.0"}],
    max_redirects: 5
  ]

  ## Public API

  @doc """
  Starts an HTTP worker with the given configuration.

  This function is called by Poolboy to create worker instances.
  """
  @spec start_link(worker_config()) :: GenServer.on_start()
  def start_link(config) do
    GenServer.start_link(__MODULE__, config)
  end

  @doc """
  Performs a GET request using the pooled worker.

  ## Parameters
  - `worker` - Worker PID from the pool
  - `path` - Request path (relative to base_url)
  - `options` - Request options (headers, params, etc.)

  ## Returns
  - `{:ok, response}` - Request successful
  - `{:error, reason}` - Request failed
  """
  @spec get(pid(), String.t(), keyword()) :: {:ok, map()} | {:error, term()}
  def get(worker, path, options \\ []) do
    GenServer.call(worker, {:get, path, options})
  end

  @doc """
  Performs a POST request using the pooled worker.

  ## Parameters
  - `worker` - Worker PID from the pool
  - `path` - Request path (relative to base_url)
  - `body` - Request body (will be JSON encoded)
  - `options` - Request options (headers, etc.)

  ## Returns
  - `{:ok, response}` - Request successful
  - `{:error, reason}` - Request failed
  """
  @spec post(pid(), String.t(), term(), keyword()) :: {:ok, map()} | {:error, term()}
  def post(worker, path, body, options \\ []) do
    GenServer.call(worker, {:post, path, body, options})
  end

  @doc """
  Gets the current status and configuration of the worker.

  ## Parameters
  - `worker` - Worker PID from the pool

  ## Returns
  - `{:ok, status}` - Worker status information
  """
  @spec get_status(pid()) :: {:ok, map()}
  def get_status(worker) do
    GenServer.call(worker, :get_status)
  end

  ## GenServer Implementation

  @typep state :: %{
           base_url: String.t(),
           timeout: timeout(),
           headers: [{String.t(), String.t()}],
           max_redirects: non_neg_integer(),
           stats: %{
             requests_made: non_neg_integer(),
             last_request_at: DateTime.t() | nil,
             errors: non_neg_integer()
           }
         }

  @impl GenServer
  def init(config) do
    merged_config = Keyword.merge(@default_config, config)

    case Keyword.get(merged_config, :base_url) do
      nil ->
        {:stop, {:invalid_config, :missing_base_url}}

      base_url ->
        # Basic URL validation
        case validate_base_url(base_url) do
          :ok ->
            state = %{
              base_url: base_url,
              timeout: Keyword.get(merged_config, :timeout),
              headers: Keyword.get(merged_config, :headers),
              max_redirects: Keyword.get(merged_config, :max_redirects),
              stats: %{
                requests_made: 0,
                last_request_at: nil,
                errors: 0
              }
            }

            Logger.debug("HTTP worker started for #{state.base_url}")
            {:ok, state}

          {:error, reason} ->
            {:stop, {:invalid_config, reason}}
        end
    end
  end

  @impl GenServer
  def handle_call({:get, path, options}, _from, state) do
    case do_http_request(:get, path, nil, options, state) do
      {:ok, response, new_state} ->
        {:reply, {:ok, response}, new_state}

      {:error, reason, new_state} ->
        {:reply, {:error, reason}, new_state}
    end
  end

  @impl GenServer
  def handle_call({:post, path, body, options}, _from, state) do
    case do_http_request(:post, path, body, options, state) do
      {:ok, response, new_state} ->
        {:reply, {:ok, response}, new_state}

      {:error, reason, new_state} ->
        {:reply, {:error, reason}, new_state}
    end
  end

  @impl GenServer
  def handle_call(:get_status, _from, state) do
    status = %{
      base_url: state.base_url,
      timeout: state.timeout,
      headers: state.headers,
      stats: state.stats,
      uptime: get_uptime()
    }

    {:reply, {:ok, status}, state}
  end

  ## Private Functions

  @spec do_http_request(atom(), String.t(), term(), keyword(), state()) ::
          {:ok, map(), state()} | {:error, term(), state()}
  defp do_http_request(method, path, body, options, state) do
    base_url = build_url(state.base_url, path)

    # Handle query parameters
    url =
      case Keyword.get(options, :params) do
        nil ->
          base_url

        params when is_list(params) ->
          query_string = URI.encode_query(params)

          if String.contains?(base_url, "?") do
            "#{base_url}&#{query_string}"
          else
            "#{base_url}?#{query_string}"
          end

        _ ->
          base_url
      end

    headers = merge_headers(state.headers, Keyword.get(options, :headers, []))

    request_options = [
      timeout: state.timeout,
      max_redirects: state.max_redirects
    ]

    start_time = System.monotonic_time()

    try do
      case perform_request(method, url, body, headers, request_options) do
        {:ok, response} ->
          duration = System.monotonic_time() - start_time
          new_state = update_stats(state, :success, duration)

          Logger.debug("HTTP #{method} #{url} completed in #{duration}μs")
          {:ok, response, new_state}

        {:error, reason} ->
          duration = System.monotonic_time() - start_time
          new_state = update_stats(state, :error, duration)

          Logger.warning("HTTP #{method} #{url} failed: #{inspect(reason)}")
          {:error, reason, new_state}
      end
    rescue
      error ->
        duration = System.monotonic_time() - start_time
        new_state = update_stats(state, :error, duration)

        Logger.error("HTTP #{method} #{url} exception: #{inspect(error)}")
        {:error, {:exception, error}, new_state}
    end
  end

  @spec build_url(String.t(), String.t()) :: String.t()
  defp build_url(base_url, path) do
    base_url = String.trim_trailing(base_url, "/")
    path = String.trim_leading(path, "/")
    "#{base_url}/#{path}"
  end

  @spec merge_headers([{String.t(), String.t()}], [{String.t(), String.t()}]) ::
          [{String.t(), String.t()}]
  defp merge_headers(default_headers, request_headers) do
    # Request headers override default headers
    default_map = Enum.into(default_headers, %{})
    request_map = Enum.into(request_headers, %{})

    Map.merge(default_map, request_map)
    |> Enum.to_list()
  end

  @spec perform_request(atom(), String.t(), term(), [{String.t(), String.t()}], keyword()) ::
          {:ok, map()} | {:error, term()}
  defp perform_request(method, url, body, headers, options) do
    # This is a mock implementation - in real usage, you'd use HTTPoison, Finch, etc.
    # For demonstration purposes, we'll simulate HTTP requests with deterministic behavior

    # Check for timeout scenarios first
    timeout = Keyword.get(options, :timeout, 30_000)

    cond do
      String.contains?(url, "/delay/") ->
        # Extract delay seconds from URL pattern like /delay/5
        delay_match = Regex.run(~r"/delay/(\d+)", url)

        delay_seconds =
          case delay_match do
            [_, seconds_str] -> String.to_integer(seconds_str)
            _ -> 0
          end

        # Convert to milliseconds and check against timeout
        delay_ms = delay_seconds * 1000

        if delay_ms > timeout do
          {:error, :timeout}
        else
          Process.sleep(delay_ms)
          response_body = create_mock_response_body(method, url, headers, body)
          json_body = Jason.encode!(response_body)

          response = %{
            status_code: 200,
            headers: [{"content-type", "application/json"}],
            body: json_body
          }

          {:ok, response}
        end

      String.contains?(url, "/status/") ->
        # Extract status code from URL pattern like /status/404
        status_match = Regex.run(~r"/status/(\d+)", url)

        status_code =
          case status_match do
            [_, code_str] -> String.to_integer(code_str)
            _ -> 200
          end

        # Simulate network latency
        Process.sleep(Enum.random(10..100))

        response_body = create_mock_response_body(method, url, headers, body)
        json_body = Jason.encode!(response_body)

        response = %{
          status_code: status_code,
          headers: [{"content-type", "application/json"}],
          body: json_body
        }

        {:ok, response}

      String.contains?(url, "/bytes/") ->
        # Extract byte count from URL pattern like /bytes/10000
        bytes_match = Regex.run(~r"/bytes/(\d+)", url)

        byte_count =
          case bytes_match do
            [_, count_str] -> String.to_integer(count_str)
            _ -> 1024
          end

        # Simulate network latency
        Process.sleep(Enum.random(10..100))

        # Return raw bytes (not JSON)
        response = %{
          status_code: 200,
          headers: [{"content-type", "application/octet-stream"}],
          body: :crypto.strong_rand_bytes(byte_count)
        }

        {:ok, response}

      String.contains?(url, "/xml") ->
        # Simulate network latency
        Process.sleep(Enum.random(10..100))

        xml_response = """
        <?xml version="1.0" encoding="UTF-8"?>
        <response>
          <method>#{method}</method>
          <url>#{url}</url>
        </response>
        """

        response = %{
          status_code: 200,
          headers: [{"content-type", "application/xml"}],
          body: xml_response
        }

        {:ok, response}

      String.contains?(url, "/nonexistent") ->
        # Simulate network latency
        Process.sleep(Enum.random(10..100))

        response_body = create_mock_response_body(method, url, headers, body)
        json_body = Jason.encode!(response_body)

        response = %{
          status_code: 404,
          headers: [{"content-type", "application/json"}],
          body: json_body
        }

        {:ok, response}

      String.contains?(url, "invalid://") ->
        {:error, :invalid_url}

      String.contains?(url, "definitely-will-cause-error") ->
        {:error, {:http_error, 404, "Not Found"}}

      String.contains?(url, "invalid-network-request") ->
        {:error, :timeout}

      true ->
        # Simulate network latency
        Process.sleep(Enum.random(10..100))

        # Default success response - simulate httpbin.org behavior
        response_body = create_httpbin_response(method, url, headers, body)
        json_body = Jason.encode!(response_body)

        response = %{
          status_code: 200,
          headers: [{"content-type", "application/json"}],
          body: json_body
        }

        {:ok, response}
    end
  end

  # Helper function to create httpbin.org-style response
  defp create_httpbin_response(method, url, headers, body) do
    # Parse URL to extract query parameters
    uri = URI.parse(url)

    query_params =
      case uri.query do
        nil -> %{}
        query_string -> URI.decode_query(query_string)
      end

    # Convert headers list to map, ensuring proper key-value format
    headers_map =
      headers
      |> Enum.into(%{}, fn
        {key, value} when is_binary(key) and is_binary(value) -> {key, value}
        other -> {"unknown", inspect(other)}
      end)

    case method do
      :get ->
        %{
          "args" => query_params,
          "headers" => headers_map,
          "origin" => "127.0.0.1",
          "url" => url
        }

      :post ->
        # Handle string vs map body encoding
        data_string =
          case body do
            body when is_binary(body) -> body
            body -> Jason.encode!(body)
          end

        %{
          "args" => query_params,
          "data" => data_string,
          "files" => %{},
          "form" => %{},
          "headers" => headers_map,
          "json" => if(is_binary(body), do: nil, else: body),
          "origin" => "127.0.0.1",
          "url" => url
        }
    end
  end

  # Helper function to create basic mock response body
  defp create_mock_response_body(method, url, headers, body) do
    # Convert headers to a safe format for JSON encoding
    headers_map =
      headers
      |> Enum.into(%{}, fn
        {key, value} when is_binary(key) and is_binary(value) -> {key, value}
        other -> {"unknown", inspect(other)}
      end)

    %{
      method: method,
      url: url,
      timestamp: DateTime.utc_now(),
      headers: headers_map,
      body: body
    }
  end

  @spec update_stats(state(), :success | :error, integer()) :: state()
  defp update_stats(state, result, _duration) do
    new_stats = %{
      state.stats
      | requests_made: state.stats.requests_made + 1,
        last_request_at: DateTime.utc_now(),
        errors:
          case result do
            :success -> state.stats.errors
            :error -> state.stats.errors + 1
          end
    }

    %{state | stats: new_stats}
  end

  @spec validate_base_url(String.t()) :: :ok | {:error, term()}
  defp validate_base_url(base_url) do
    cond do
      is_nil(base_url) or base_url == "" ->
        {:error, :empty_base_url}

      not is_binary(base_url) ->
        {:error, :invalid_base_url_type}

      not String.starts_with?(base_url, ["http://", "https://"]) ->
        {:error, :invalid_scheme}

      true ->
        :ok
    end
  end

  @spec get_uptime() :: integer()
  defp get_uptime do
    # This is a simplified uptime calculation
    # In practice, you might store the start time in the state
    System.monotonic_time()
  end
end
</file>

<file path="foundation/infrastructure/circuit_breaker.ex">
defmodule Foundation.Infrastructure.CircuitBreaker do
  @moduledoc """
  Circuit breaker wrapper around :fuse library.

  Provides standardized circuit breaker functionality with telemetry integration
  and Foundation-specific error handling. Translates :fuse errors to 
  Foundation.Types.Error structures.

  ## Usage

      # Start a fuse instance
      {:ok, _pid} = CircuitBreaker.start_fuse_instance(:my_service, options)
      
      # Execute protected operation
      case CircuitBreaker.execute(:my_service, fn -> risky_operation() end) do
        {:ok, result} -> result
        {:error, error} -> handle_error(error)
      end
      
      # Check circuit status
      status = CircuitBreaker.get_status(:my_service)
  """

  alias Foundation.Types.Error
  alias Foundation.Telemetry

  @type fuse_name :: atom()
  @type fuse_options :: [
          strategy: :standard | :fault_injection,
          tolerance: non_neg_integer(),
          refresh: non_neg_integer()
        ]
  @type operation :: (-> any())
  @type operation_result :: {:ok, any()} | {:error, Error.t()}

  @doc """
  Start a new fuse instance with the given name and options.

  ## Parameters
  - `name`: Unique atom identifier for the fuse
  - `options`: Fuse configuration options

  ## Examples

      iex> CircuitBreaker.start_fuse_instance(:database, 
      ...>   strategy: :standard, tolerance: 5, refresh: 60_000)
      {:ok, #PID<0.123.0>}
  """
  @spec start_fuse_instance(fuse_name(), fuse_options()) :: :ok | {:error, Error.t()}
  def start_fuse_instance(name, options \\ []) when is_atom(name) do
    default_options = {{:standard, 5, 60_000}, {:reset, 60_000}}

    fuse_options =
      case options do
        [] ->
          default_options

        [strategy: :standard, tolerance: tolerance, refresh: refresh] ->
          {{:standard, tolerance, refresh}, {:reset, refresh}}

        _ ->
          default_options
      end

    try do
      case :fuse.install(name, fuse_options) do
        :ok ->
          emit_telemetry(:fuse_installed, %{name: name, options: fuse_options})
          :ok

        {:error, :already_installed} ->
          emit_telemetry(:fuse_already_installed, %{name: name})
          :ok

        {:error, reason} ->
          error =
            Error.new(
              code: 5001,
              error_type: :circuit_breaker_install_failed,
              message: "Failed to install circuit breaker: #{inspect(reason)}",
              severity: :high,
              context: %{fuse_name: name, reason: reason}
            )

          emit_telemetry(:fuse_install_failed, %{name: name, reason: reason})
          {:error, error}
      end
    rescue
      exception ->
        error =
          Error.new(
            code: 5002,
            error_type: :circuit_breaker_exception,
            message: "Exception during fuse installation: #{inspect(exception)}",
            severity: :critical,
            context: %{fuse_name: name, exception: exception}
          )

        emit_telemetry(:fuse_install_exception, %{name: name, exception: exception})
        {:error, error}
    end
  end

  @doc """
  Execute an operation protected by the circuit breaker.

  ## Parameters
  - `name`: Fuse instance name
  - `operation`: Function to execute
  - `metadata`: Additional telemetry metadata

  ## Examples

      iex> CircuitBreaker.execute(:database, fn -> DB.query("SELECT 1") end)
      {:ok, [%{column: 1}]}
      
      iex> CircuitBreaker.execute(:failing_service, fn -> raise "boom" end)
      {:error, %Error{error_type: :circuit_breaker_blown}}
  """
  @spec execute(fuse_name(), operation(), map()) :: operation_result()
  def execute(name, operation, metadata \\ %{})

  def execute(name, operation, metadata) when is_atom(name) and is_function(operation, 0) do
    start_time = System.monotonic_time(:microsecond)

    try do
      case :fuse.ask(name, :sync) do
        :ok ->
          # Circuit is closed, execute operation
          try do
            result = operation.()
            duration = System.monotonic_time(:microsecond) - start_time

            emit_telemetry(
              :call_executed,
              Map.merge(metadata, %{
                name: name,
                duration: duration,
                status: :success
              })
            )

            {:ok, result}
          rescue
            exception ->
              # Operation failed, melt the fuse
              :fuse.melt(name)
              duration = System.monotonic_time(:microsecond) - start_time

              error =
                Error.new(
                  code: 5003,
                  error_type: :protected_operation_failed,
                  message: "Protected operation failed: #{inspect(exception)}",
                  severity: :medium,
                  context: %{fuse_name: name, exception: exception}
                )

              emit_telemetry(
                :call_executed,
                Map.merge(metadata, %{
                  name: name,
                  duration: duration,
                  status: :failed,
                  exception: exception
                })
              )

              {:error, error}
          catch
            kind, value ->
              # Handle throw and exit
              :fuse.melt(name)
              duration = System.monotonic_time(:microsecond) - start_time

              error =
                Error.new(
                  code: 5012,
                  error_type: :protected_operation_failed,
                  message: "Protected operation failed with #{kind}: #{inspect(value)}",
                  severity: :medium,
                  context: %{fuse_name: name, kind: kind, value: value}
                )

              emit_telemetry(
                :call_executed,
                Map.merge(metadata, %{
                  name: name,
                  duration: duration,
                  status: :failed,
                  kind: kind,
                  value: value
                })
              )

              {:error, error}
          end

        :blown ->
          # Circuit is open
          error =
            Error.new(
              code: 5004,
              error_type: :circuit_breaker_blown,
              message: "Circuit breaker is open for #{name}",
              severity: :medium,
              context: %{fuse_name: name},
              retry_strategy: :fixed_delay
            )

          emit_telemetry(
            :call_rejected,
            Map.merge(metadata, %{
              name: name,
              reason: :circuit_blown
            })
          )

          {:error, error}

        {:error, :not_found} ->
          # Fuse not installed
          error =
            Error.new(
              code: 5005,
              error_type: :circuit_breaker_not_found,
              message: "Circuit breaker #{name} not found",
              severity: :high,
              context: %{fuse_name: name}
            )

          emit_telemetry(
            :call_rejected,
            Map.merge(metadata, %{
              name: name,
              reason: :not_found
            })
          )

          {:error, error}
      end
    rescue
      exception ->
        duration = System.monotonic_time(:microsecond) - start_time

        error =
          Error.new(
            code: 5006,
            error_type: :circuit_breaker_exception,
            message: "Exception in circuit breaker execution: #{inspect(exception)}",
            severity: :critical,
            context: %{fuse_name: name, exception: exception}
          )

        emit_telemetry(
          :call_executed,
          Map.merge(metadata, %{
            name: name,
            duration: duration,
            status: :exception,
            exception: exception
          })
        )

        {:error, error}
    end
  end

  def execute(name, operation, _metadata) do
    {:error,
     Error.new(
       code: 5009,
       error_type: :invalid_input,
       message:
         "Invalid circuit breaker inputs: name must be atom, operation must be 0-arity function",
       severity: :medium,
       context: %{
         name: name,
         operation_type: if(is_function(operation), do: :function, else: :not_function)
       }
     )}
  rescue
    _ ->
      {:error,
       Error.new(
         code: 5010,
         error_type: :invalid_input,
         message: "Invalid circuit breaker inputs",
         severity: :medium,
         context: %{name: name, operation: operation}
       )}
  end

  @doc """
  Get the current status of a circuit breaker.

  ## Parameters
  - `name`: Fuse instance name

  ## Returns
  - `:ok` - Circuit is closed (healthy)
  - `:blown` - Circuit is open (unhealthy)  
  - `{:error, Error.t()}` - Fuse not found or other error

  ## Examples

      iex> CircuitBreaker.get_status(:my_service)
      :ok
      
      iex> CircuitBreaker.get_status(:blown_service) 
      :blown
  """
  @spec get_status(fuse_name()) :: :ok | :blown | {:error, Error.t()}
  def get_status(name) when is_atom(name) do
    try do
      case :fuse.ask(name, :sync) do
        :ok ->
          :ok

        :blown ->
          :blown

        {:error, :not_found} ->
          error =
            Error.new(
              code: 5007,
              error_type: :circuit_breaker_not_found,
              message: "Circuit breaker #{name} not found",
              severity: :medium,
              context: %{fuse_name: name}
            )

          {:error, error}
      end
    rescue
      exception ->
        error =
          Error.new(
            code: 5008,
            error_type: :circuit_breaker_exception,
            message: "Exception checking circuit breaker status: #{inspect(exception)}",
            severity: :medium,
            context: %{fuse_name: name, exception: exception}
          )

        {:error, error}
    end
  end

  def get_status(name) do
    {:error,
     Error.new(
       code: 5011,
       error_type: :invalid_input,
       message: "Invalid circuit breaker name: must be an atom",
       severity: :medium,
       context: %{name: name}
     )}
  end

  @doc """
  Reset a blown circuit breaker manually.

  ## Parameters
  - `name`: Fuse instance name

  ## Examples

      iex> CircuitBreaker.reset(:my_service)
      :ok
  """
  @spec reset(fuse_name()) :: :ok | {:error, Error.t()}
  def reset(name) when is_atom(name) do
    try do
      case :fuse.reset(name) do
        :ok ->
          emit_telemetry(:state_change, %{name: name, new_state: :reset})
          :ok

        {:error, :not_found} ->
          error =
            Error.new(
              code: 5013,
              error_type: :circuit_breaker_not_found,
              message: "Cannot reset circuit breaker #{name}: not found",
              severity: :medium,
              context: %{fuse_name: name}
            )

          {:error, error}
      end
    rescue
      exception ->
        error =
          Error.new(
            code: 5014,
            error_type: :circuit_breaker_exception,
            message: "Exception resetting circuit breaker: #{inspect(exception)}",
            severity: :medium,
            context: %{fuse_name: name, exception: exception}
          )

        {:error, error}
    end
  end

  # Private helper functions

  @spec emit_telemetry(atom(), map()) :: :ok
  defp emit_telemetry(event_type, metadata) do
    event_name = [:foundation, :foundation, :infra, :circuit_breaker, event_type]
    Telemetry.emit_counter(event_name, metadata)
  end
end
</file>

<file path="foundation/infrastructure/connection_manager.ex">
defmodule Foundation.Infrastructure.ConnectionManager do
  @moduledoc """
  Connection pooling manager wrapping Poolboy for resource management.

  Provides a unified interface for managing connection pools across different
  resource types (database connections, HTTP clients, etc.) with proper
  lifecycle management and telemetry integration.

  ## Usage

      # Start a pool for database connections
      {:ok, pool_pid} = ConnectionManager.start_pool(:database, [
        size: 10,
        max_overflow: 5,
        worker_module: MyApp.DatabaseWorker,
        worker_args: [host: "localhost", port: 5432]
      ])

      # Execute work with a pooled connection
      result = ConnectionManager.with_connection(:database, fn worker ->
        GenServer.call(worker, {:query, "SELECT * FROM users"})
      end)

      # Get pool status
      status = ConnectionManager.get_pool_status(:database)

  ## Pool Configuration

  - `:size` - Initial pool size (default: 5)
  - `:max_overflow` - Maximum additional workers (default: 10)
  - `:worker_module` - Module implementing the worker behavior
  - `:worker_args` - Arguments passed to worker start_link/1
  - `:strategy` - Pool strategy (default: :lifo)

  ## Telemetry Events

  - `[:foundation, :foundation, :connection_pool, :checkout]` - Connection checked out
  - `[:foundation, :foundation, :connection_pool, :checkin]` - Connection returned
  - `[:foundation, :foundation, :connection_pool, :timeout]` - Checkout timeout
  - `[:foundation, :foundation, :connection_pool, :overflow]` - Pool overflow occurred
  """

  use GenServer
  require Logger

  alias Foundation.Services.TelemetryService

  @type pool_name :: atom()
  @type pool_config :: [
          size: non_neg_integer(),
          max_overflow: non_neg_integer(),
          worker_module: module(),
          worker_args: term(),
          strategy: :lifo | :fifo
        ]
  @type pool_status :: %{
          size: non_neg_integer(),
          overflow: non_neg_integer(),
          workers: non_neg_integer(),
          waiting: non_neg_integer(),
          monitors: non_neg_integer()
        }

  # Default pool configuration
  @default_config [
    size: 5,
    max_overflow: 10,
    strategy: :lifo
  ]

  @default_checkout_timeout 5_000

  ## Public API

  @doc """
  Starts the ConnectionManager GenServer.
  """
  @spec start_link(keyword()) :: GenServer.on_start()
  def start_link(opts \\ []) do
    GenServer.start_link(__MODULE__, opts, name: __MODULE__)
  end

  @doc """
  Starts a new connection pool with the given configuration.

  ## Parameters
  - `pool_name` - Unique identifier for the pool
  - `config` - Pool configuration options

  ## Returns
  - `{:ok, pid}` - Pool started successfully
  - `{:error, reason}` - Pool failed to start
  """
  @spec start_pool(pool_name(), pool_config()) :: {:ok, pid()} | {:error, term()}
  def start_pool(pool_name, config) do
    GenServer.call(__MODULE__, {:start_pool, pool_name, config})
  end

  @doc """
  Stops an existing connection pool.

  ## Parameters
  - `pool_name` - Pool identifier to stop

  ## Returns
  - `:ok` - Pool stopped successfully
  - `{:error, :not_found}` - Pool doesn't exist
  """
  @spec stop_pool(pool_name()) :: :ok | {:error, :not_found}
  def stop_pool(pool_name) do
    GenServer.call(__MODULE__, {:stop_pool, pool_name})
  end

  @doc """
  Executes a function with a connection from the specified pool.

  Automatically handles checkout/checkin and provides proper error handling
  with telemetry integration.

  ## Parameters
  - `pool_name` - Pool to get connection from
  - `fun` - Function to execute with the worker
  - `timeout` - Checkout timeout (default: 5000ms)

  ## Returns
  - `{:ok, result}` - Function executed successfully
  - `{:error, reason}` - Execution failed or pool unavailable
  """
  @spec with_connection(pool_name(), (pid() -> term()), timeout()) ::
          {:ok, term()} | {:error, term()}
  def with_connection(pool_name, fun, timeout \\ @default_checkout_timeout) do
    # Add buffer to GenServer timeout to account for processing overhead
    # But ensure it's reasonable - minimum 500ms buffer, maximum 2000ms buffer
    buffer = min(max(trunc(timeout * 0.2), 500), 2000)
    genserver_timeout = timeout + buffer
    GenServer.call(__MODULE__, {:with_connection, pool_name, fun, timeout}, genserver_timeout)
  end

  @doc """
  Gets the current status of a connection pool.

  ## Parameters
  - `pool_name` - Pool to get status for

  ## Returns
  - `{:ok, status}` - Pool status information
  - `{:error, :not_found}` - Pool doesn't exist
  """
  @spec get_pool_status(pool_name()) :: {:ok, pool_status()} | {:error, :not_found}
  def get_pool_status(pool_name) do
    GenServer.call(__MODULE__, {:get_pool_status, pool_name})
  end

  @doc """
  Lists all active connection pools.

  ## Returns
  - `[pool_name]` - List of active pool names
  """
  @spec list_pools() :: [pool_name()]
  def list_pools do
    GenServer.call(__MODULE__, :list_pools)
  end

  ## GenServer Implementation

  @impl GenServer
  def init(_opts) do
    state = %{
      pools: %{},
      configs: %{}
    }

    Logger.info("ConnectionManager started")
    {:ok, state}
  end

  @impl GenServer
  def handle_call({:start_pool, pool_name, config}, _from, state) do
    case Map.has_key?(state.pools, pool_name) do
      true ->
        {:reply, {:error, :already_exists}, state}

      false ->
        case do_start_pool(pool_name, config) do
          {:ok, pool_pid} ->
            new_state = %{
              state
              | pools: Map.put(state.pools, pool_name, pool_pid),
                configs: Map.put(state.configs, pool_name, config)
            }

            Logger.info("Started connection pool: #{pool_name}")
            emit_telemetry(:pool_started, %{}, %{pool_name: pool_name, config: config})

            {:reply, {:ok, pool_pid}, new_state}

          {:error, reason} ->
            Logger.error("Failed to start pool #{pool_name}: #{inspect(reason)}")
            {:reply, {:error, reason}, state}
        end
    end
  end

  @impl GenServer
  def handle_call({:stop_pool, pool_name}, _from, state) do
    case Map.get(state.pools, pool_name) do
      nil ->
        {:reply, {:error, :not_found}, state}

      pool_pid ->
        :poolboy.stop(pool_pid)

        new_state = %{
          state
          | pools: Map.delete(state.pools, pool_name),
            configs: Map.delete(state.configs, pool_name)
        }

        Logger.info("Stopped connection pool: #{pool_name}")
        emit_telemetry(:pool_stopped, %{}, %{pool_name: pool_name})

        {:reply, :ok, new_state}
    end
  end

  @impl GenServer
  def handle_call({:with_connection, pool_name, fun, timeout}, _from, state) do
    case Map.get(state.pools, pool_name) do
      nil ->
        {:reply, {:error, :pool_not_found}, state}

      pool_pid ->
        result = do_with_connection(pool_name, pool_pid, fun, timeout)
        {:reply, result, state}
    end
  end

  @impl GenServer
  def handle_call({:get_pool_status, pool_name}, _from, state) do
    case Map.get(state.pools, pool_name) do
      nil ->
        {:reply, {:error, :not_found}, state}

      pool_pid ->
        status = :poolboy.status(pool_pid)

        # poolboy.status returns a tuple: {state, size, workers, waiting}
        formatted_status =
          case status do
            {_state, size, workers, waiting} ->
              %{
                size: size,
                overflow: 0,
                workers: workers,
                waiting: waiting,
                monitors: 0
              }
          end

        {:reply, {:ok, formatted_status}, state}
    end
  end

  @impl GenServer
  def handle_call(:list_pools, _from, state) do
    pool_names = Map.keys(state.pools)
    {:reply, pool_names, state}
  end

  ## Private Functions

  @spec do_start_pool(pool_name(), pool_config()) :: {:ok, pid()} | {:error, term()}
  defp do_start_pool(pool_name, config) do
    # Validate configuration values
    case validate_pool_config(config) do
      :ok ->
        # Validate worker module exists before attempting to start pool
        worker_module = Keyword.get(config, :worker_module)

        case validate_worker_module(worker_module) do
          :ok ->
            {poolboy_config, worker_args} = build_poolboy_config(pool_name, config)

            case :poolboy.start_link(poolboy_config, worker_args) do
              {:ok, pid} -> {:ok, pid}
              {:error, reason} -> {:error, reason}
            end

          {:error, reason} ->
            {:error, reason}
        end

      {:error, reason} ->
        {:error, reason}
    end
  rescue
    error -> {:error, error}
  end

  @spec validate_pool_config(pool_config()) :: :ok | {:error, term()}
  defp validate_pool_config(config) do
    merged_config = Keyword.merge(@default_config, config)

    size = Keyword.get(merged_config, :size)
    max_overflow = Keyword.get(merged_config, :max_overflow)

    cond do
      not is_integer(size) or size < 0 ->
        {:error, {:invalid_config, :size, "Size must be a non-negative integer"}}

      not is_integer(max_overflow) or max_overflow < 0 ->
        {:error, {:invalid_config, :max_overflow, "Max overflow must be a non-negative integer"}}

      true ->
        :ok
    end
  end

  @spec validate_worker_module(module()) :: :ok | {:error, term()}
  defp validate_worker_module(worker_module) do
    case Code.ensure_compiled(worker_module) do
      {:module, _} -> :ok
      _ -> {:error, {:invalid_worker_module, worker_module}}
    end
  end

  @spec build_poolboy_config(pool_name(), pool_config()) :: {keyword(), keyword()}
  defp build_poolboy_config(pool_name, config) do
    merged_config = Keyword.merge(@default_config, config)

    poolboy_config = [
      name: {:local, pool_name},
      worker_module: Keyword.fetch!(merged_config, :worker_module),
      size: Keyword.get(merged_config, :size),
      max_overflow: Keyword.get(merged_config, :max_overflow),
      strategy: Keyword.get(merged_config, :strategy)
    ]

    worker_args = Keyword.get(merged_config, :worker_args, [])

    {poolboy_config, worker_args}
  end

  @spec do_with_connection(pool_name(), pid(), (pid() -> term()), timeout()) ::
          {:ok, term()} | {:error, term()}
  defp do_with_connection(pool_name, pool_pid, fun, timeout) do
    start_time = System.monotonic_time()

    try do
      worker = :poolboy.checkout(pool_pid, true, timeout)

      emit_telemetry(
        :checkout,
        %{
          checkout_time: System.monotonic_time() - start_time
        },
        %{pool_name: pool_name}
      )

      try do
        result = fun.(worker)
        {:ok, result}
      rescue
        error ->
          Logger.error("Function execution error in pool #{pool_name}: #{inspect(error)}")
          {:error, error}
      catch
        :exit, reason ->
          # If the worker process exits while we're calling it, treat it as a function result
          # This allows the GenServer.call to return its response before the process exits
          Logger.warning(
            "Worker process exited during call in pool #{pool_name}: #{inspect(reason)}"
          )

          {:error, reason}
      after
        :poolboy.checkin(pool_pid, worker)
        emit_telemetry(:checkin, %{}, %{pool_name: pool_name})
      end
    catch
      :exit, {:timeout, {GenServer, :call, _}} ->
        emit_telemetry(:timeout, %{timeout: timeout}, %{pool_name: pool_name})
        {:error, :checkout_timeout}

      :exit, {:timeout, _} ->
        emit_telemetry(:timeout, %{timeout: timeout}, %{pool_name: pool_name})
        {:error, :checkout_timeout}

      :exit, {:noproc, _} ->
        emit_telemetry(:timeout, %{timeout: timeout}, %{pool_name: pool_name})
        {:error, :checkout_timeout}

      :exit, reason ->
        Logger.error("Connection pool error for #{pool_name}: #{inspect(reason)}")
        {:error, reason}
    end
  end

  @spec emit_telemetry(atom(), map(), map()) :: :ok
  defp emit_telemetry(event, measurements, metadata) do
    TelemetryService.execute(
      [:foundation, :foundation, :connection_pool, event],
      measurements,
      metadata
    )
  end
end
</file>

<file path="foundation/infrastructure/infrastructure.ex">
defmodule Foundation.Infrastructure do
  @moduledoc """
  Unified infrastructure facade orchestrating multiple protection patterns.

  This module provides a single entry point for coordinating circuit breakers,
  rate limiting, and connection pooling to create resilient service operations.
  It implements the Facade pattern to simplify interaction with complex
  infrastructure components.

  ## Usage

      # Execute a protected operation with all safeguards
      result = Infrastructure.execute_protected(:external_api, [
        circuit_breaker: :api_breaker,
        rate_limiter: {:api_calls, "user:123"},
        connection_pool: :http_pool
      ], fn ->
        # Your operation here
        HTTPClient.get("/api/data")
      end)

      # Configure protection rules
      Infrastructure.configure_protection(:external_api, %{
        circuit_breaker: %{
          failure_threshold: 5,
          recovery_time: 30_000
        },
        rate_limiter: %{
          scale: 60_000,    # 1 minute
          limit: 100        # 100 requests per minute
        },
        connection_pool: %{
          size: 10,
          max_overflow: 5
        }
      })

  ## Protection Layers

  1. **Rate Limiting** - First line of defense, prevents overwhelming downstream
  2. **Circuit Breaker** - Fails fast when downstream is unhealthy
  3. **Connection Pool** - Manages resource allocation efficiently

  ## Telemetry Events

  - `[:foundation, :foundation, :infrastructure, :execute_start]`
  - `[:foundation, :foundation, :infrastructure, :execute_stop]`
  - `[:foundation, :foundation, :infrastructure, :execute_exception]`
  - `[:foundation, :foundation, :infrastructure, :protection_triggered]`
  """

  require Logger

  alias Foundation.Infrastructure.{CircuitBreaker, RateLimiter, ConnectionManager}
  alias Foundation.Services.{TelemetryService}
  alias Foundation.Types.Error

  @type protection_key :: atom()
  @type protection_options :: [
          circuit_breaker: atom(),
          rate_limiter: {atom(), binary()},
          connection_pool: atom(),
          timeout: timeout()
        ]
  @type protection_config :: %{
          circuit_breaker: map(),
          rate_limiter: map(),
          connection_pool: map()
        }
  @type execution_result :: {:ok, term()} | {:error, term()}

  @default_timeout 5_000
  @agent_name __MODULE__.ConfigAgent

  ## Public API

  @doc """
  Initialize all infrastructure components.

  Sets up supervision and configuration for Fuse, Hammer, and Poolboy.
  This function should be called during application startup.

  ## Examples

      iex> Infrastructure.initialize_all_infra_components()
      {:ok, []}
  """
  @spec initialize_all_infra_components() :: {:ok, []} | {:error, term()}
  def initialize_all_infra_components() do
    initialize_all_infra_components(%{})
  end

  @doc """
  Get the status of all infrastructure components.

  ## Examples

      iex> Infrastructure.get_infrastructure_status()
      {:ok, %{fuse: :running, hammer: :running}}
  """
  @spec get_infrastructure_status() :: {:ok, map()} | {:error, term()}
  def get_infrastructure_status() do
    try do
      fuse_status =
        case Process.whereis(:fuse_sup) do
          nil -> :not_started
          pid when is_pid(pid) -> :running
        end

      hammer_status =
        case Application.get_env(:hammer, :backend) do
          nil -> :not_configured
          _ -> :configured
        end

      status = %{
        fuse: fuse_status,
        hammer: hammer_status,
        timestamp: System.system_time(:millisecond)
      }

      {:ok, status}
    rescue
      exception ->
        {:error, {:infrastructure_status_error, exception}}
    end
  end

  @doc """
  Executes a function with comprehensive protection patterns applied.

  Applies protection layers in order: rate limiting → circuit breaker → connection pooling.
  Each layer can abort the execution early if protection rules are triggered.

  ## Parameters
  - `protection_key` - Identifier for protection configuration
  - `options` - Protection layer options
  - `fun` - Function to execute with protection

  ## Returns
  - `{:ok, result}` - Function executed successfully
  - `{:error, reason}` - Execution blocked or failed
  """
  @spec execute_protected(protection_key(), protection_options(), (-> term())) ::
          execution_result()
  def execute_protected(protection_key, options, fun) do
    start_time = System.monotonic_time()

    emit_telemetry(:execute_start, %{protection_key: protection_key}, %{
      options: sanitize_options(options)
    })

    try do
      with {:ok, _} <- check_rate_limit(options),
           {:ok, result} <- execute_with_circuit_breaker(options, fun) do
        duration = System.monotonic_time() - start_time

        emit_telemetry(:execute_stop, %{protection_key: protection_key}, %{
          duration: duration,
          success: true
        })

        {:ok, result}
      else
        {:error, reason} = error ->
          duration = System.monotonic_time() - start_time

          emit_telemetry(:execute_stop, %{protection_key: protection_key}, %{
            duration: duration,
            success: false,
            reason: reason
          })

          error
      end
    rescue
      error ->
        duration = System.monotonic_time() - start_time

        emit_telemetry(:execute_exception, %{protection_key: protection_key}, %{
          duration: duration,
          error: inspect(error)
        })

        {:error, {:exception, error}}
    end
  end

  @doc """
  Configures protection rules for a specific key.

  Stores configuration in internal state for runtime access and validation.

  ## Parameters
  - `protection_key` - Identifier for protection configuration
  - `config` - Protection layer configurations

  ## Returns
  - `:ok` - Configuration stored successfully
  - `{:error, reason}` - Configuration invalid or storage failed
  """
  @spec configure_protection(protection_key(), protection_config()) :: :ok | {:error, term()}
  def configure_protection(protection_key, config) do
    case validate_protection_config(config) do
      :ok ->
        # Ensure Agent is started
        ensure_config_agent_started()

        # Store in Agent instead of ConfigServer
        Agent.update(@agent_name, fn state ->
          Map.put(state, protection_key, config)
        end)

        Logger.info("Configured protection for #{protection_key}")
        :ok

      {:error, reason} ->
        Logger.warning("Invalid protection config for #{protection_key}: #{inspect(reason)}")
        {:error, reason}
    end
  end

  @doc """
  Gets the current protection configuration for a key.

  ## Parameters
  - `protection_key` - Identifier for protection configuration

  ## Returns
  - `{:ok, config}` - Current configuration
  - `{:error, :not_found}` - No configuration exists
  """
  @spec get_protection_config(protection_key()) :: {:ok, any()} | {:error, any()}
  def get_protection_config(protection_key) do
    case ensure_config_agent_started() do
      :ok ->
        case Agent.get(@agent_name, fn state -> Map.get(state, protection_key) end) do
          nil -> {:error, :not_found}
          config -> {:ok, config}
        end

      {:error, reason} ->
        {:error, reason}
    end
  end

  @doc """
  Gets comprehensive status of all protection layers for a key.

  ## Parameters
  - `protection_key` - Identifier for protection status

  ## Returns
  - `{:ok, status}` - Status of all protection layers
  - `{:error, reason}` - Status retrieval failed
  """
  @spec get_protection_status(protection_key()) :: {:ok, map()} | {:error, term()}
  def get_protection_status(protection_key) do
    with {:ok, config} <- get_protection_config(protection_key) do
      status = %{
        circuit_breaker: get_circuit_breaker_status(config),
        rate_limiter: get_rate_limiter_status(config),
        connection_pool: get_connection_pool_status(config)
      }

      {:ok, status}
    end
  end

  @doc """
  Lists all configured protection keys.

  ## Returns
  - `[protection_key]` - List of configured protection keys
  """
  @spec list_protection_keys() :: [protection_key()]
  def list_protection_keys do
    case ensure_config_agent_started() do
      :ok ->
        Agent.get(@agent_name, fn state -> Map.keys(state) end)

      {:error, _} ->
        []
    end
  end

  ## Private Functions

  @spec ensure_config_agent_started() :: :ok | {:error, term()}
  defp ensure_config_agent_started do
    case Process.whereis(@agent_name) do
      nil ->
        case Agent.start_link(fn -> %{} end, name: @agent_name) do
          {:ok, _pid} -> :ok
          {:error, {:already_started, _pid}} -> :ok
          {:error, reason} -> {:error, reason}
        end

      _pid ->
        :ok
    end
  end

  @spec check_rate_limit(protection_options()) :: {:ok, :allowed} | {:error, term()}
  defp check_rate_limit(options) do
    case Keyword.get(options, :rate_limiter) do
      nil ->
        {:ok, :allowed}

      {rule_name, identifier} ->
        case RateLimiter.check_rate(identifier, rule_name, 100, 60_000) do
          :ok ->
            {:ok, :allowed}

          {:error, %Error{error_type: :rate_limit_exceeded} = error} ->
            emit_protection_triggered(:rate_limit, %{
              rule_name: rule_name,
              identifier: identifier
            })

            {:error, error}

          {:error, reason} ->
            {:error, {:rate_limit_error, reason}}
        end
    end
  end

  @spec execute_with_circuit_breaker(protection_options(), (-> term())) ::
          {:ok, term()} | {:error, term()}
  defp execute_with_circuit_breaker(options, fun) do
    case Keyword.get(options, :circuit_breaker) do
      nil ->
        execute_with_connection_pool(options, fun)

      circuit_breaker_name ->
        case CircuitBreaker.execute(circuit_breaker_name, fn ->
               execute_with_connection_pool(options, fun)
             end) do
          {:ok, {:ok, result}} -> {:ok, result}
          {:ok, {:error, reason}} -> {:error, reason}
          other -> other
        end
    end
  end

  @spec execute_with_connection_pool(protection_options(), (-> term())) ::
          {:ok, term()} | {:error, term()}
  defp execute_with_connection_pool(options, fun) do
    case Keyword.get(options, :connection_pool) do
      nil ->
        try do
          result = fun.()
          {:ok, result}
        rescue
          error -> {:error, {:execution_error, error}}
        end

      pool_name ->
        timeout = Keyword.get(options, :timeout, @default_timeout)

        ConnectionManager.with_connection(
          pool_name,
          fn _worker ->
            fun.()
          end,
          timeout
        )
    end
  end

  @spec validate_protection_config(protection_config()) :: :ok | {:error, term()}
  defp validate_protection_config(config) when is_map(config) do
    required_keys = [:circuit_breaker, :rate_limiter, :connection_pool]

    case Enum.all?(required_keys, &Map.has_key?(config, &1)) do
      true -> validate_individual_configs(config)
      false -> {:error, :missing_required_keys}
    end
  end

  defp validate_protection_config(_), do: {:error, :invalid_config_format}

  @spec validate_individual_configs(protection_config()) :: :ok | {:error, term()}
  defp validate_individual_configs(config) do
    with :ok <- validate_circuit_breaker_config(config.circuit_breaker),
         :ok <- validate_rate_limiter_config(config.rate_limiter),
         :ok <- validate_connection_pool_config(config.connection_pool) do
      :ok
    end
  end

  @spec validate_circuit_breaker_config(map()) :: :ok | {:error, term()}
  defp validate_circuit_breaker_config(config) when is_map(config) do
    required_keys = [:failure_threshold, :recovery_time]

    case Enum.all?(required_keys, &Map.has_key?(config, &1)) do
      true -> :ok
      false -> {:error, :invalid_circuit_breaker_config}
    end
  end

  defp validate_circuit_breaker_config(_), do: {:error, :invalid_circuit_breaker_config}

  @spec validate_rate_limiter_config(map()) :: :ok | {:error, term()}
  defp validate_rate_limiter_config(config) when is_map(config) do
    required_keys = [:scale, :limit]

    case Enum.all?(required_keys, &Map.has_key?(config, &1)) do
      true -> :ok
      false -> {:error, :invalid_rate_limiter_config}
    end
  end

  defp validate_rate_limiter_config(_), do: {:error, :invalid_rate_limiter_config}

  @spec validate_connection_pool_config(map()) :: :ok | {:error, term()}
  defp validate_connection_pool_config(config) when is_map(config) do
    required_keys = [:size, :max_overflow]

    case Enum.all?(required_keys, &Map.has_key?(config, &1)) do
      true -> :ok
      false -> {:error, :invalid_connection_pool_config}
    end
  end

  defp validate_connection_pool_config(_), do: {:error, :invalid_connection_pool_config}

  @spec get_circuit_breaker_status(protection_config()) :: map()
  defp get_circuit_breaker_status(_config) do
    # This would integrate with actual circuit breaker status
    # For now, return placeholder status
    %{status: :unknown, message: "Circuit breaker status not implemented"}
  end

  @spec get_rate_limiter_status(protection_config()) :: map()
  defp get_rate_limiter_status(_config) do
    # This would integrate with actual rate limiter status
    # For now, return placeholder status
    %{status: :unknown, message: "Rate limiter status not implemented"}
  end

  @spec get_connection_pool_status(protection_config()) :: map()
  defp get_connection_pool_status(_config) do
    # This would integrate with actual connection pool status
    # For now, return placeholder status
    %{status: :unknown, message: "Connection pool status not implemented"}
  end

  @spec sanitize_options(protection_options()) :: map()
  defp sanitize_options(options) when is_list(options) do
    # Remove sensitive data from options for telemetry
    options
    |> Keyword.take([:circuit_breaker, :connection_pool, :timeout])
    |> Enum.into(%{})
  end

  defp sanitize_options(options), do: %{options: inspect(options)}

  @spec emit_protection_triggered(atom(), map()) :: :ok
  defp emit_protection_triggered(protection_type, metadata) do
    emit_telemetry(:protection_triggered, %{protection_type: protection_type}, metadata)
  end

  @spec emit_telemetry(atom(), map(), map()) :: :ok
  defp emit_telemetry(event, measurements, metadata) do
    TelemetryService.execute(
      [:foundation, :foundation, :infrastructure, event],
      measurements,
      metadata
    )
  end

  @spec initialize_all_infra_components(map()) :: {:ok, []} | {:error, term()}
  defp initialize_all_infra_components(config) do
    try do
      # Initialize Hammer configuration if not already done
      case Application.get_env(:hammer, :backend) do
        nil ->
          Application.put_env(:hammer, :backend, {Hammer.Backend.ETS,
           [
             # 2 hours
             expiry_ms: 60_000 * 60 * 2,
             # 10 minutes
             cleanup_interval_ms: 60_000 * 10
           ]})

        _ ->
          :ok
      end

      # Ensure Fuse application is started
      case Application.ensure_all_started(:fuse) do
        {:ok, _apps} ->
          :ok

        {:error, reason} ->
          raise "Failed to start Fuse application: #{inspect(reason)}"
      end

      emit_telemetry(
        :infrastructure_initialized,
        %{
          config: config,
          components: [:fuse, :hammer]
        },
        %{}
      )

      {:ok, []}
    rescue
      exception ->
        {:error, {:infrastructure_init_failed, exception}}
    end
  end
end
</file>

<file path="foundation/infrastructure/rate_limiter.ex">
defmodule Foundation.Infrastructure.RateLimiter do
  @moduledoc """
  Rate limiter wrapper around Hammer library.

  Provides standardized rate limiting functionality with telemetry integration
  and Foundation-specific error handling. Translates Hammer responses to 
  Foundation.Types.Error structures.

  ## Usage

      # Check if request is allowed
      case RateLimiter.check_rate("user:123", :login, 5, 60_000) do
        :ok -> proceed_with_request()
        {:error, error} -> handle_rate_limit(error)
      end
      
      # Get current rate status
      status = RateLimiter.get_status("user:123", :login)
  """

  defmodule HammerBackend do
    @moduledoc false
    use Hammer,
      backend: :ets,
      cleanup_rate: 60_000
  end

  alias Foundation.Types.Error
  alias Foundation.Telemetry

  @type entity_id :: String.t() | atom() | integer()
  @type operation :: atom()
  @type rate_limit :: pos_integer()
  @type time_window :: pos_integer()
  @type rate_check_result :: :ok | {:error, Error.t()}

  @doc """
  Check if a request is allowed under rate limiting constraints.

  ## Parameters
  - `entity_id`: Identifier for the entity being rate limited (user, IP, etc.)
  - `operation`: Type of operation being performed  
  - `limit`: Maximum number of requests allowed
  - `time_window_ms`: Time window in milliseconds
  - `metadata`: Additional telemetry metadata

  ## Examples

      iex> RateLimiter.check_rate("user:123", :api_call, 100, 60_000)
      :ok
      
      iex> RateLimiter.check_rate("user:456", :heavy_operation, 5, 60_000)
      {:error, %Error{error_type: :rate_limit_exceeded}}
  """
  @spec check_rate(entity_id(), operation(), rate_limit(), time_window(), map()) ::
          rate_check_result()
  def check_rate(entity_id, operation, limit, time_window_ms, metadata \\ %{}) do
    case build_rate_key(entity_id, operation) do
      {:error, error} ->
        {:error, error}

      key ->
        try do
          # Use Hammer with ETS backend for rate limiting
          case HammerBackend.hit(key, time_window_ms, limit, 1) do
            {:allow, count} ->
              emit_telemetry(
                :request_allowed,
                Map.merge(metadata, %{
                  entity_id: entity_id,
                  operation: operation,
                  count: count,
                  limit: limit,
                  time_window_ms: time_window_ms
                })
              )

              :ok

            {:deny, _limit} ->
              error =
                Error.new(
                  code: 6001,
                  error_type: :rate_limit_exceeded,
                  message: "Rate limit exceeded for #{entity_id}:#{operation}",
                  severity: :medium,
                  context: %{
                    entity_id: entity_id,
                    operation: operation,
                    limit: limit,
                    time_window_ms: time_window_ms
                  },
                  retry_strategy: :fixed_delay
                )

              emit_telemetry(
                :request_denied,
                Map.merge(metadata, %{
                  entity_id: entity_id,
                  operation: operation,
                  limit: limit,
                  time_window_ms: time_window_ms
                })
              )

              {:error, error}
          end
        rescue
          exception ->
            error =
              Error.new(
                code: 6003,
                error_type: :rate_limiter_exception,
                message: "Exception in rate limiter: #{inspect(exception)}",
                severity: :critical,
                context: %{
                  entity_id: entity_id,
                  operation: operation,
                  exception: exception
                }
              )

            emit_telemetry(
              :rate_limiter_exception,
              Map.merge(metadata, %{
                entity_id: entity_id,
                operation: operation,
                exception: exception
              })
            )

            {:error, error}
        end
    end
  end

  @doc """
  Get the current rate limiting status for an entity and operation.

  This is a simplified implementation that doesn't provide detailed bucket information.

  ## Parameters
  - `entity_id`: Identifier for the entity
  - `operation`: Type of operation

  ## Returns
  - `{:ok, %{status: :available | :rate_limited}}`
  - `{:error, Error.t()}`

  ## Examples

      iex> RateLimiter.get_status("user:123", :api_call)
      {:ok, %{status: :available}}
  """
  @spec get_status(entity_id(), operation()) :: {:ok, map()}
  def get_status(entity_id, _operation) do
    # Simplified implementation that returns basic status
    # In a production environment, you might integrate with the actual
    # Hammer backend to get precise bucket information
    {:ok,
     %{
       status: :available,
       current_count: 0,
       limit: 100,
       window_ms: 60_000,
       entity_id: entity_id
     }}
  end

  @doc """
  Reset the rate limiting bucket for an entity and operation.

  Note: This is a simplified implementation that may not actually clear 
  the bucket depending on the Hammer backend configuration.

  ## Parameters
  - `entity_id`: Identifier for the entity
  - `operation`: Type of operation

  ## Examples

      iex> RateLimiter.reset("user:123", :api_call)
      :ok
  """
  @spec reset(entity_id(), operation()) :: :ok | {:error, Error.t()}
  def reset(entity_id, operation) do
    try do
      # Emit telemetry for reset request
      emit_telemetry(:bucket_reset, %{
        entity_id: entity_id,
        operation: operation
      })

      # For now, we just log the reset attempt
      # In a production implementation, you might want to use a different 
      # Hammer backend that supports bucket deletion
      :ok
    rescue
      exception ->
        error =
          Error.new(
            code: 6007,
            error_type: :rate_limiter_exception,
            message: "Exception resetting rate limiter: #{inspect(exception)}",
            severity: :medium,
            context: %{
              entity_id: entity_id,
              operation: operation,
              exception: exception
            }
          )

        {:error, error}
    end
  end

  @doc """
  Execute an operation with rate limiting protection.

  ## Parameters
  - `entity_id`: Identifier for the entity
  - `operation_name`: Type of operation for rate limiting
  - `limit`: Maximum number of requests allowed
  - `time_window_ms`: Time window in milliseconds
  - `operation_fun`: Function to execute if allowed
  - `metadata`: Additional telemetry metadata

  ## Examples

      iex> RateLimiter.execute_with_limit("user:123", :api_call, 100, 60_000, fn ->
      ...>   expensive_api_call()
      ...> end)
      {:ok, result}
  """
  @spec execute_with_limit(entity_id(), operation(), rate_limit(), time_window(), (-> any()), map()) ::
          {:ok, any()} | {:error, Error.t()}
  def execute_with_limit(
        entity_id,
        operation_name,
        limit,
        time_window_ms,
        operation_fun,
        metadata \\ %{}
      )
      when is_function(operation_fun, 0) do
    case check_rate(entity_id, operation_name, limit, time_window_ms, metadata) do
      :ok ->
        try do
          result = operation_fun.()
          {:ok, result}
        rescue
          exception ->
            error =
              Error.new(
                code: 6008,
                error_type: :rate_limited_operation_failed,
                message: "Rate limited operation failed: #{inspect(exception)}",
                severity: :medium,
                context: %{
                  entity_id: entity_id,
                  operation: operation_name,
                  exception: exception
                }
              )

            {:error, error}
        end

      {:error, _} = error ->
        error
    end
  end

  # Private helper functions

  @spec build_rate_key(entity_id(), operation()) :: String.t() | {:error, Error.t()}
  defp build_rate_key(entity_id, operation) do
    try do
      "foundation:#{entity_id}:#{operation}"
    rescue
      Protocol.UndefinedError ->
        {:error,
         Error.new(
           code: 6008,
           error_type: :validation_failed,
           message: "Invalid entity_id or operation type",
           severity: :medium,
           context: %{
             entity_id: entity_id,
             operation: operation
           }
         )}
    end
  end

  @spec emit_telemetry(atom(), map()) :: :ok
  defp emit_telemetry(event_type, metadata) do
    event_name = [:foundation, :foundation, :infra, :rate_limiter, event_type]
    Telemetry.emit_counter(event_name, metadata)
  end
end
</file>

<file path="foundation/logic/config_logic.ex">
defmodule Foundation.Logic.ConfigLogic do
  @moduledoc """
  Pure business logic functions for configuration operations.

  Contains configuration manipulation, merging, and transformation logic.
  No side effects - all functions are pure and easily testable.
  """

  alias Foundation.Types.{Config, Error}
  alias Foundation.Validation.ConfigValidator
  require Logger

  @type config_path :: [atom()]
  @type config_value :: term()

  # Paths that can be updated at runtime
  @updatable_paths [
    [:ai, :planning, :sampling_rate],
    [:ai, :planning, :performance_target],
    [:capture, :processing, :batch_size],
    [:capture, :processing, :flush_interval],
    [:interface, :query_timeout],
    [:interface, :max_results],
    [:dev, :debug_mode],
    [:dev, :verbose_logging],
    [:dev, :performance_monitoring],
    [:infrastructure, :rate_limiting, :enabled],
    [:infrastructure, :circuit_breaker, :enabled],
    [:infrastructure, :connection_pool, :enabled],
    [:infrastructure, :rate_limiting, :cleanup_interval]
  ]

  @doc """
  Get the list of paths that can be updated at runtime.
  """
  @spec updatable_paths() :: [[atom(), ...], ...]
  def updatable_paths, do: @updatable_paths

  @doc """
  Check if a configuration path can be updated at runtime.
  """
  @spec updatable_path?(config_path()) :: boolean()
  def updatable_path?(path) when is_list(path) do
    path in @updatable_paths
  end

  @doc """
  Update a configuration value at the given path.
  Returns the updated configuration if successful.
  """
  @spec update_config(Config.t(), config_path(), config_value()) ::
          {:ok, Config.t()} | {:error, Error.t()}
  def update_config(%Config{} = config, path, value) when is_list(path) do
    cond do
      not updatable_path?(path) ->
        create_error(
          :config_update_forbidden,
          "Configuration path #{inspect(path)} cannot be updated at runtime",
          %{path: path, allowed_paths: @updatable_paths}
        )

      true ->
        new_config = put_in(config, path, value)

        case ConfigValidator.validate(new_config) do
          :ok -> {:ok, new_config}
          {:error, _} = error -> error
        end
    end
  end

  @doc """
  Get a configuration value by path.
  """
  @spec get_config_value(Config.t(), config_path()) :: {:ok, config_value()} | {:error, Error.t()}
  def get_config_value(config, path) when is_list(path) do
    try do
      # We need to check if the path exists, not just if the value is nil
      case get_nested_value(config, path) do
        {:ok, value} ->
          {:ok, value}

        {:error, :path_not_found} ->
          {:error,
           Error.new(
             error_type: :config_path_not_found,
             message: "Configuration path not found: #{inspect(path)}",
             context: %{path: path},
             category: :config,
             subcategory: :access,
             severity: :medium
           )}
      end
    rescue
      error ->
        Logger.warning(
          "Failed to get config value for path #{inspect(path)}: #{Exception.message(error)}"
        )

        {:error,
         Error.new(
           error_type: :config_path_invalid,
           message: "Invalid configuration path: #{Exception.message(error)}",
           context: %{path: path, error: Exception.message(error)},
           category: :config,
           subcategory: :access,
           severity: :medium
         )}
    end
  end

  def get_config_value(_config, path) do
    Logger.warning("Invalid path provided to get_config_value: #{inspect(path)}")

    {:error,
     Error.new(
       error_type: :invalid_path,
       message: "Invalid path type: #{inspect(path)}",
       context: %{path: path},
       category: :config,
       subcategory: :validation,
       severity: :medium
     )}
  end

  # Helper function to traverse the path and distinguish between nil values and non-existent paths
  defp get_nested_value(data, []) do
    {:ok, data}
  end

  defp get_nested_value(data, [key | rest]) when is_map(data) do
    if Map.has_key?(data, key) do
      get_nested_value(Map.get(data, key), rest)
    else
      {:error, :path_not_found}
    end
  end

  defp get_nested_value(_data, _path) do
    # We hit a non-map value before exhausting the path
    {:error, :path_not_found}
  end

  @doc """
  Merge configuration with environment overrides.
  """
  @spec merge_env_config(Config.t(), keyword()) :: Config.t()
  def merge_env_config(%Config{} = config, env_config) when is_list(env_config) do
    Enum.reduce(env_config, config, fn {key, value}, acc ->
      if Map.has_key?(acc, key) do
        current_value = Map.get(acc, key)
        merged_value = deep_merge(current_value, value)
        Map.put(acc, key, merged_value)
      else
        acc
      end
    end)
  end

  @doc """
  Merge configuration with keyword list overrides.
  """
  @spec merge_opts_config(Config.t(), keyword()) :: Config.t()
  def merge_opts_config(%Config{} = config, opts) when is_list(opts) do
    Enum.reduce(opts, config, fn {key, value}, acc ->
      Map.put(acc, key, value)
    end)
  end

  @doc """
  Build a configuration from environment and options.
  """
  @spec build_config(keyword()) :: {:ok, Config.t()} | {:error, Error.t()}
  def build_config(opts \\ []) do
    base_config = Config.new()
    env_config = Application.get_all_env(:foundation)

    merged_config =
      base_config
      |> merge_env_config(env_config)
      |> merge_opts_config(opts)

    case ConfigValidator.validate(merged_config) do
      :ok -> {:ok, merged_config}
      {:error, _} = error -> error
    end
  end

  @doc """
  Reset configuration to defaults.
  """
  @spec reset_config() :: Config.t()
  def reset_config do
    Config.new()
  end

  @doc """
  Create a configuration diff between two configs.
  """
  @spec diff_configs(Config.t(), Config.t()) :: map()
  def diff_configs(%Config{} = old_config, %Config{} = new_config) do
    old_map = Map.from_struct(old_config)
    new_map = Map.from_struct(new_config)

    create_diff(old_map, new_map, [])
  end

  ## Private Functions

  defp deep_merge(left, right) when is_map(left) and is_list(right) do
    right_map = Enum.into(right, %{})

    Map.merge(left, right_map, fn _key, left_val, right_val ->
      deep_merge(left_val, right_val)
    end)
  end

  defp deep_merge(left, right) when is_map(left) and is_map(right) do
    Map.merge(left, right, fn _key, left_val, right_val ->
      deep_merge(left_val, right_val)
    end)
  end

  defp deep_merge(_left, right), do: right

  defp create_diff(old_map, new_map, path) when is_map(old_map) and is_map(new_map) do
    all_keys = MapSet.union(MapSet.new(Map.keys(old_map)), MapSet.new(Map.keys(new_map)))

    Enum.reduce(all_keys, %{}, fn key, acc ->
      old_val = Map.get(old_map, key)
      new_val = Map.get(new_map, key)
      current_path = path ++ [key]

      cond do
        old_val == new_val ->
          acc

        is_map(old_val) and is_map(new_val) ->
          nested_diff = create_diff(old_val, new_val, current_path)
          if map_size(nested_diff) > 0, do: Map.put(acc, key, nested_diff), else: acc

        true ->
          Map.put(acc, key, %{old: old_val, new: new_val, path: current_path})
      end
    end)
  end

  defp create_diff(old_val, new_val, path) do
    if old_val == new_val do
      %{}
    else
      %{old: old_val, new: new_val, path: path}
    end
  end

  defp create_error(error_type, message, context) do
    error =
      Error.new(
        error_type: error_type,
        message: message,
        context: context,
        category: :config,
        subcategory: :runtime,
        severity: :medium
      )

    {:error, error}
  end
end
</file>

<file path="foundation/logic/event_logic.ex">
defmodule Foundation.Logic.EventLogic do
  @moduledoc """
  Pure business logic functions for event operations.

  Contains event creation, transformation, and analysis logic.
  No side effects - all functions are pure and easily testable.
  """

  alias Foundation.Types.{Event, Error}
  alias Foundation.Validation.EventValidator
  alias Foundation.Utils

  @type event_opts :: keyword()
  @type serialization_opts :: keyword()

  @doc """
  Create a new event with the given parameters.
  """
  @spec create_event(atom(), term(), event_opts()) :: {:ok, Event.t()} | {:error, Error.t()}
  def create_event(event_type, data, opts \\ []) do
    event =
      Event.new(
        event_id: Keyword.get(opts, :event_id, Utils.generate_id()),
        event_type: event_type,
        timestamp: Keyword.get(opts, :timestamp, Utils.monotonic_timestamp()),
        wall_time: Keyword.get(opts, :wall_time, DateTime.utc_now()),
        node: Keyword.get(opts, :node, Node.self()),
        pid: Keyword.get(opts, :pid, self()),
        correlation_id: Keyword.get(opts, :correlation_id),
        parent_id: Keyword.get(opts, :parent_id),
        data: data
      )

    case EventValidator.validate(event) do
      :ok -> {:ok, event}
      {:error, _} = error -> error
    end
  end

  @doc """
  Create a function entry event.
  """
  @spec create_function_entry(module(), atom(), arity(), [term()], event_opts()) ::
          {:ok, Event.t()} | {:error, Error.t()}
  def create_function_entry(module, function, arity, args, opts \\ []) do
    data = %{
      call_id: Utils.generate_id(),
      module: module,
      function: function,
      arity: arity,
      args: Utils.truncate_if_large(args),
      caller_module: Keyword.get(opts, :caller_module),
      caller_function: Keyword.get(opts, :caller_function),
      caller_line: Keyword.get(opts, :caller_line)
    }

    create_event(:function_entry, data, opts)
  end

  @doc """
  Create a function exit event.
  """
  @spec create_function_exit(
          module(),
          atom(),
          arity(),
          pos_integer(),
          term(),
          non_neg_integer(),
          atom()
        ) :: {:ok, Event.t()} | {:error, Error.t()}
  def create_function_exit(module, function, arity, call_id, result, duration_ns, exit_reason) do
    data = %{
      call_id: call_id,
      module: module,
      function: function,
      arity: arity,
      result: Utils.truncate_if_large(result),
      duration_ns: duration_ns,
      exit_reason: exit_reason
    }

    create_event(:function_exit, data)
  end

  @doc """
  Create a state change event.
  """
  @spec create_state_change(pid(), atom(), term(), term(), event_opts()) ::
          {:ok, Event.t()} | {:error, Error.t()}
  def create_state_change(server_pid, callback, old_state, new_state, opts \\ []) do
    data = %{
      server_pid: server_pid,
      callback: callback,
      old_state: Utils.truncate_if_large(old_state),
      new_state: Utils.truncate_if_large(new_state),
      state_diff: compute_state_diff(old_state, new_state),
      trigger_message: Keyword.get(opts, :trigger_message),
      trigger_call_id: Keyword.get(opts, :trigger_call_id)
    }

    create_event(:state_change, data, opts)
  end

  @doc """
  Serialize an event to binary format.
  """
  @spec serialize_event(Event.t(), serialization_opts()) :: {:ok, binary()} | {:error, Error.t()}
  def serialize_event(%Event{} = event, opts \\ []) do
    compression = Keyword.get(opts, :compression, true)

    try do
      binary =
        if compression do
          :erlang.term_to_binary(event, [:compressed])
        else
          :erlang.term_to_binary(event)
        end

      {:ok, binary}
    rescue
      error ->
        create_error(
          :serialization_failed,
          "Failed to serialize event",
          %{original_error: error, event_id: event.event_id}
        )
    end
  end

  @doc """
  Deserialize an event from binary format.
  """
  @spec deserialize_event(binary()) :: {:ok, Event.t()} | {:error, Error.t()}
  def deserialize_event(binary) when is_binary(binary) do
    try do
      event = :erlang.binary_to_term(binary)

      case EventValidator.validate(event) do
        :ok -> {:ok, event}
        {:error, _} = error -> error
      end
    rescue
      error ->
        create_error(
          :deserialization_failed,
          "Failed to deserialize event",
          %{original_error: error, binary_size: byte_size(binary)}
        )
    end
  end

  @doc """
  Calculate the serialized size of an event.
  """
  @spec calculate_serialized_size(Event.t()) :: {:ok, non_neg_integer()} | {:error, Error.t()}
  def calculate_serialized_size(%Event{} = event) do
    case serialize_event(event) do
      {:ok, binary} -> {:ok, byte_size(binary)}
      {:error, _} = error -> error
    end
  end

  @doc """
  Extract correlation chain from a list of events.
  """
  @spec extract_correlation_chain([Event.t()], String.t()) :: [Event.t()]
  def extract_correlation_chain(events, correlation_id)
      when is_list(events) and is_binary(correlation_id) do
    events
    |> Enum.filter(fn event -> event.correlation_id == correlation_id end)
    |> Enum.sort_by(& &1.timestamp)
  end

  @doc """
  Group events by correlation ID.
  """
  @spec group_by_correlation([Event.t()]) :: %{String.t() => [Event.t()]}
  def group_by_correlation(events) when is_list(events) do
    events
    |> Enum.filter(fn event -> not is_nil(event.correlation_id) end)
    |> Enum.group_by(& &1.correlation_id)
    |> Map.new(fn {correlation_id, group_events} ->
      {correlation_id, Enum.sort_by(group_events, & &1.timestamp)}
    end)
  end

  @doc """
  Filter events by time range.
  """
  @spec filter_by_time_range([Event.t()], integer(), integer()) :: [Event.t()]
  def filter_by_time_range(events, start_time, end_time)
      when is_list(events) and is_integer(start_time) and is_integer(end_time) do
    Enum.filter(events, fn event ->
      event.timestamp >= start_time and event.timestamp <= end_time
    end)
  end

  @doc """
  Transform event data using a transformation function.
  """
  @spec transform_event_data(Event.t(), (term() -> term())) :: Event.t()
  def transform_event_data(%Event{} = event, transform_fn) when is_function(transform_fn, 1) do
    %{event | data: transform_fn.(event.data)}
  end

  ## Private Functions

  defp compute_state_diff(old_state, new_state) do
    if old_state == new_state do
      :no_change
    else
      :changed
    end
  end

  defp create_error(error_type, message, context) do
    error =
      Error.new(
        error_type: error_type,
        message: message,
        context: context,
        category: :data,
        subcategory: :runtime,
        severity: :medium
      )

    {:error, error}
  end
end
</file>

<file path="foundation/services/config_server.ex">
defmodule Foundation.Services.ConfigServer do
  @moduledoc """
  GenServer implementation for configuration management.

  Handles configuration persistence, updates, and notifications.
  Delegates business logic to ConfigLogic module.

  This server provides a centralized point for configuration access and
  modification, with support for subscriptions to configuration changes.

  See `@type server_state` for the internal state structure.

  ## Examples

      # Get configuration
      {:ok, config} = Foundation.Services.ConfigServer.get()

      # Update a configuration value
      :ok = Foundation.Services.ConfigServer.update([:ai, :provider], :openai)

      # Subscribe to configuration changes
      :ok = Foundation.Services.ConfigServer.subscribe()
  """

  use GenServer
  require Logger

  alias Foundation.Types.{Config, Error}
  alias Foundation.Logic.ConfigLogic
  alias Foundation.Validation.ConfigValidator
  alias Foundation.Contracts.Configurable
  alias Foundation.Services.{EventStore, TelemetryService}
  alias Foundation.{ProcessRegistry, ServiceRegistry}

  @behaviour Configurable

  @typedoc "Internal state of the configuration server"
  @type server_state :: %{
          config: Config.t(),
          subscribers: [pid()],
          monitors: %{reference() => pid()},
          metrics: metrics(),
          namespace: ProcessRegistry.namespace()
        }

  @typedoc "Metrics tracking for the configuration server"
  @type metrics :: %{
          start_time: integer(),
          updates_count: non_neg_integer(),
          last_update: integer() | nil
        }

  ## Public API (Configurable Behaviour Implementation)

  @doc """
  Get the complete configuration.

  Returns the current configuration or an error if the service is unavailable.
  """
  @impl Configurable
  @spec get() :: {:ok, Config.t()} | {:error, Error.t()}
  def get do
    case ServiceRegistry.lookup(:production, :config_server) do
      {:ok, pid} -> GenServer.call(pid, :get_config)
      {:error, _} -> create_service_error("Configuration service not started")
    end
  end

  @doc """
  Get a configuration value by path.

  ## Parameters
  - `path`: List of atoms representing the path to the configuration value

  ## Examples

      {:ok, provider} = get([:ai, :provider])
      {:ok, timeout} = get([:capture, :processing, :timeout])
  """
  @impl Configurable
  @spec get([atom()]) :: {:ok, term()} | {:error, Error.t()}
  def get(path) when is_list(path) do
    case ServiceRegistry.lookup(:production, :config_server) do
      {:ok, pid} -> GenServer.call(pid, {:get_config_path, path})
      {:error, _} -> create_service_error("Configuration service not started")
    end
  end

  @doc """
  Update a configuration value at the given path.

  ## Parameters
  - `path`: List of atoms representing the path to the configuration value
  - `value`: New value to set

  ## Examples

      :ok = update([:ai, :provider], :openai)
      :ok = update([:capture, :ring_buffer, :size], 2048)
  """
  @impl Configurable
  @spec update([atom()], term()) :: :ok | {:error, Error.t()}
  def update(path, value) when is_list(path) do
    case ServiceRegistry.lookup(:production, :config_server) do
      {:ok, pid} -> GenServer.call(pid, {:update_config, path, value})
      {:error, _} -> create_service_error("Configuration service not started")
    end
  end

  @doc """
  Validate a configuration structure.

  Delegates to the ConfigValidator module for validation logic.
  """
  @impl Configurable
  @spec validate(Config.t()) :: :ok | {:error, Error.t()}
  def validate(config) do
    ConfigValidator.validate(config)
  end

  @doc """
  Get the list of paths that can be updated at runtime.

  Delegates to the ConfigLogic module for the list of updatable paths.
  """
  @impl Configurable
  @spec updatable_paths() :: [[atom(), ...], ...]
  def updatable_paths do
    ConfigLogic.updatable_paths()
  end

  @doc """
  Reset configuration to defaults.

  Resets the configuration to its default values and notifies all subscribers.
  """
  @impl Configurable
  @spec reset() :: :ok | {:error, Error.t()}
  def reset do
    case ServiceRegistry.lookup(:production, :config_server) do
      {:ok, pid} -> GenServer.call(pid, :reset_config)
      {:error, _} -> create_service_error("Configuration service not started")
    end
  end

  @doc """
  Check if the configuration service is available.

  Returns true if the GenServer is running and registered.
  """
  @impl Configurable
  @spec available?() :: boolean()
  def available? do
    case ServiceRegistry.lookup(:production, :config_server) do
      {:ok, _pid} -> true
      {:error, _} -> false
    end
  end

  @doc """
  Reset all internal state for testing purposes.

  Clears all subscribers, metrics, and resets configuration to defaults.
  This function should only be used in test environments.
  """
  @spec reset_state() :: :ok | {:error, Error.t()}
  def reset_state do
    if Application.get_env(:foundation, :test_mode, false) do
      case ServiceRegistry.lookup(:production, :config_server) do
        {:ok, pid} -> GenServer.call(pid, :reset_state)
        {:error, _} -> create_service_error("Configuration service not started")
      end
    else
      {:error,
       Error.new(
         code: 5002,
         error_type: :operation_forbidden,
         message: "State reset only allowed in test mode",
         severity: :high,
         category: :security,
         subcategory: :authorization
       )}
    end
  end

  ## Additional Functions

  @doc """
  Initialize the configuration service with default options.

  ## Examples

      :ok = Foundation.Services.ConfigServer.initialize()
  """
  @spec initialize() :: :ok | {:error, Error.t()}
  def initialize() do
    initialize([])
  end

  @doc """
  Initialize the configuration service with custom options.

  ## Parameters
  - `opts`: Keyword list of initialization options

  ## Examples

      :ok = Foundation.Services.ConfigServer.initialize(cache_size: 1000)
  """
  @spec initialize(keyword()) :: :ok | {:error, Error.t()}
  def initialize(opts) when is_list(opts) do
    # Check if service is already running in production namespace
    case ServiceRegistry.lookup(:production, :config_server) do
      {:ok, _pid} ->
        # Already running
        :ok

      {:error, _} ->
        # Service not running, try to start it
        case start_link(Keyword.put(opts, :namespace, :production)) do
          {:ok, _pid} ->
            :ok

          {:error, {:already_started, _pid}} ->
            :ok

          {:error, reason} ->
            create_service_error("Failed to start ConfigServer: #{inspect(reason)}")
        end
    end
  end

  @doc """
  Get configuration service status.

  ## Examples

      {:ok, status} = Foundation.Services.ConfigServer.status()
  """
  @spec status() :: {:ok, map()} | {:error, Error.t()}
  def status() do
    case ServiceRegistry.lookup(:production, :config_server) do
      {:ok, pid} -> GenServer.call(pid, :get_status)
      {:error, _} -> create_service_error("Configuration service not started")
    end
  end

  ## GenServer API

  @doc """
  Start the configuration server.

  ## Parameters
  - `opts`: Keyword list of options passed to GenServer initialization
    - `:namespace` - The namespace to register in (defaults to :production)
  """
  @spec start_link(keyword()) :: GenServer.on_start()
  def start_link(opts \\ []) do
    namespace = Keyword.get(opts, :namespace, :production)
    name = ServiceRegistry.via_tuple(namespace, :config_server)
    GenServer.start_link(__MODULE__, Keyword.put(opts, :namespace, namespace), name: name)
  end

  @doc """
  Stop the configuration server.
  """
  @spec stop() :: :ok
  def stop do
    case ServiceRegistry.lookup(:production, :config_server) do
      {:ok, pid} -> GenServer.stop(pid)
      {:error, _} -> :ok
    end
  end

  @doc """
  Subscribe to configuration change notifications.

  ## Parameters
  - `pid`: Process to subscribe (defaults to calling process)

  ## Examples

      :ok = Foundation.Services.ConfigServer.subscribe()
      :ok = Foundation.Services.ConfigServer.subscribe(some_pid)
  """
  @spec subscribe(pid()) :: :ok | {:error, Error.t()}
  def subscribe(pid \\ self()) do
    case ServiceRegistry.lookup(:production, :config_server) do
      {:ok, server_pid} -> GenServer.call(server_pid, {:subscribe, pid})
      {:error, _} -> create_service_error("Configuration service not started")
    end
  end

  @doc """
  Unsubscribe from configuration change notifications.

  ## Parameters
  - `pid`: Process to unsubscribe (defaults to calling process)
  """
  @spec unsubscribe(pid()) :: :ok | {:error, Error.t()}
  def unsubscribe(pid \\ self()) do
    case ServiceRegistry.lookup(:production, :config_server) do
      {:ok, server_pid} -> GenServer.call(server_pid, {:unsubscribe, pid})
      {:error, _} -> create_service_error("Configuration service not started")
    end
  end

  ## GenServer Callbacks

  @doc """
  Initialize the GenServer state.

  Builds the initial configuration and sets up metrics tracking.
  """
  @impl GenServer
  @spec init(keyword()) :: {:ok, server_state()} | {:stop, term()}
  def init(opts) do
    namespace = Keyword.get(opts, :namespace, :production)

    case ConfigLogic.build_config(opts) do
      {:ok, config} ->
        Logger.info(
          "Configuration server initialized successfully in namespace #{inspect(namespace)}"
        )

        state = %{
          config: config,
          subscribers: [],
          monitors: %{},
          namespace: namespace,
          metrics: %{
            start_time: System.monotonic_time(:millisecond),
            updates_count: 0,
            last_update: nil
          }
        }

        {:ok, state}

      {:error, error} ->
        Logger.error("Failed to initialize configuration: #{inspect(error)}")
        {:stop, {:config_validation_failed, error}}
    end
  end

  @impl GenServer
  @spec handle_call(term(), GenServer.from(), server_state()) ::
          {:reply, term(), server_state()} | {:noreply, server_state()}
  def handle_call(:get_config, _from, %{config: config} = state) do
    {:reply, {:ok, config}, state}
  end

  @impl GenServer
  def handle_call({:get_config_path, path}, _from, %{config: config} = state) do
    result = ConfigLogic.get_config_value(config, path)
    {:reply, result, state}
  end

  @impl GenServer
  def handle_call({:update_config, path, value}, _from, %{config: config} = state) do
    case ConfigLogic.update_config(config, path, value) do
      {:ok, new_config} ->
        new_state = %{
          state
          | config: new_config,
            metrics:
              Map.merge(state.metrics, %{
                updates_count: state.metrics.updates_count + 1,
                last_update: System.monotonic_time(:millisecond)
              })
        }

        # Notify subscribers
        notify_subscribers(state.subscribers, {:config_updated, path, value})

        # Emit event to EventStore for audit and correlation
        emit_config_event(:config_updated, %{
          path: path,
          new_value: value,
          previous_value: ConfigLogic.get_config_value(config, path),
          timestamp: System.monotonic_time(:millisecond)
        })

        # Emit telemetry for config updates
        emit_config_telemetry(:config_updated, %{path: path})

        {:reply, :ok, new_state}

      {:error, _} = error ->
        {:reply, error, state}
    end
  end

  @impl GenServer
  def handle_call(:reset_config, _from, state) do
    new_config = ConfigLogic.reset_config()

    case ConfigValidator.validate(new_config) do
      :ok ->
        new_state = %{state | config: new_config}
        notify_subscribers(state.subscribers, {:config_reset, new_config})

        # Emit event to EventStore for audit and correlation
        emit_config_event(:config_reset, %{
          timestamp: System.monotonic_time(:millisecond),
          reset_from_updates_count: state.metrics.updates_count
        })

        # Emit telemetry for config resets
        emit_config_telemetry(:config_reset, %{
          reset_from_updates_count: state.metrics.updates_count
        })

        {:reply, :ok, new_state}

      {:error, _} = error ->
        {:reply, error, state}
    end
  end

  @impl GenServer
  def handle_call({:subscribe, pid}, _from, %{subscribers: subscribers, monitors: monitors} = state) do
    if pid in subscribers do
      {:reply, :ok, state}
    else
      # Fix race condition: add to list first, then monitor
      new_subscribers = [pid | subscribers]
      monitor_ref = Process.monitor(pid)
      new_monitors = Map.put(monitors, monitor_ref, pid)

      new_state = %{state | subscribers: new_subscribers, monitors: new_monitors}
      {:reply, :ok, new_state}
    end
  end

  @impl GenServer
  def handle_call(
        {:unsubscribe, pid},
        _from,
        %{subscribers: subscribers, monitors: monitors} = state
      ) do
    new_subscribers = List.delete(subscribers, pid)

    # Find and demonitor the reference for this PID
    {new_monitors, _} =
      Enum.reduce(monitors, {%{}, nil}, fn
        {ref, ^pid}, {acc_monitors, _} ->
          Process.demonitor(ref, [:flush])
          {acc_monitors, ref}

        {ref, other_pid}, {acc_monitors, found_ref} ->
          {Map.put(acc_monitors, ref, other_pid), found_ref}
      end)

    new_state = %{state | subscribers: new_subscribers, monitors: new_monitors}
    {:reply, :ok, new_state}
  end

  @impl GenServer
  def handle_call(:reset_state, _from, state) do
    # Reset to initial state (for testing)
    case ConfigLogic.build_config([]) do
      {:ok, config} ->
        new_state = %{
          config: config,
          subscribers: [],
          monitors: %{},
          metrics: %{
            start_time: System.monotonic_time(:millisecond),
            updates_count: 0,
            last_update: nil
          }
        }

        {:reply, :ok, new_state}

      {:error, error} ->
        {:reply, {:error, error}, state}
    end
  end

  @impl GenServer
  def handle_call(:get_status, _from, %{metrics: metrics, subscribers: subscribers} = state) do
    current_time = System.monotonic_time(:millisecond)

    status = %{
      status: :running,
      uptime_ms: current_time - metrics.start_time,
      updates_count: metrics.updates_count,
      last_update: metrics.last_update,
      subscribers_count: length(subscribers)
    }

    {:reply, {:ok, status}, state}
  end

  @impl GenServer
  @spec handle_info(term(), server_state()) :: {:noreply, server_state()}
  def handle_info(
        {:DOWN, ref, :process, pid, _reason},
        %{subscribers: subscribers, monitors: monitors} = state
      ) do
    # Remove dead subscriber using the monitor reference
    new_subscribers = List.delete(subscribers, pid)
    new_monitors = Map.delete(monitors, ref)
    new_state = %{state | subscribers: new_subscribers, monitors: new_monitors}
    {:noreply, new_state}
  end

  @impl GenServer
  def handle_info(msg, state) do
    Logger.warning("Unexpected message in ConfigServer: #{inspect(msg)}")
    {:noreply, state}
  end

  ## Private Functions

  @spec notify_subscribers([pid()], term()) :: :ok
  defp notify_subscribers(subscribers, message) do
    Enum.each(subscribers, fn pid ->
      send(pid, {:config_notification, message})
    end)
  end

  @spec emit_config_event(atom(), map()) :: :ok
  defp emit_config_event(event_type, data) do
    # Only emit if EventStore is available to avoid blocking config operations
    if EventStore.available?() do
      try do
        case Foundation.Events.new_event(event_type, data) do
          {:ok, event} ->
            case EventStore.store(event) do
              {:ok, _id} ->
                :ok

              {:error, error} ->
                Logger.warning("Failed to emit config event: #{inspect(error)}")
            end

          {:error, error} ->
            Logger.warning("Failed to create config event: #{inspect(error)}")
        end
      rescue
        error ->
          Logger.warning("Exception while emitting config event: #{inspect(error)}")
      end
    end
  end

  @spec emit_config_telemetry(atom(), map()) :: :ok
  defp emit_config_telemetry(operation_type, metadata) do
    # Only emit if TelemetryService is available to avoid blocking config operations
    if TelemetryService.available?() do
      try do
        case operation_type do
          :config_updated ->
            TelemetryService.emit_counter([:foundation, :config_updates], metadata)

          :config_reset ->
            TelemetryService.emit_counter([:foundation, :config_resets], metadata)
        end
      rescue
        error ->
          Logger.warning("Exception while emitting config telemetry: #{inspect(error)}")
      end
    end
  end

  @spec create_service_error(String.t()) :: {:error, Error.t()}
  defp create_service_error(message) do
    error =
      Error.new(
        code: 5000,
        error_type: :service_unavailable,
        message: message,
        severity: :high,
        category: :system,
        subcategory: :initialization
      )

    {:error, error}
  end
end
</file>

<file path="foundation/services/event_store.ex">
defmodule Foundation.Services.EventStore do
  @moduledoc """
  Event storage service implementation using GenServer.

  Provides persistent event storage with querying capabilities.
  """

  use GenServer
  require Logger

  alias Foundation.{Services.TelemetryService}
  alias Foundation.Types.{Event, Error}
  alias Foundation.Validation.EventValidator
  alias Foundation.Contracts.EventStore, as: EventStoreContract

  @behaviour EventStoreContract

  @type server_state :: %{
          events: %{non_neg_integer() => Event.t()},
          next_id: non_neg_integer(),
          metrics: map()
        }

  ## Public API (EventStore Behaviour Implementation)

  @impl EventStoreContract
  @spec store(Event.t()) :: {:ok, non_neg_integer()} | {:error, Error.t()}
  def store(event) do
    case Foundation.ServiceRegistry.lookup(:production, :event_store) do
      {:ok, pid} -> GenServer.call(pid, {:store_event, event})
      {:error, _} -> create_service_error("Event store not available")
    end
  end

  @impl EventStoreContract
  @spec store_batch([Event.t()]) :: {:ok, [non_neg_integer()]} | {:error, Error.t()}
  def store_batch(events) when is_list(events) do
    case Foundation.ServiceRegistry.lookup(:production, :event_store) do
      {:ok, pid} -> GenServer.call(pid, {:store_batch, events})
      {:error, _} -> create_service_error("Event store not available")
    end
  end

  @impl EventStoreContract
  @spec get(non_neg_integer()) :: {:ok, Event.t()} | {:error, Error.t()}
  def get(event_id) when is_integer(event_id) and event_id > 0 do
    case Foundation.ServiceRegistry.lookup(:production, :event_store) do
      {:ok, pid} -> GenServer.call(pid, {:get_event, event_id})
      {:error, _} -> create_service_error("Event store not available")
    end
  end

  @impl EventStoreContract
  @spec query(map()) :: {:ok, [Event.t()]} | {:error, Error.t()}
  def query(query_map) when is_map(query_map) do
    case Foundation.ServiceRegistry.lookup(:production, :event_store) do
      {:ok, pid} -> GenServer.call(pid, {:query_events, query_map})
      {:error, _} -> create_service_error("Event store not available")
    end
  end

  @impl EventStoreContract
  @spec get_by_correlation(String.t()) :: {:ok, [Event.t()]} | {:error, Error.t()}
  def get_by_correlation(correlation_id) when is_binary(correlation_id) do
    case Foundation.ServiceRegistry.lookup(:production, :event_store) do
      {:ok, pid} -> GenServer.call(pid, {:get_by_correlation, correlation_id})
      {:error, _} -> create_service_error("Event store not available")
    end
  end

  @impl EventStoreContract
  @spec prune_before(integer()) :: {:ok, non_neg_integer()} | {:error, Error.t()}
  def prune_before(timestamp) when is_integer(timestamp) do
    case Foundation.ServiceRegistry.lookup(:production, :event_store) do
      {:ok, pid} -> GenServer.call(pid, {:prune_before, timestamp})
      {:error, _} -> create_service_error("Event store not available")
    end
  end

  @impl EventStoreContract
  @spec stats() :: {:ok, map()} | {:error, Error.t()}
  def stats do
    case Foundation.ServiceRegistry.lookup(:production, :event_store) do
      {:ok, pid} -> GenServer.call(pid, :get_stats)
      {:error, _} -> create_service_error("Event store not available")
    end
  end

  @impl EventStoreContract
  @spec available?() :: boolean()
  def available? do
    case Foundation.ServiceRegistry.lookup(:production, :event_store) do
      {:ok, _pid} -> true
      {:error, _} -> false
    end
  end

  @impl EventStoreContract
  @spec initialize() :: :ok | {:error, Error.t()}
  def initialize do
    initialize([])
  end

  # Note: initialize/1 is not in the contract but we need it for internal use
  @spec initialize(keyword()) :: :ok | {:error, Error.t()}
  def initialize(opts) do
    case Foundation.ServiceRegistry.lookup(:production, :event_store) do
      {:ok, _pid} ->
        :ok

      {:error, _} ->
        case start_link(opts) do
          {:ok, _pid} ->
            :ok

          {:error, {:already_started, _pid}} ->
            :ok

          {:error, reason} ->
            create_service_error("Failed to initialize event store: #{inspect(reason)}")
        end
    end
  end

  @impl EventStoreContract
  @spec status() :: {:ok, map()} | {:error, Error.t()}
  def status do
    case Foundation.ServiceRegistry.lookup(:production, :event_store) do
      {:ok, pid} -> GenServer.call(pid, :get_status)
      {:error, _} -> create_service_error("Event store not available")
    end
  end

  @doc """
  Reset all stored events and metrics for testing purposes.

  This function should only be used in test environments.
  """
  @spec reset_state() :: :ok | {:error, Error.t()}
  def reset_state do
    if Application.get_env(:foundation, :test_mode, false) do
      case Foundation.ServiceRegistry.lookup(:production, :event_store) do
        {:ok, pid} -> GenServer.call(pid, :reset_state)
        {:error, _} -> create_service_error("Event store not available")
      end
    else
      {:error,
       Error.new(
         code: 3002,
         error_type: :operation_forbidden,
         message: "State reset only allowed in test mode",
         severity: :high,
         category: :security,
         subcategory: :authorization
       )}
    end
  end

  ## GenServer API

  @spec start_link(keyword()) :: GenServer.on_start()
  def start_link(opts \\ []) do
    namespace = Keyword.get(opts, :namespace, :production)
    name = Foundation.ServiceRegistry.via_tuple(namespace, :event_store)
    GenServer.start_link(__MODULE__, Keyword.put(opts, :namespace, namespace), name: name)
  end

  @spec stop() :: :ok
  def stop do
    case Foundation.ServiceRegistry.lookup(:production, :event_store) do
      {:ok, pid} -> GenServer.stop(pid)
      {:error, _} -> :ok
    end
  end

  ## GenServer Callbacks

  @impl GenServer
  @spec init(keyword()) :: {:ok, server_state()}
  def init(_opts) do
    state = %{
      events: %{},
      next_id: 1,
      metrics: %{
        events_stored: 0,
        events_pruned: 0,
        start_time: System.monotonic_time(:millisecond)
      }
    }

    Logger.info("Event store initialized successfully")
    {:ok, state}
  end

  @impl GenServer
  @spec handle_call(term(), GenServer.from(), server_state()) ::
          {:reply, term(), server_state()}
  def handle_call({:store_event, event}, _from, state) do
    case EventValidator.validate(event) do
      :ok ->
        # Use event's ID if provided, otherwise assign next available ID
        event_id =
          if event.event_id && event.event_id > 0 do
            event.event_id
          else
            state.next_id
          end

        updated_event = %{event | event_id: event_id}
        new_events = Map.put(state.events, event_id, updated_event)

        new_next_id = max(state.next_id, event_id) + 1
        new_metrics = Map.update!(state.metrics, :events_stored, &(&1 + 1))

        new_state = %{
          state
          | events: new_events,
            next_id: new_next_id,
            metrics: new_metrics
        }

        # Emit telemetry for events stored
        TelemetryService.emit_counter([:foundation, :event_store, :events_stored], 1, %{
          event_type: updated_event.event_type,
          event_id: event_id
        })

        {:reply, {:ok, event_id}, new_state}

      {:error, _} = error ->
        {:reply, error, state}
    end
  end

  @impl GenServer
  def handle_call({:store_batch, events}, _from, state) do
    # Validate all events first
    case validate_batch(events) do
      :ok ->
        {new_state, event_ids} = store_events_batch(events, state)
        {:reply, {:ok, event_ids}, new_state}

      {:error, _} = error ->
        {:reply, error, state}
    end
  end

  @impl GenServer
  def handle_call({:get_event, event_id}, _from, %{events: events} = state) do
    case Map.get(events, event_id) do
      nil ->
        error =
          Error.new(
            code: 3001,
            error_type: :not_found,
            message: "Event not found",
            severity: :low,
            context: %{event_id: event_id},
            category: :data,
            subcategory: :retrieval
          )

        {:reply, {:error, error}, state}

      event ->
        {:reply, {:ok, event}, state}
    end
  end

  @impl GenServer
  def handle_call({:query_events, query_map}, _from, %{events: events} = state) do
    filtered_events = apply_query_filters(events, query_map)
    {:reply, {:ok, filtered_events}, state}
  end

  @impl GenServer
  def handle_call({:get_by_correlation, correlation_id}, _from, %{events: events} = state) do
    correlated_events =
      events
      |> Map.values()
      |> Enum.filter(fn event -> event.correlation_id == correlation_id end)
      |> Enum.sort_by(& &1.timestamp)

    {:reply, {:ok, correlated_events}, state}
  end

  @impl GenServer
  def handle_call({:prune_before, cutoff_timestamp}, _from, %{events: events} = state) do
    {remaining_events, pruned_count} =
      Enum.reduce(events, {%{}, 0}, fn {id, event}, {acc_events, count} ->
        if event.timestamp < cutoff_timestamp do
          {acc_events, count + 1}
        else
          {Map.put(acc_events, id, event), count}
        end
      end)

    new_metrics = Map.update!(state.metrics, :events_pruned, &(&1 + pruned_count))
    new_state = %{state | events: remaining_events, metrics: new_metrics}

    {:reply, {:ok, pruned_count}, new_state}
  end

  @impl GenServer
  def handle_call(:get_stats, _from, %{events: events, metrics: metrics} = state) do
    current_time = System.monotonic_time(:millisecond)

    stats =
      Map.merge(metrics, %{
        current_event_count: map_size(events),
        uptime_ms: current_time - metrics.start_time,
        memory_usage_estimate: estimate_memory_usage(events)
      })

    {:reply, {:ok, stats}, state}
  end

  @impl GenServer
  def handle_call(:get_status, _from, %{events: events} = state) do
    status = %{
      status: :running,
      event_count: map_size(events),
      next_id: state.next_id
    }

    {:reply, {:ok, status}, state}
  end

  @impl GenServer
  def handle_call(:reset_state, _from, _state) do
    # Reset to initial state (for testing)
    new_state = %{
      events: %{},
      next_id: 1,
      metrics: %{
        events_stored: 0,
        events_pruned: 0,
        start_time: System.monotonic_time(:millisecond)
      }
    }

    {:reply, :ok, new_state}
  end

  @impl GenServer
  def handle_info(msg, state) do
    Logger.warning("Unexpected message in EventStore: #{inspect(msg)}")
    {:noreply, state}
  end

  ## Private Functions

  @spec validate_batch([Event.t()]) :: :ok | {:error, Error.t()}
  defp validate_batch(events) do
    Enum.reduce_while(events, :ok, fn event, :ok ->
      case EventValidator.validate(event) do
        :ok -> {:cont, :ok}
        {:error, _} = error -> {:halt, error}
      end
    end)
  end

  @spec store_events_batch([Event.t()], server_state()) :: {server_state(), [non_neg_integer()]}
  defp store_events_batch(events, state) do
    {new_state, event_ids} =
      Enum.reduce(events, {state, []}, fn event, {acc_state, acc_ids} ->
        event_id =
          if event.event_id && event.event_id > 0 do
            event.event_id
          else
            acc_state.next_id
          end

        updated_event = %{event | event_id: event_id}
        new_events = Map.put(acc_state.events, event_id, updated_event)
        new_next_id = max(acc_state.next_id, event_id) + 1

        updated_state = %{
          acc_state
          | events: new_events,
            next_id: new_next_id
        }

        {updated_state, [event_id | acc_ids]}
      end)

    # Update metrics
    events_count = length(events)
    new_metrics = Map.update!(new_state.metrics, :events_stored, &(&1 + events_count))
    final_state = %{new_state | metrics: new_metrics}

    # Emit telemetry for batch events stored
    TelemetryService.emit_counter([:foundation, :event_store, :events_stored], events_count, %{
      batch_size: events_count
    })

    {final_state, Enum.reverse(event_ids)}
  end

  @spec apply_query_filters(map(), map()) :: [Event.t()]
  defp apply_query_filters(events, query_map) do
    events
    |> Map.values()
    |> filter_by_event_type(query_map[:event_type])
    |> filter_by_time_range(query_map[:time_range])
    |> apply_pagination(query_map)
  end

  @spec filter_by_event_type([Event.t()], atom() | nil) :: [Event.t()]
  defp filter_by_event_type(events, nil), do: events

  defp filter_by_event_type(events, event_type) do
    Enum.filter(events, fn event -> event.event_type == event_type end)
  end

  @spec filter_by_time_range([Event.t()], {integer(), integer()} | nil) :: [Event.t()]
  defp filter_by_time_range(events, nil), do: events

  defp filter_by_time_range(events, {start_time, end_time}) do
    Enum.filter(events, fn event ->
      event.timestamp >= start_time && event.timestamp <= end_time
    end)
  end

  @spec apply_pagination([Event.t()], map()) :: [Event.t()]
  defp apply_pagination(events, query_map) do
    events
    |> maybe_sort(query_map[:order_by])
    |> maybe_offset(query_map[:offset])
    |> maybe_limit(query_map[:limit])
  end

  @spec maybe_sort([Event.t()], atom() | nil) :: [Event.t()]
  defp maybe_sort(events, nil), do: events
  defp maybe_sort(events, :event_id), do: Enum.sort_by(events, & &1.event_id)
  defp maybe_sort(events, :timestamp), do: Enum.sort_by(events, & &1.timestamp)
  defp maybe_sort(events, _), do: events

  @spec maybe_offset([Event.t()], non_neg_integer() | nil) :: [Event.t()]
  defp maybe_offset(events, nil), do: events
  defp maybe_offset(events, offset) when offset >= 0, do: Enum.drop(events, offset)
  defp maybe_offset(events, _), do: events

  @spec maybe_limit([Event.t()], pos_integer() | nil) :: [Event.t()]
  defp maybe_limit(events, nil), do: events
  defp maybe_limit(events, limit) when limit > 0, do: Enum.take(events, limit)
  defp maybe_limit(events, _), do: events

  @spec estimate_memory_usage(map()) :: non_neg_integer()
  defp estimate_memory_usage(events) do
    # Simple estimation - in a real implementation this might be more sophisticated
    # Rough estimate of 1KB per event
    map_size(events) * 1000
  end

  @spec create_service_error(String.t()) :: {:error, Error.t()}
  defp create_service_error(message) do
    error =
      Error.new(
        code: 3000,
        error_type: :service_unavailable,
        message: message,
        severity: :high,
        category: :system,
        subcategory: :availability
      )

    {:error, error}
  end
end
</file>

<file path="foundation/services/telemetry_service.ex">
defmodule Foundation.Services.TelemetryService do
  @moduledoc """
  GenServer implementation for telemetry collection and metrics.

  Provides structured telemetry with automatic metric collection,
  event emission, and performance monitoring.
  """

  use GenServer
  require Logger

  alias Foundation.Types.Error
  alias Foundation.Contracts.Telemetry

  @behaviour Telemetry

  @type server_state :: %{
          metrics: %{atom() => map()},
          handlers: %{[atom()] => function()},
          config: map(),
          namespace: atom()
        }

  @default_config %{
    enable_vm_metrics: true,
    enable_process_metrics: true,
    # 5 minutes
    metric_retention_ms: 300_000,
    # 1 minute
    cleanup_interval: 60_000
  }

  ## Public API (Telemetry Behaviour Implementation)

  @impl Telemetry
  @spec execute([atom()], map(), map()) :: :ok
  def execute(event_name, measurements, metadata) when is_list(event_name) do
    case Foundation.ServiceRegistry.lookup(:production, :telemetry_service) do
      # Fail silently for telemetry
      {:error, _} -> :ok
      {:ok, pid} -> GenServer.cast(pid, {:execute_event, event_name, measurements, metadata})
    end
  end

  @impl Telemetry
  @spec measure([atom()], map(), (-> term())) :: term()
  def measure(event_name, metadata, fun) when is_list(event_name) and is_function(fun, 0) do
    start_time = System.monotonic_time()

    try do
      result = fun.()
      end_time = System.monotonic_time()
      duration = end_time - start_time

      measurements = %{duration: duration}
      execute(event_name ++ [:stop], measurements, metadata)

      result
    rescue
      error ->
        end_time = System.monotonic_time()
        duration = end_time - start_time

        measurements = %{duration: duration}
        error_metadata = Map.put(metadata, :error, error)
        execute(event_name ++ [:exception], measurements, error_metadata)

        reraise error, __STACKTRACE__
    end
  end

  @impl Telemetry
  @spec emit_counter([atom()], map()) :: :ok
  def emit_counter(event_name, metadata) when is_list(event_name) do
    measurements = %{counter: 1}
    execute(event_name, measurements, metadata)
  end

  # Overloaded version for tests that pass a value
  @spec emit_counter([atom()], number(), map()) :: :ok
  def emit_counter(event_name, value, metadata) when is_list(event_name) and is_number(value) do
    measurements = %{counter: value}
    execute(event_name, measurements, metadata)
  end

  @impl Telemetry
  @spec emit_gauge([atom()], number(), map()) :: :ok
  def emit_gauge(event_name, value, metadata) when is_list(event_name) and is_number(value) do
    measurements = %{gauge: value}
    execute(event_name, measurements, metadata)
  end

  @impl Telemetry
  @spec get_metrics() :: {:ok, map()} | {:error, Error.t()}
  def get_metrics do
    case Foundation.ServiceRegistry.lookup(:production, :telemetry_service) do
      {:error, _} -> create_service_error("Telemetry service not started")
      {:ok, pid} -> GenServer.call(pid, :get_metrics)
    end
  end

  @impl Telemetry
  @spec attach_handlers([[atom()]]) :: :ok | {:error, Error.t()}
  def attach_handlers(event_names) when is_list(event_names) do
    case Foundation.ServiceRegistry.lookup(:production, :telemetry_service) do
      {:error, _} -> create_service_error("Telemetry service not started")
      {:ok, pid} -> GenServer.call(pid, {:attach_handlers, event_names})
    end
  end

  @impl Telemetry
  @spec detach_handlers([[atom()]]) :: :ok
  def detach_handlers(event_names) when is_list(event_names) do
    case Foundation.ServiceRegistry.lookup(:production, :telemetry_service) do
      # Fail silently
      {:error, _} -> :ok
      {:ok, pid} -> GenServer.cast(pid, {:detach_handlers, event_names})
    end
  end

  @impl Telemetry
  @spec available?() :: boolean()
  def available? do
    case Foundation.ServiceRegistry.lookup(:production, :telemetry_service) do
      {:ok, _pid} -> true
      {:error, _} -> false
    end
  end

  @impl Telemetry
  @spec initialize() :: :ok | {:error, Error.t()}
  def initialize do
    initialize([])
  end

  @impl Telemetry
  @spec status() :: {:ok, map()} | {:error, Error.t()}
  def status do
    case Foundation.ServiceRegistry.lookup(:production, :telemetry_service) do
      {:error, _} -> create_service_error("Telemetry service not started")
      {:ok, pid} -> GenServer.call(pid, :get_status)
    end
  end

  ## GenServer API

  @spec start_link(keyword()) :: GenServer.on_start()
  def start_link(opts \\ []) do
    namespace = Keyword.get(opts, :namespace, :production)
    name = Foundation.ServiceRegistry.via_tuple(namespace, :telemetry_service)
    GenServer.start_link(__MODULE__, Keyword.put(opts, :namespace, namespace), name: name)
  end

  @spec stop() :: :ok
  def stop do
    case Foundation.ServiceRegistry.lookup(:production, :telemetry_service) do
      {:ok, pid} -> GenServer.stop(pid)
      {:error, _} -> :ok
    end
  end

  @doc """
  Reset all metrics (for testing purposes).
  """
  @spec reset_metrics() :: :ok | {:error, Error.t()}
  def reset_metrics do
    case Foundation.ServiceRegistry.lookup(:production, :telemetry_service) do
      {:error, _} -> create_service_error("Telemetry service not started")
      {:ok, pid} -> GenServer.call(pid, :clear_metrics)
    end
  end

  @doc """
  Reset all internal state for testing purposes.

  Clears all metrics, handlers, and resets configuration to defaults.
  This function should only be used in test environments.
  """
  @spec reset_state() :: :ok | {:error, Error.t()}
  def reset_state do
    if Application.get_env(:foundation, :test_mode, false) do
      case Foundation.ServiceRegistry.lookup(:production, :telemetry_service) do
        {:error, _} -> create_service_error("Telemetry service not started")
        {:ok, pid} -> GenServer.call(pid, :reset_state)
      end
    else
      {:error,
       Error.new(
         code: 7002,
         error_type: :operation_forbidden,
         message: "State reset only allowed in test mode",
         severity: :high,
         category: :security,
         subcategory: :authorization
       )}
    end
  end

  ## GenServer Callbacks

  @impl GenServer
  @spec init(keyword()) :: {:ok, server_state()}
  def init(opts) do
    config = Map.merge(@default_config, Map.new(opts))
    namespace = Keyword.get(opts, :namespace, :production)

    state = %{
      metrics: %{},
      handlers: %{},
      config: config,
      namespace: namespace
    }

    # Schedule periodic cleanup
    schedule_cleanup(config.cleanup_interval)

    # Attach VM metrics if enabled
    if config.enable_vm_metrics do
      attach_vm_metrics()
    end

    Logger.info("Telemetry service initialized successfully in namespace #{inspect(namespace)}")
    {:ok, state}
  end

  @impl GenServer
  @spec handle_cast(term(), server_state()) :: {:noreply, server_state()}
  def handle_cast({:execute_event, event_name, measurements, metadata}, state) do
    new_state = record_metric(event_name, measurements, metadata, state)

    # Execute any attached handlers
    execute_handlers(event_name, measurements, metadata, state.handlers)

    # Also emit to standard telemetry system for external listeners
    :telemetry.execute(event_name, measurements, metadata)

    {:noreply, new_state}
  end

  @impl GenServer
  def handle_cast({:detach_handlers, event_names}, %{handlers: handlers} = state) do
    new_handlers = Map.drop(handlers, event_names)
    new_state = %{state | handlers: new_handlers}
    {:noreply, new_state}
  end

  @impl GenServer
  @spec handle_call(term(), GenServer.from(), server_state()) ::
          {:reply, term(), server_state()}
  def handle_call(:get_metrics, _from, %{metrics: metrics} = state) do
    # Transform flat event names into nested structure for API compatibility
    nested_metrics = transform_to_nested_structure(metrics)

    # Add current timestamp to metrics
    timestamped_metrics = Map.put(nested_metrics, :retrieved_at, System.monotonic_time())

    {:reply, {:ok, timestamped_metrics}, state}
  end

  @impl GenServer
  def handle_call(:get_status, _from, state) do
    status = %{
      status: :running,
      metrics_count: map_size(state.metrics),
      handlers_count: map_size(state.handlers),
      config: state.config
    }

    {:reply, {:ok, status}, state}
  end

  @impl GenServer
  def handle_call(:clear_metrics, _from, state) do
    new_state = %{state | metrics: %{}}
    {:reply, :ok, new_state}
  end

  @impl GenServer
  def handle_call(:reset_state, _from, %{namespace: namespace} = _state) do
    # Reset to initial state (for testing)
    config = Map.merge(@default_config, %{})

    new_state = %{
      metrics: %{},
      handlers: %{},
      config: config,
      namespace: namespace
    }

    {:reply, :ok, new_state}
  end

  @impl GenServer
  def handle_call({:attach_handlers, event_names}, _from, %{handlers: handlers} = state) do
    new_handlers =
      Enum.reduce(event_names, handlers, fn event_name, acc ->
        handler_fn = create_default_handler(event_name)
        Map.put(acc, event_name, handler_fn)
      end)

    new_state = %{state | handlers: new_handlers}
    {:reply, :ok, new_state}
  end

  @impl GenServer
  @spec handle_info(term(), server_state()) :: {:noreply, server_state()}
  def handle_info(:cleanup_old_metrics, %{config: config} = state) do
    new_state = cleanup_old_metrics(state, config.metric_retention_ms)
    schedule_cleanup(config.cleanup_interval)
    {:noreply, new_state}
  end

  @impl GenServer
  def handle_info(msg, state) do
    Logger.debug("Unexpected message in TelemetryService: #{inspect(msg)}")
    {:noreply, state}
  end

  ## Private Functions

  defp record_metric(event_name, measurements, metadata, %{metrics: metrics} = state) do
    timestamp = System.monotonic_time()

    metric_entry = %{
      timestamp: timestamp,
      measurements: measurements,
      metadata: metadata,
      count: 1
    }

    new_metrics =
      Map.update(metrics, event_name, metric_entry, fn existing ->
        %{
          existing
          | timestamp: timestamp,
            measurements: merge_measurements(existing.measurements, measurements),
            count: existing.count + 1
        }
      end)

    %{state | metrics: new_metrics}
  end

  defp merge_measurements(existing, new) do
    Map.merge(existing, new, fn
      :gauge, _old_val, new_val ->
        # For gauges, always use the latest value (no averaging)
        new_val

      :counter, old_val, new_val when is_number(old_val) and is_number(new_val) ->
        # For counters, accumulate the values
        old_val + new_val

      _key, old_val, new_val when is_number(old_val) and is_number(new_val) ->
        # For other numeric values, keep running average (backwards compatibility)
        (old_val + new_val) / 2

      _key, _old_val, new_val ->
        # For non-numeric, keep the new value
        new_val
    end)
  end

  defp execute_handlers(event_name, measurements, metadata, handlers) do
    case Map.get(handlers, event_name) do
      nil ->
        :ok

      handler_fn when is_function(handler_fn) ->
        try do
          handler_fn.(event_name, measurements, metadata)
        rescue
          error ->
            Logger.warning("Telemetry handler failed: #{inspect(error)}")
        end
    end
  end

  defp create_default_handler(event_name) do
    fn ^event_name, measurements, metadata ->
      Logger.debug("Telemetry event: #{inspect(event_name)}",
        measurements: measurements,
        metadata: metadata
      )
    end
  end

  defp cleanup_old_metrics(%{metrics: metrics} = state, retention_ms) do
    current_time = System.monotonic_time()
    cutoff_time = current_time - retention_ms

    new_metrics =
      Enum.filter(metrics, fn {_event_name, metric_data} ->
        metric_data.timestamp > cutoff_time
      end)
      |> Map.new()

    %{state | metrics: new_metrics}
  end

  defp attach_vm_metrics do
    # Attach standard VM telemetry events
    vm_events = [
      [:vm, :memory],
      [:vm, :total_run_queue_lengths],
      [:vm, :system_counts]
    ]

    Enum.each(vm_events, fn event_name ->
      emit_gauge(event_name, 1, %{source: :vm})
    end)
  end

  defp schedule_cleanup(interval) do
    Process.send_after(self(), :cleanup_old_metrics, interval)
  end

  defp create_service_error(message) do
    error =
      Error.new(
        error_type: :service_unavailable,
        message: message,
        category: :system,
        subcategory: :initialization,
        severity: :medium
      )

    {:error, error}
  end

  ## Additional Functions

  @spec initialize(keyword()) :: :ok | {:error, Error.t()}
  def initialize(opts) do
    namespace = Keyword.get(opts, :namespace, :production)

    case Foundation.ServiceRegistry.lookup(namespace, :telemetry_service) do
      {:ok, _pid} ->
        # Service already running
        :ok

      {:error, _} ->
        # Service not running, try to start it
        case start_link(opts) do
          {:ok, _pid} ->
            :ok

          {:error, {:already_started, _pid}} ->
            :ok

          {:error, reason} ->
            {:error,
             Error.new(
               error_type: :service_initialization_failed,
               message: "Failed to initialize telemetry service",
               context: %{reason: reason},
               category: :system,
               subcategory: :startup,
               severity: :high
             )}
        end
    end
  end

  defp transform_to_nested_structure(metrics) do
    Enum.reduce(metrics, %{}, fn {event_name, metric_data}, acc ->
      case event_name do
        [:foundation, :event_store, :events_stored] ->
          # Transform to the expected nested structure for events_stored
          # Safely build the nested path
          foundation_map = Map.get(acc, :foundation, %{})
          updated_foundation = Map.put(foundation_map, :events_stored, metric_data.count || 0)
          Map.put(acc, :foundation, updated_foundation)

        [:foundation, :config_updates] ->
          # Transform config_updates metric
          foundation_map = Map.get(acc, :foundation, %{})
          updated_foundation = Map.put(foundation_map, :config_updates, metric_data.count || 0)
          Map.put(acc, :foundation, updated_foundation)

        [:foundation, :config_resets] ->
          # Transform config_resets metric
          foundation_map = Map.get(acc, :foundation, %{})
          updated_foundation = Map.put(foundation_map, :config_resets, metric_data.count || 0)
          Map.put(acc, :foundation, updated_foundation)

        [:foundation, :config_operations] ->
          # Transform general config operations metric
          foundation_map = Map.get(acc, :foundation, %{})
          updated_foundation = Map.put(foundation_map, :config_operations, metric_data.count || 0)
          Map.put(acc, :foundation, updated_foundation)

        [:foundation | rest] ->
          # Handle other foundation metrics
          nested_path = [:foundation] ++ rest
          put_nested_value(acc, nested_path, metric_data)

        [first | rest] when rest != [] ->
          # Handle other nested metrics
          nested_path = [first] ++ rest
          put_nested_value(acc, nested_path, metric_data)

        [single] ->
          # Single-level metrics
          Map.put(acc, single, metric_data)

        _ ->
          acc
      end
    end)
  end

  defp put_nested_value(map, [key], value) do
    Map.put(map, key, value)
  end

  defp put_nested_value(map, [key | rest], value) do
    Map.update(map, key, put_nested_value(%{}, rest, value), fn existing ->
      put_nested_value(existing, rest, value)
    end)
  end
end
</file>

<file path="foundation/types/config.ex">
defmodule Foundation.Types.Config do
  @moduledoc """
  Pure data structure for Foundation configuration.

  Contains no business logic - just data and Access implementation.
  All validation and manipulation logic is in separate modules.

  This struct defines the complete configuration schema for Foundation,
  including AI, capture, storage, interface, and development settings.

  See `@type t` for the complete type specification.

  ## Examples

      iex> config = Foundation.Types.Config.new()
      iex> config.ai.provider
      :mock

      iex> config = Foundation.Types.Config.new(dev: %{debug_mode: true})
      iex> config.dev.debug_mode
      true
  """

  @behaviour Access

  # All fields have meaningful defaults, so no @enforce_keys needed
  defstruct [
    # AI Configuration
    ai: %{
      provider: :mock,
      api_key: nil,
      model: "gpt-4",
      analysis: %{
        max_file_size: 1_000_000,
        timeout: 30_000,
        cache_ttl: 3600
      },
      planning: %{
        default_strategy: :balanced,
        performance_target: 0.01,
        sampling_rate: 1.0
      }
    },

    # Capture Configuration
    capture: %{
      ring_buffer: %{
        size: 1024,
        max_events: 1000,
        overflow_strategy: :drop_oldest,
        num_buffers: :schedulers
      },
      processing: %{
        batch_size: 100,
        flush_interval: 50,
        max_queue_size: 1000
      },
      vm_tracing: %{
        enable_spawn_trace: true,
        enable_exit_trace: true,
        enable_message_trace: false,
        trace_children: true
      }
    },

    # Storage Configuration
    storage: %{
      hot: %{
        max_events: 100_000,
        max_age_seconds: 3600,
        prune_interval: 60_000
      },
      warm: %{
        enable: false,
        path: "./foundation_data",
        max_size_mb: 100,
        compression: :zstd
      },
      cold: %{
        enable: false
      }
    },

    # Interface Configuration
    interface: %{
      query_timeout: 10_000,
      max_results: 1000,
      enable_streaming: true
    },

    # Development Configuration
    dev: %{
      debug_mode: false,
      verbose_logging: false,
      performance_monitoring: true
    },

    # Infrastructure Configuration
    infrastructure: %{
      rate_limiting: %{
        default_rules: %{
          # 100 requests per minute
          api_calls: %{scale: 60_000, limit: 100},
          # 500 queries per minute
          db_queries: %{scale: 60_000, limit: 500},
          # 10 operations per second
          file_operations: %{scale: 1_000, limit: 10}
        },
        enabled: true,
        # 5 minutes
        cleanup_interval: 300_000
      },
      circuit_breaker: %{
        default_config: %{
          failure_threshold: 5,
          recovery_time: 30_000,
          call_timeout: 5_000
        },
        enabled: true
      },
      connection_pool: %{
        default_config: %{
          size: 10,
          max_overflow: 5,
          strategy: :lifo
        },
        enabled: true
      }
    }
  ]

  @typedoc "AI configuration section"
  @type ai_config :: %{
          provider: :mock | :openai | :anthropic,
          api_key: String.t() | nil,
          model: String.t(),
          analysis: %{
            max_file_size: pos_integer(),
            timeout: pos_integer(),
            cache_ttl: pos_integer()
          },
          planning: %{
            default_strategy: :balanced | :fast | :thorough,
            performance_target: float(),
            sampling_rate: float()
          }
        }

  @typedoc "Capture configuration section"
  @type capture_config :: %{
          ring_buffer: %{
            size: pos_integer(),
            max_events: pos_integer(),
            overflow_strategy: :drop_oldest | :drop_newest | :error,
            num_buffers: :schedulers | pos_integer()
          },
          processing: %{
            batch_size: pos_integer(),
            flush_interval: pos_integer(),
            max_queue_size: pos_integer()
          },
          vm_tracing: %{
            enable_spawn_trace: boolean(),
            enable_exit_trace: boolean(),
            enable_message_trace: boolean(),
            trace_children: boolean()
          }
        }

  @typedoc "Storage configuration section"
  @type storage_config :: %{
          hot: %{
            max_events: pos_integer(),
            max_age_seconds: pos_integer(),
            prune_interval: pos_integer()
          },
          warm: %{
            enable: boolean(),
            path: String.t(),
            max_size_mb: pos_integer(),
            compression: :zstd | :gzip | :none
          },
          cold: %{
            enable: boolean()
          }
        }

  @typedoc "Interface configuration section"
  @type interface_config :: %{
          query_timeout: pos_integer(),
          max_results: pos_integer(),
          enable_streaming: boolean()
        }

  @typedoc "Development configuration section"
  @type dev_config :: %{
          debug_mode: boolean(),
          verbose_logging: boolean(),
          performance_monitoring: boolean()
        }

  @typedoc "Infrastructure configuration section"
  @type infrastructure_config :: %{
          rate_limiting: %{
            default_rules: %{atom() => %{scale: pos_integer(), limit: pos_integer()}},
            enabled: boolean(),
            cleanup_interval: pos_integer()
          },
          circuit_breaker: %{
            default_config: %{
              failure_threshold: pos_integer(),
              recovery_time: pos_integer(),
              call_timeout: pos_integer()
            },
            enabled: boolean()
          },
          connection_pool: %{
            default_config: %{
              size: pos_integer(),
              max_overflow: pos_integer(),
              strategy: :lifo | :fifo
            },
            enabled: boolean()
          }
        }

  @type t :: %__MODULE__{
          ai: ai_config(),
          capture: capture_config(),
          storage: storage_config(),
          interface: interface_config(),
          dev: dev_config(),
          infrastructure: infrastructure_config()
        }

  ## Access Behavior Implementation

  @doc """
  Fetch a configuration key.

  Implementation of the Access behaviour for configuration structs.
  """
  @impl Access
  @spec fetch(t(), atom()) :: {:ok, term()} | :error
  def fetch(%__MODULE__{} = config, key) do
    config
    |> Map.from_struct()
    |> Map.fetch(key)
  end

  @doc """
  Get and update a configuration key.

  Implementation of the Access behaviour for configuration structs.
  """
  @impl Access
  @spec get_and_update(t(), atom(), (term() -> {term(), term()} | :pop)) ::
          {term(), t()}
  def get_and_update(%__MODULE__{} = config, key, function) do
    map_config = Map.from_struct(config)

    case Map.get_and_update(map_config, key, function) do
      {current_value, updated_map} ->
        case struct(__MODULE__, updated_map) do
          updated_config when is_struct(updated_config, __MODULE__) ->
            {current_value, updated_config}

          _ ->
            {current_value, config}
        end
    end
  end

  @doc """
  Pop a configuration key.

  Implementation of the Access behaviour for configuration structs.
  """
  @impl Access
  @spec pop(t(), atom()) :: {term(), t()}
  def pop(%__MODULE__{} = config, key) do
    map_config = Map.from_struct(config)
    {value, updated_map} = Map.pop(map_config, key)
    updated_config = struct(__MODULE__, updated_map)
    {value, updated_config}
  end

  @doc """
  Create a new configuration with default values.

  ## Examples

      iex> config = Foundation.Types.Config.new()
      iex> config.ai.provider
      :mock

      iex> config.capture.ring_buffer.size
      1024
  """
  @spec new() :: t()
  def new, do: %__MODULE__{}

  @doc """
  Create a new configuration with overrides.

  Performs deep merging of nested configuration maps.

  ## Parameters
  - `overrides`: Keyword list of configuration overrides

  ## Examples

      iex> config = Foundation.Types.Config.new(dev: %{debug_mode: true})
      iex> config.dev.debug_mode
      true

      iex> config = Foundation.Types.Config.new(ai: %{provider: :openai})
      iex> config.ai.provider
      :openai
  """
  @spec new(keyword()) :: t()
  def new(overrides) do
    config = new()

    Enum.reduce(overrides, config, fn {key, value}, acc ->
      case Map.get(acc, key) do
        existing_value when is_map(existing_value) and is_map(value) ->
          # Deep merge maps
          merged_value = deep_merge(existing_value, value)
          Map.put(acc, key, merged_value)

        _ ->
          # Replace non-map values
          Map.put(acc, key, value)
      end
    end)
  end

  # Helper function for deep merging maps
  @spec deep_merge(map(), map()) :: map()
  defp deep_merge(original, override) when is_map(original) and is_map(override) do
    Map.merge(original, override, fn _key, v1, v2 ->
      if is_map(v1) and is_map(v2) do
        deep_merge(v1, v2)
      else
        v2
      end
    end)
  end
end
</file>

<file path="foundation/types/error.ex">
defmodule Foundation.Types.Error do
  @moduledoc """
  Pure data structure for Foundation errors.

  Contains structured error information with hierarchical codes,
  context, and recovery suggestions. No business logic - just data.

  All errors must have a code, error type, message, and severity.
  Timestamp defaults to creation time if not provided.

  See `@type t` for the complete type specification.

  ## Examples

      iex> error = Foundation.Types.Error.new([
      ...>   code: 1001,
      ...>   error_type: :validation_failed,
      ...>   message: "Invalid configuration",
      ...>   severity: :high
      ...> ])
      iex> error.error_type
      :validation_failed
  """

  @typedoc "Specific error type identifier"
  @type error_code :: atom()

  @typedoc "Additional context information for the error"
  @type error_context :: map()

  @typedoc "Stack trace information as a list of maps"
  @type stacktrace_info :: [map()]

  @typedoc "High-level error category"
  @type error_category :: :config | :system | :data | :external

  @typedoc "Specific error subcategory within a category"
  @type error_subcategory :: :structure | :validation | :access | :runtime

  @typedoc "Error severity level"
  @type error_severity :: :low | :medium | :high | :critical

  @typedoc "Strategy for retrying failed operations"
  @type retry_strategy :: :no_retry | :immediate | :fixed_delay | :exponential_backoff

  @enforce_keys [:code, :error_type, :message, :severity]
  defstruct [
    :code,
    :error_type,
    :message,
    :severity,
    :context,
    :correlation_id,
    :timestamp,
    :stacktrace,
    :category,
    :subcategory,
    :retry_strategy,
    :recovery_actions
  ]

  @type t :: %__MODULE__{
          code: pos_integer(),
          error_type: error_code(),
          message: String.t(),
          severity: error_severity(),
          context: error_context() | nil,
          correlation_id: String.t() | nil,
          timestamp: DateTime.t() | nil,
          stacktrace: stacktrace_info() | nil,
          category: error_category() | nil,
          subcategory: error_subcategory() | nil,
          retry_strategy: retry_strategy() | nil,
          recovery_actions: [String.t()] | nil
        }

  @doc """
  Create a new error structure.

  ## Parameters
  - `fields`: Keyword list containing at minimum `:code`, `:error_type`, `:message`, and `:severity`

  ## Examples

      iex> error = Foundation.Types.Error.new([
      ...>   code: 2001,
      ...>   error_type: :network_timeout,
      ...>   message: "Connection timed out",
      ...>   severity: :medium,
      ...>   retry_strategy: :exponential_backoff
      ...> ])
      iex> error.error_type
      :network_timeout

      iex> error = Foundation.Types.Error.new([
      ...>   code: 3001,
      ...>   error_type: :data_corruption,
      ...>   message: "Data integrity check failed",
      ...>   severity: :critical,
      ...>   context: %{table: "events", checksum: "abc123"}
      ...> ])
      iex> error.severity
      :critical

  ## Raises
  - `KeyError` if required keys are missing
  """
  @spec new(keyword()) :: t()
  def new(fields \\ []) do
    defaults = [
      timestamp: DateTime.utc_now(),
      context: %{},
      recovery_actions: []
    ]

    struct(__MODULE__, Keyword.merge(defaults, fields))
  end
end
</file>

<file path="foundation/types/event.ex">
defmodule Foundation.Types.Event do
  @moduledoc """
  Event data structure for Foundation.

  Events represent actions, state changes, and system events that occur
  during Foundation operation. This is a pure data structure with no behavior.

  While all fields are optional for maximum flexibility, production events 
  typically should have at least an event_type and timestamp.

  See `@type t` for the complete type specification.

  ## Examples

      iex> event = Foundation.Types.Event.new([
      ...>   event_type: :config_updated,
      ...>   event_id: 123,
      ...>   timestamp: System.monotonic_time()
      ...> ])
      iex> event.event_type
      :config_updated

      iex> empty_event = Foundation.Types.Event.new()
      iex> is_nil(empty_event.event_type)
      true
  """

  @typedoc "Unique identifier for an event"
  @type event_id :: pos_integer()

  @typedoc "Correlation identifier for tracking related events"
  @type correlation_id :: String.t()

  @enforce_keys [:event_type, :event_id, :timestamp]
  defstruct [
    :event_type,
    :event_id,
    :timestamp,
    :wall_time,
    :node,
    :pid,
    :correlation_id,
    :parent_id,
    :data
  ]

  @type t :: %__MODULE__{
          event_type: atom() | nil,
          event_id: event_id() | nil,
          timestamp: integer() | nil,
          wall_time: DateTime.t() | nil,
          node: node() | nil,
          pid: pid() | nil,
          correlation_id: correlation_id() | nil,
          parent_id: event_id() | nil,
          data: term() | nil
        }

  @doc """
  Create a new event structure with required fields.

  Creates an event with minimal required fields for enforcement.
  Additional fields can be provided via keyword list.

  ## Examples

      iex> event = Foundation.Types.Event.new()
      iex> event.event_type
      :default

      iex> event = Foundation.Types.Event.new(event_type: :custom)
      iex> event.event_type
      :custom
  """
  @spec new() :: t()
  def new() do
    new([])
  end

  @spec new(keyword()) :: t()
  def new(fields) when is_list(fields) do
    defaults = [
      event_type: :default,
      event_id: System.unique_integer([:positive]),
      timestamp: System.monotonic_time()
    ]

    final_fields = Keyword.merge(defaults, fields)
    struct(__MODULE__, final_fields)
  end

  @doc """
  Create a new event structure with a specific event type.

  Accepts an atom for the event_type and creates an event with 
  default values for required fields.

  ## Parameters
  - `event_type`: An atom representing the event type

  ## Examples

      iex> event = Foundation.Types.Event.new(:process_started)
      iex> event.event_type
      :process_started
  """
  @spec new(atom()) :: t()
  def new(event_type) when is_atom(event_type) do
    new(event_type: event_type)
  end

  @doc """
  Create an empty event structure without enforcement (for testing).

  This function bypasses the @enforce_keys constraint to allow creation
  of events with nil values for testing purposes.

  ## Examples

      iex> event = Foundation.Types.Event.empty()
      iex> is_nil(event.event_type)
      true
  """
  @spec empty() :: %__MODULE__{
          event_type: nil,
          event_id: nil,
          timestamp: nil,
          wall_time: nil,
          node: nil,
          pid: nil,
          correlation_id: nil,
          parent_id: nil,
          data: nil
        }
  def empty() do
    %__MODULE__{
      event_type: nil,
      event_id: nil,
      timestamp: nil,
      wall_time: nil,
      node: nil,
      pid: nil,
      correlation_id: nil,
      parent_id: nil,
      data: nil
    }
  end
end
</file>

<file path="foundation/validation/config_validator.ex">
defmodule Foundation.Validation.ConfigValidator do
  @moduledoc """
  Pure validation functions for configuration structures.

  Contains only validation logic - no side effects, no GenServer calls.
  All functions are pure and easily testable.
  """

  alias Foundation.Types.{Config, Error}

  @doc """
  Validate a complete configuration structure.
  """
  @spec validate(Config.t()) :: :ok | {:error, Error.t()}
  def validate(%Config{} = config) do
    with :ok <- validate_ai_config(config.ai),
         :ok <- validate_capture_config(config.capture),
         :ok <- validate_storage_config(config.storage),
         :ok <- validate_interface_config(config.interface),
         :ok <- validate_dev_config(config.dev),
         :ok <- validate_infrastructure_config(config.infrastructure) do
      :ok
    end
  end

  @doc """
  Validate AI configuration section.
  """
  @spec validate_ai_config(map()) :: :ok | {:error, Error.t()}
  def validate_ai_config(%{provider: provider} = config) do
    with :ok <- validate_provider(provider),
         :ok <- validate_ai_analysis(config.analysis),
         :ok <- validate_ai_planning(config.planning) do
      :ok
    end
  end

  @doc """
  Validate capture configuration section.
  """
  @spec validate_capture_config(map()) :: :ok | {:error, Error.t()}
  def validate_capture_config(%{ring_buffer: rb, processing: proc, vm_tracing: vt}) do
    with :ok <- validate_ring_buffer(rb),
         :ok <- validate_processing(proc),
         :ok <- validate_vm_tracing(vt) do
      :ok
    end
  end

  @doc """
  Validate storage configuration section.
  """
  @spec validate_storage_config(map()) :: :ok | {:error, Error.t()}
  def validate_storage_config(%{hot: hot, warm: warm, cold: cold}) do
    with :ok <- validate_hot_storage(hot),
         :ok <- validate_warm_storage(warm),
         :ok <- validate_cold_storage(cold) do
      :ok
    end
  end

  @doc """
  Validate interface configuration section.
  """
  @spec validate_interface_config(map()) :: :ok | {:error, Error.t()}
  def validate_interface_config(%{
        query_timeout: timeout,
        max_results: max,
        enable_streaming: stream
      })
      when is_integer(timeout) and timeout > 0 and
             is_integer(max) and max > 0 and
             is_boolean(stream) do
    :ok
  end

  def validate_interface_config(_) do
    create_validation_error("Invalid interface configuration")
  end

  @doc """
  Validate development configuration section.
  """
  @spec validate_dev_config(map()) :: :ok | {:error, Error.t()}
  def validate_dev_config(%{
        debug_mode: debug,
        verbose_logging: verbose,
        performance_monitoring: perf
      })
      when is_boolean(debug) and is_boolean(verbose) and is_boolean(perf) do
    :ok
  end

  @spec validate_dev_config(map()) :: {:error, Error.t()}
  def validate_dev_config(_) do
    create_validation_error("Invalid development configuration")
  end

  @doc """
  Validate infrastructure configuration section.
  """
  @spec validate_infrastructure_config(map()) :: :ok | {:error, Error.t()}
  def validate_infrastructure_config(%{
        rate_limiting: rate_limiting,
        circuit_breaker: circuit_breaker,
        connection_pool: connection_pool
      }) do
    with :ok <- validate_rate_limiting_config(rate_limiting),
         :ok <- validate_circuit_breaker_config(circuit_breaker),
         :ok <- validate_connection_pool_config(connection_pool) do
      :ok
    end
  end

  def validate_infrastructure_config(_) do
    create_validation_error("Invalid infrastructure configuration")
  end

  ## Private Validation Functions

  defp validate_provider(provider) do
    valid_providers = [:mock, :openai, :anthropic, :gemini]

    if provider in valid_providers do
      :ok
    else
      create_error(
        :invalid_config_value,
        "Invalid AI provider",
        %{provider: provider, valid_providers: valid_providers}
      )
    end
  end

  defp validate_ai_analysis(%{max_file_size: size, timeout: timeout, cache_ttl: ttl})
       when is_integer(size) and size > 0 and
              is_integer(timeout) and timeout > 0 and
              is_integer(ttl) and ttl > 0 do
    :ok
  end

  defp validate_ai_analysis(_) do
    create_validation_error("Invalid AI analysis configuration")
  end

  defp validate_ai_planning(%{
         performance_target: target,
         sampling_rate: rate,
         default_strategy: strategy
       }) do
    valid_strategies = [:fast, :balanced, :thorough]

    cond do
      not is_number(target) or target < 0 ->
        create_error(:constraint_violation, "Performance target must be a non-negative number")

      not is_number(rate) or rate < 0 or rate > 1 ->
        create_error(:range_error, "Sampling rate must be between 0 and 1")

      strategy not in valid_strategies ->
        create_error(:invalid_config_value, "Invalid planning strategy")

      true ->
        :ok
    end
  end

  defp validate_ai_planning(_) do
    create_validation_error("Invalid AI planning configuration")
  end

  defp validate_ring_buffer(%{size: size, max_events: max, overflow_strategy: strategy})
       when is_integer(size) and size > 0 and
              is_integer(max) and max > 0 do
    valid_strategies = [:drop_oldest, :drop_newest, :block]

    if strategy in valid_strategies do
      :ok
    else
      create_error(:invalid_config_value, "Invalid overflow strategy")
    end
  end

  defp validate_ring_buffer(_) do
    create_validation_error("Invalid ring buffer configuration")
  end

  defp validate_processing(%{batch_size: batch, flush_interval: flush, max_queue_size: queue})
       when is_integer(batch) and batch > 0 and
              is_integer(flush) and flush > 0 and
              is_integer(queue) and queue > 0 do
    :ok
  end

  defp validate_processing(_) do
    create_validation_error("Invalid processing configuration")
  end

  defp validate_vm_tracing(%{
         enable_spawn_trace: spawn,
         enable_exit_trace: exit,
         enable_message_trace: msg,
         trace_children: children
       })
       when is_boolean(spawn) and is_boolean(exit) and
              is_boolean(msg) and is_boolean(children) do
    :ok
  end

  defp validate_vm_tracing(_) do
    create_validation_error("Invalid VM tracing configuration")
  end

  defp validate_hot_storage(%{max_events: max, max_age_seconds: age, prune_interval: interval})
       when is_integer(max) and max > 0 and
              is_integer(age) and age > 0 and
              is_integer(interval) and interval > 0 do
    :ok
  end

  defp validate_hot_storage(_) do
    create_validation_error("Invalid hot storage configuration")
  end

  defp validate_warm_storage(%{enable: false}), do: :ok

  defp validate_warm_storage(%{enable: true, path: path, max_size_mb: size, compression: comp})
       when is_binary(path) and is_integer(size) and size > 0 do
    valid_compression = [:none, :gzip, :zstd]

    if comp in valid_compression do
      :ok
    else
      create_error(:invalid_config_value, "Invalid compression type")
    end
  end

  defp validate_warm_storage(_) do
    create_validation_error("Invalid warm storage configuration")
  end

  defp validate_cold_storage(%{enable: false}), do: :ok

  defp validate_cold_storage(_) do
    create_validation_error("Invalid cold storage configuration")
  end

  ## Error Creation Helpers

  @spec create_validation_error(String.t()) :: {:error, Error.t()}
  defp create_validation_error(message) do
    create_error(:validation_failed, message)
  end

  # Infrastructure validation helpers
  defp validate_rate_limiting_config(%{
         default_rules: rules,
         enabled: enabled,
         cleanup_interval: interval
       })
       when is_map(rules) and is_boolean(enabled) and is_integer(interval) and interval > 0 do
    validate_rate_limiting_rules(rules)
  end

  defp validate_rate_limiting_config(_) do
    create_validation_error("Invalid rate limiting configuration")
  end

  defp validate_rate_limiting_rules(rules) when is_map(rules) do
    Enum.reduce_while(rules, :ok, fn {rule_name, rule_config}, _acc ->
      case validate_rate_limiting_rule(rule_name, rule_config) do
        :ok -> {:cont, :ok}
        error -> {:halt, error}
      end
    end)
  end

  defp validate_rate_limiting_rule(rule_name, %{scale: scale, limit: limit})
       when is_atom(rule_name) and is_integer(scale) and scale > 0 and
              is_integer(limit) and limit > 0 do
    :ok
  end

  defp validate_rate_limiting_rule(rule_name, _) do
    create_validation_error("Invalid rate limiting rule: #{inspect(rule_name)}")
  end

  defp validate_circuit_breaker_config(%{
         default_config: config,
         enabled: enabled
       })
       when is_map(config) and is_boolean(enabled) do
    validate_circuit_breaker_default_config(config)
  end

  defp validate_circuit_breaker_config(_) do
    create_validation_error("Invalid circuit breaker configuration")
  end

  defp validate_circuit_breaker_default_config(%{
         failure_threshold: threshold,
         recovery_time: recovery,
         call_timeout: timeout
       })
       when is_integer(threshold) and threshold > 0 and
              is_integer(recovery) and recovery > 0 and
              is_integer(timeout) and timeout > 0 do
    :ok
  end

  defp validate_circuit_breaker_default_config(_) do
    create_validation_error("Invalid circuit breaker default configuration")
  end

  defp validate_connection_pool_config(%{
         default_config: config,
         enabled: enabled
       })
       when is_map(config) and is_boolean(enabled) do
    validate_connection_pool_default_config(config)
  end

  defp validate_connection_pool_config(_) do
    create_validation_error("Invalid connection pool configuration")
  end

  defp validate_connection_pool_default_config(%{
         size: size,
         max_overflow: overflow,
         strategy: strategy
       })
       when is_integer(size) and size > 0 and
              is_integer(overflow) and overflow >= 0 and
              strategy in [:lifo, :fifo] do
    :ok
  end

  defp validate_connection_pool_default_config(_) do
    create_validation_error("Invalid connection pool default configuration")
  end

  @spec create_error(atom(), String.t(), map()) :: {:error, Error.t()}
  defp create_error(error_type, message, context \\ %{}) do
    error =
      Error.new(
        code: error_code_for_type(error_type),
        error_type: error_type,
        message: message,
        severity: severity_for_type(error_type),
        context: context,
        category: :config,
        subcategory: :validation
      )

    {:error, error}
  end

  @spec error_code_for_type(
          :validation_failed
          | :invalid_config_value
          | :constraint_violation
          | :range_error
        ) :: 1001 | 1002 | 1003 | 1004
  defp error_code_for_type(:validation_failed), do: 1001
  defp error_code_for_type(:invalid_config_value), do: 1002
  defp error_code_for_type(:constraint_violation), do: 1003
  defp error_code_for_type(:range_error), do: 1004

  @spec severity_for_type(
          :validation_failed
          | :invalid_config_value
          | :constraint_violation
          | :range_error
        ) :: Error.error_severity()
  defp severity_for_type(:constraint_violation), do: :high
  defp severity_for_type(:range_error), do: :high
  defp severity_for_type(_), do: :medium
end
</file>

<file path="foundation/validation/event_validator.ex">
defmodule Foundation.Validation.EventValidator do
  @moduledoc """
  Pure validation functions for event structures.

  Contains only validation logic - no side effects, no business logic.
  All functions are pure and easily testable.

  This module validates Event structs to ensure they contain valid data
  before storage or processing.

  ## Examples

      iex> event = Foundation.Types.Event.new([
      ...>   event_id: 123,
      ...>   event_type: :function_entry,
      ...>   timestamp: System.monotonic_time()
      ...> ])
      iex> Foundation.Validation.EventValidator.validate(event)
      :ok
  """

  alias Foundation.Types.{Event, Error}

  @typedoc "Maximum allowed size for event data in bytes"
  @type max_data_size :: 1_000_000

  @doc """
  Validate an event structure.

  Performs comprehensive validation including required fields, types, and data size.

  ## Parameters
  - `event`: The Event struct to validate

  ## Examples

      iex> valid_event = Event.new([event_id: 1, event_type: :test, timestamp: 123])
      iex> EventValidator.validate(valid_event)
      :ok

      iex> invalid_event = Event.new([event_id: nil, event_type: :test])
      iex> EventValidator.validate(invalid_event)
      {:error, %Error{error_type: :validation_failed}}
  """
  @spec validate(Event.t()) :: :ok | {:error, Error.t()}
  def validate(%Event{} = event) do
    with :ok <- validate_required_fields(event),
         :ok <- validate_field_types(event),
         :ok <- validate_data_size(event) do
      :ok
    end
  end

  @doc """
  Validate that an event has all required fields.

  Checks that critical fields like event_id, event_type, and timestamp are present.
  """
  @spec validate_required_fields(Event.t()) :: :ok | {:error, Error.t()}
  def validate_required_fields(%Event{} = event) do
    cond do
      is_nil(event.event_id) ->
        create_error(:validation_failed, "Event ID cannot be nil")

      is_nil(event.event_type) ->
        create_error(:validation_failed, "Event type cannot be nil")

      is_nil(event.timestamp) ->
        create_error(:validation_failed, "Timestamp cannot be nil")

      true ->
        :ok
    end
  end

  @doc """
  Validate event field types.

  Ensures all fields have the correct data types when present.
  """
  @spec validate_field_types(Event.t()) :: :ok | {:error, Error.t()}
  def validate_field_types(%Event{} = event) do
    cond do
      event.event_id && (not is_integer(event.event_id) or event.event_id <= 0) ->
        create_error(:type_mismatch, "Event ID must be a positive integer")

      event.event_type && not is_atom(event.event_type) ->
        create_error(:type_mismatch, "Event type must be an atom")

      event.timestamp && not is_integer(event.timestamp) ->
        create_error(:type_mismatch, "Timestamp must be an integer")

      event.wall_time && not is_struct(event.wall_time, DateTime) ->
        create_error(:type_mismatch, "Wall time must be a DateTime")

      event.node && not is_atom(event.node) ->
        create_error(:type_mismatch, "Node must be an atom")

      event.pid && not is_pid(event.pid) ->
        create_error(:type_mismatch, "PID must be a process identifier")

      event.correlation_id && not is_binary(event.correlation_id) ->
        create_error(:type_mismatch, "Correlation ID must be a string")

      event.parent_id && (not is_integer(event.parent_id) or event.parent_id <= 0) ->
        create_error(:type_mismatch, "Parent ID must be a positive integer")

      true ->
        :ok
    end
  end

  @doc """
  Validate event data size to prevent memory issues.

  Checks that the event data doesn't exceed the maximum allowed size.
  """
  @spec validate_data_size(Event.t()) :: :ok | {:error, Error.t()}
  def validate_data_size(%Event{data: data}) do
    # Check if data is too large (prevent memory issues)
    size = estimate_size(data)
    max_size = 1_000_000

    if size > max_size do
      create_error(
        :data_too_large,
        "Event data too large",
        %{size: size, max_size: max_size}
      )
    else
      :ok
    end
  end

  @doc """
  Validate event type is allowed.

  Checks that the event type is one of the predefined valid types.

  ## Parameters
  - `event_type`: Atom representing the event type

  ## Examples

      iex> EventValidator.validate_event_type(:function_entry)
      :ok

      iex> EventValidator.validate_event_type(:invalid_type)
      {:error, %Error{error_type: :invalid_event_type}}
  """
  @spec validate_event_type(atom()) :: :ok | {:error, Error.t()}
  def validate_event_type(event_type) when is_atom(event_type) do
    # Define allowed event types
    allowed_types = [
      :function_entry,
      :function_exit,
      :state_change,
      :message_send,
      :message_receive,
      :spawn,
      :exit,
      :link,
      :unlink,
      :monitor,
      :demonitor,
      :system_event,
      :custom_event,
      :config_updated,
      :config_reset,
      :test,
      :test1,
      :test2,
      :test3,
      :default,
      :type_a,
      :type_b
    ]

    if event_type in allowed_types do
      :ok
    else
      create_error(
        :invalid_event_type,
        "Invalid event type",
        %{event_type: event_type, allowed_types: allowed_types}
      )
    end
  end

  def validate_event_type(_) do
    create_error(:type_mismatch, "Event type must be an atom")
  end

  ## Private Functions

  @spec estimate_size(term()) :: non_neg_integer()
  defp estimate_size(data) do
    try do
      :erlang.external_size(data)
    rescue
      _ -> 0
    end
  end

  @spec create_error(atom(), String.t(), map()) :: {:error, Error.t()}
  defp create_error(error_type, message, context \\ %{}) do
    error =
      Error.new(
        code: error_code_for_type(error_type),
        error_type: error_type,
        message: message,
        severity: severity_for_type(error_type),
        context: context,
        category: :data,
        subcategory: :validation
      )

    {:error, error}
  end

  @spec error_code_for_type(
          :validation_failed
          | :type_mismatch
          | :data_too_large
          | :invalid_event_type
        ) :: 2001 | 2002 | 2003 | 2004
  defp error_code_for_type(:validation_failed), do: 2001
  defp error_code_for_type(:type_mismatch), do: 2002
  defp error_code_for_type(:data_too_large), do: 2003
  defp error_code_for_type(:invalid_event_type), do: 2004

  @spec severity_for_type(
          :validation_failed
          | :type_mismatch
          | :data_too_large
          | :invalid_event_type
        ) :: Error.error_severity()
  defp severity_for_type(:data_too_large), do: :high
  defp severity_for_type(:validation_failed), do: :high
  defp severity_for_type(_), do: :medium
end
</file>

<file path="foundation/application.ex">
defmodule Foundation.Application do
  @moduledoc """
  Foundation Application Supervisor
  Manages the lifecycle of all Foundation components in a supervised manner.
  The supervision tree is designed to be fault-tolerant and to restart
  components in the correct order if failures occur.
  """
  use Application
  require Logger

  @impl true
  def start(_type, _args) do
    Logger.info("Starting Foundation application...")

    base_children = [
      # Foundation Layer Services
      # Registry must start first for service discovery
      {Foundation.ProcessRegistry, []},

      # Core foundation services with production namespace
      {Foundation.Services.ConfigServer, [namespace: :production]},
      {Foundation.Services.EventStore, [namespace: :production]},
      {Foundation.Services.TelemetryService, [namespace: :production]},

      # Infrastructure protection components
      {Foundation.Infrastructure.ConnectionManager, []},
      {Foundation.Infrastructure.RateLimiter.HammerBackend, []},

      # Task supervisor for dynamic tasks
      {Task.Supervisor, name: Foundation.TaskSupervisor}

      # Future layers will be added here:
      # Layer 1: Core capture pipeline will be added here
      # {Foundation.Capture.PipelineManager, []},
      # Layer 2: Storage and correlation will be added here
      # {Foundation.Storage.QueryCoordinator, []},
      # Layer 4: AI components will be added here
      # {Foundation.AI.Orchestrator, []},
    ]

    children = base_children ++ test_children()

    opts = [strategy: :one_for_one, name: Foundation.Supervisor]

    case Supervisor.start_link(children, opts) do
      {:ok, pid} ->
        Logger.info("Foundation application started successfully")
        {:ok, pid}

      {:error, reason} ->
        Logger.error("Failed to start Foundation application: #{inspect(reason)}")
        {:error, reason}
    end
  end

  @impl true
  def stop(_state) do
    Logger.info("Stopping Foundation application...")
    :ok
  end

  # Private function to add test-specific children
  defp test_children do
    if Application.get_env(:foundation, :test_mode, false) do
      [{Foundation.TestSupport.TestSupervisor, []}]
    else
      []
    end
  end
end
</file>

<file path="foundation/config.ex">
defmodule Foundation.Config do
  @moduledoc """
  Public API for configuration management.

  Thin wrapper around ConfigServer that provides a clean, documented interface.
  All business logic is delegated to the service layer.
  """

  @behaviour Foundation.Contracts.Configurable

  alias Foundation.Services.ConfigServer
  alias Foundation.Types.Config
  alias Foundation.Error

  @type config_path :: [atom()]
  @type config_value :: term()

  @forbidden_update_paths [
    [:ai, :api_key],
    [:storage, :encryption_key],
    [:security],
    [:system, :node_name]
  ]

  @doc """
  Initialize the configuration service.

  ## Examples

      iex> Foundation.Config.initialize()
      :ok
  """
  @spec initialize() :: :ok | {:error, Error.t()}
  def initialize() do
    ConfigServer.initialize()
  end

  @doc """
  Initialize the configuration service with options.

  ## Examples

      iex> Foundation.Config.initialize(cache_size: 1000)
      :ok
  """
  @spec initialize(keyword()) :: :ok | {:error, Error.t()}
  def initialize(opts) when is_list(opts) do
    ConfigServer.initialize(opts)
  end

  @doc """
  Get configuration service status.

  ## Examples

      iex> Foundation.Config.status()
      {:ok, %{status: :running, uptime: 12345}}
  """
  @spec status() :: {:ok, map()} | {:error, Error.t()}
  def status() do
    ConfigServer.status()
  end

  @doc """
  Get the complete configuration.

  ## Examples

      iex> Foundation.Config.get()
      {:ok, %Config{...}}
  """
  @spec get() :: {:ok, Config.t()} | {:error, Error.t()}
  defdelegate get(), to: ConfigServer

  @doc """
  Get a configuration value by path.

  ## Examples

      iex> Foundation.Config.get([:ai, :provider])
      {:ok, :openai}

      iex> Foundation.Config.get([:nonexistent, :path])
      {:error, %Error{error_type: :config_path_not_found}}
  """
  @spec get(config_path()) :: {:ok, config_value()} | {:error, Error.t()}
  defdelegate get(path), to: ConfigServer

  @doc """
  Update a configuration value at the given path.

  Only paths returned by `updatable_paths/0` can be updated at runtime.

  ## Examples

      iex> Foundation.Config.update([:dev, :debug_mode], true)
      :ok

      iex> Foundation.Config.update([:ai, :provider], :anthropic)
      {:error, %Error{error_type: :config_update_forbidden}}
  """
  @spec update(config_path(), config_value()) :: :ok | {:error, Error.t()}
  defdelegate update(path, value), to: ConfigServer

  @doc """
  Validate a configuration structure.

  ## Examples

      iex> config = %Config{ai: %{provider: :invalid}}
      iex> Foundation.Config.validate(config)
      {:error, %Error{error_type: :invalid_config_value}}
  """
  @spec validate(Config.t()) :: :ok | {:error, Error.t()}
  defdelegate validate(config), to: ConfigServer

  @doc """
  Get the list of paths that can be updated at runtime.

  ## Examples

      iex> Foundation.Config.updatable_paths()
      [
        [:ai, :planning, :sampling_rate],
        [:dev, :debug_mode],
        ...
      ]
  """
  @spec updatable_paths() :: [[atom(), ...], ...]
  defdelegate updatable_paths(), to: ConfigServer

  @doc """
  Reset configuration to defaults.

  ## Examples

      iex> Foundation.Config.reset()
      :ok
  """
  @spec reset() :: :ok | {:error, Error.t()}
  defdelegate reset(), to: ConfigServer

  @doc """
  Check if the configuration service is available.

  ## Examples

      iex> Foundation.Config.available?()
      true
  """
  @spec available?() :: boolean()
  defdelegate available?(), to: ConfigServer

  @doc """
  Subscribe to configuration change notifications.

  The calling process will receive messages of the form:
  `{:config_notification, {:config_updated, path, new_value}}`

  ## Examples

      iex> Foundation.Config.subscribe()
      :ok
  """
  @spec subscribe() :: :ok | {:error, Error.t()}
  def subscribe do
    ConfigServer.subscribe()
  end

  @doc """
  Unsubscribe from configuration change notifications.

  ## Examples

      iex> Foundation.Config.unsubscribe()
      :ok
  """
  @spec unsubscribe() :: :ok
  def unsubscribe do
    ConfigServer.unsubscribe()
  end

  @doc """
  Get configuration with a default value if path doesn't exist.

  ## Examples

      iex> Foundation.Config.get_with_default([:ai, :timeout], 30_000)
      30_000
  """
  @spec get_with_default(config_path(), config_value()) :: config_value()
  # Dialyzer warning suppressed: Config.get/1 may never fail in current context,
  # but this graceful fallback pattern is intentional for robustness
  @dialyzer {:nowarn_function, get_with_default: 2}
  def get_with_default(path, default) do
    case get(path) do
      {:ok, value} -> value
      {:error, _} -> default
    end
  end

  @doc """
  Update configuration if the path is updatable, otherwise return error.

  Convenience function that checks updatable paths before attempting update.

  ## Examples

      iex> Foundation.Config.safe_update([:dev, :debug_mode], true)
      :ok
  """
  @spec safe_update(config_path(), config_value()) :: :ok | {:error, Error.t()}
  def safe_update(path, value) do
    cond do
      not is_list(path) ->
        {:error, Error.new(:invalid_path, "Invalid configuration path")}

      path in @forbidden_update_paths ->
        {:error, Error.new(:config_update_forbidden, "Configuration path update forbidden")}

      true ->
        update(path, value)
    end
  end
end
</file>

<file path="foundation/error_context.ex">
defmodule Foundation.ErrorContext do
  @moduledoc """
  Enhanced error context system with proper propagation and debugging support.

  Phase 1 Implementation:
  - Nested context support with breadcrumbs
  - Operation tracking and correlation
  - Emergency context recovery
  - Enhanced error propagation patterns
  """

  alias Foundation.{Error, Utils}

  @type t :: %__MODULE__{
          operation_id: pos_integer(),
          module: module(),
          function: atom(),
          correlation_id: String.t(),
          start_time: integer(),
          metadata: map(),
          breadcrumbs: [breadcrumb()],
          parent_context: t() | nil
        }

  @type context :: t()

  @type breadcrumb :: %{
          module: module(),
          function: atom(),
          timestamp: integer(),
          metadata: map()
        }

  @enforce_keys [:operation_id, :module, :function, :correlation_id, :start_time]
  defstruct [
    # Unique ID for this operation
    :operation_id,
    # Module where operation started
    :module,
    # Function where operation started
    :function,
    # Cross-system correlation
    :correlation_id,
    # When operation began
    :start_time,
    # Additional context data
    metadata: %{},
    # Operation trail
    breadcrumbs: [],
    # Nested context support
    parent_context: nil
  ]

  ## Context Creation and Management

  @doc """
  Create a new error context for an operation.

  ## Parameters
  - `module`: The module where the operation starts
  - `function`: The function where the operation starts
  - `opts`: Options including correlation_id, metadata, parent_context

  ## Examples

      iex> ErrorContext.new(MyModule, :my_function)
      %ErrorContext{module: MyModule, function: :my_function, ...}
  """
  @spec new(module(), atom(), keyword()) :: t()
  def new(module, function, opts \\ []) do
    %__MODULE__{
      operation_id: Utils.generate_id(),
      module: module,
      function: function,
      correlation_id: Keyword.get(opts, :correlation_id, Utils.generate_correlation_id()),
      start_time: Utils.monotonic_timestamp(),
      metadata: Keyword.get(opts, :metadata, %{}),
      breadcrumbs: [
        %{
          module: module,
          function: function,
          timestamp: Utils.monotonic_timestamp(),
          metadata: %{}
        }
      ],
      parent_context: Keyword.get(opts, :parent_context)
    }
  end

  @doc """
  Create a child context inheriting from a parent context.

  ## Parameters
  - `parent`: The parent context
  - `module`: The module for the child operation
  - `function`: The function for the child operation
  - `metadata`: Additional metadata for the child context

  ## Examples

      iex> child = ErrorContext.child_context(parent, ChildModule, :child_function)
      %ErrorContext{parent_context: ^parent, ...}
  """
  @spec child_context(t(), module(), atom(), map()) :: t()
  def child_context(%__MODULE__{} = parent, module, function, metadata \\ %{}) do
    %__MODULE__{
      operation_id: Utils.generate_id(),
      module: module,
      function: function,
      correlation_id: parent.correlation_id,
      start_time: Utils.monotonic_timestamp(),
      metadata: Map.merge(parent.metadata, metadata),
      breadcrumbs:
        parent.breadcrumbs ++
          [
            %{
              module: module,
              function: function,
              timestamp: Utils.monotonic_timestamp(),
              metadata: metadata
            }
          ],
      parent_context: parent
    }
  end

  @doc """
  Add a breadcrumb to track operation flow.

  ## Parameters
  - `context`: The context to add breadcrumb to
  - `module`: Module name for the breadcrumb
  - `function`: Function name for the breadcrumb
  - `metadata`: Additional metadata for this step
  """
  @spec add_breadcrumb(t(), module(), atom(), map()) :: t()
  def add_breadcrumb(%__MODULE__{} = context, module, function, metadata \\ %{}) do
    breadcrumb = %{
      module: module,
      function: function,
      timestamp: Utils.monotonic_timestamp(),
      metadata: metadata
    }

    %{context | breadcrumbs: context.breadcrumbs ++ [breadcrumb]}
  end

  @doc """
  Add metadata to an existing context.

  ## Parameters
  - `context`: The context to add metadata to
  - `new_metadata`: Map of metadata to merge
  """
  @spec add_metadata(t(), map()) :: t()
  def add_metadata(%__MODULE__{} = context, new_metadata) when is_map(new_metadata) do
    %{context | metadata: Map.merge(context.metadata, new_metadata)}
  end

  ## Error Context Integration

  @doc """
  Execute a function with error context tracking.

  Automatically captures exceptions and enhances them with context information.

  ## Parameters
  - `context`: The context to use for the operation
  - `fun`: Zero-arity function to execute

  ## Returns
  - The result of the function, or {:error, enhanced_error} on exception
  """
  @spec with_context(t(), (-> term())) :: term() | {:error, Error.t()}
  def with_context(%__MODULE__{} = context, fun) when is_function(fun, 0) do
    # Store context in process dictionary for emergency access
    Process.put(:error_context, context)

    try do
      result = fun.()

      # Clean up and enhance successful results with context
      Process.delete(:error_context)
      enhance_result_with_context(result, context)
    rescue
      exception ->
        # Capture and enhance exception with full context
        enhanced_error = create_exception_error(exception, context, __STACKTRACE__)

        # Clean up
        Process.delete(:error_context)

        # Emit error telemetry
        Error.collect_error_metrics(enhanced_error)

        {:error, enhanced_error}
    end
  end

  @doc """
  Enhance an Error struct with additional context information.

  ## Parameters
  - `error`: The error to enhance
  - `context`: The context to add to the error
  """
  @spec enhance_error(Error.t(), t()) :: Error.t()
  def enhance_error(%Error{} = error, %__MODULE__{} = context) do
    # Enhance existing error with additional context
    enhanced_context =
      Map.merge(error.context, %{
        operation_context: %{
          operation_id: context.operation_id,
          correlation_id: context.correlation_id,
          breadcrumbs: context.breadcrumbs,
          duration_ns: Utils.monotonic_timestamp() - context.start_time,
          metadata: context.metadata
        }
      })

    %{
      error
      | context: enhanced_context,
        correlation_id: error.correlation_id || context.correlation_id
    }
  end

  @spec enhance_error({:error, Error.t()}, t()) :: {:error, Error.t()}
  def enhance_error({:error, %Error{} = error}, %__MODULE__{} = context) do
    {:error, enhance_error(error, context)}
  end

  @spec enhance_error({:error, term()}, t()) :: {:error, Error.t()}
  def enhance_error({:error, reason}, %__MODULE__{} = context) do
    # Convert raw error to structured error with context
    error =
      Error.new(:external_error, "External operation failed",
        context: %{
          original_reason: reason,
          operation_context: %{
            operation_id: context.operation_id,
            correlation_id: context.correlation_id,
            breadcrumbs: context.breadcrumbs,
            duration_ns: Utils.monotonic_timestamp() - context.start_time
          }
        },
        correlation_id: context.correlation_id
      )

    {:error, error}
  end

  @spec enhance_error(term(), t()) :: term()
  def enhance_error(result, _context), do: result

  ## Context Recovery and Debugging

  @doc """
  Get the current error context from the process dictionary.

  This is an emergency recovery mechanism for debugging.
  """
  @spec get_current_context() :: t() | nil
  def get_current_context do
    # Emergency context retrieval from process dictionary
    Process.get(:error_context)
  end

  @doc """
  Format breadcrumbs as a human-readable string.

  ## Parameters
  - `context`: The context containing breadcrumbs to format
  """
  @spec format_breadcrumbs(t()) :: String.t()
  def format_breadcrumbs(%__MODULE__{breadcrumbs: breadcrumbs}) do
    Enum.map_join(breadcrumbs, " -> ", fn %{module: mod, function: func, timestamp: ts} ->
      relative_time = Utils.monotonic_timestamp() - ts
      "#{mod}.#{func} (#{Utils.format_duration(relative_time)} ago)"
    end)
  end

  @doc """
  Get the duration of an operation in nanoseconds.

  ## Parameters
  - `context`: The context to calculate duration for
  """
  @spec get_operation_duration(t()) :: integer()
  def get_operation_duration(%__MODULE__{start_time: start_time}) do
    Utils.monotonic_timestamp() - start_time
  end

  ## Enhanced Error Context Integration

  @doc """
  Add context to an existing error or create a new one.
  Enhanced version with better error chaining and context preservation.

  ## Parameters
  - `result`: The result to potentially enhance with context
  - `context`: The context to add
  - `additional_info`: Additional context information
  """
  @spec add_context(term(), t() | map(), map()) :: term()
  def add_context(result, context, additional_info \\ %{})

  @spec add_context(:ok, t() | map(), map()) :: :ok
  def add_context(:ok, _context, _additional_info), do: :ok

  @spec add_context({:ok, term()}, t() | map(), map()) :: {:ok, term()}
  def add_context({:ok, _} = success, _context, _additional_info), do: success

  @spec add_context({:error, Error.t()}, t(), map()) :: {:error, Error.t()}
  def add_context({:error, %Error{} = error}, %__MODULE__{} = context, additional_info) do
    enhanced_error = enhance_error(error, context)
    additional_context = Map.merge(enhanced_error.context, additional_info)
    {:error, %{enhanced_error | context: additional_context}}
  end

  @spec add_context({:error, Error.t()}, map(), map()) :: {:error, Error.t()}
  def add_context({:error, %Error{} = error}, context, additional_info) when is_map(context) do
    # Handle legacy map-based context
    updated_context = Map.merge(error.context, Map.merge(context, additional_info))
    {:error, %{error | context: updated_context}}
  end

  @spec add_context({:error, term()}, t(), map()) :: {:error, Error.t()}
  def add_context({:error, reason}, %__MODULE__{} = context, additional_info) do
    full_context =
      Map.merge(additional_info, %{
        original_reason: reason,
        operation_context: %{
          operation_id: context.operation_id,
          correlation_id: context.correlation_id,
          breadcrumbs: context.breadcrumbs,
          duration_ns: get_operation_duration(context),
          metadata: context.metadata
        }
      })

    {:error, Error.new(:external_error, "External operation failed", context: full_context)}
  end

  @spec add_context({:error, term()}, map(), map()) :: {:error, Error.t()}
  def add_context({:error, reason}, context, additional_info) when is_map(context) do
    # Handle legacy map-based context
    full_context = Map.merge(context, Map.merge(additional_info, %{original_reason: reason}))
    {:error, Error.new(:external_error, "External operation failed", context: full_context)}
  end

  ## Private Helpers

  defp enhance_result_with_context(result, context) do
    # For successful results, we might want to add telemetry
    duration = get_operation_duration(context)

    Foundation.Telemetry.emit_gauge(
      [:foundation, :operations, :duration],
      duration,
      %{
        module: context.module,
        function: context.function,
        correlation_id: context.correlation_id
      }
    )

    result
  end

  defp create_exception_error(exception, context, stacktrace) do
    Error.new(:internal_error, "Exception in operation: #{Exception.message(exception)}",
      context: %{
        exception_type: exception.__struct__,
        exception_message: Exception.message(exception),
        operation_context: %{
          operation_id: context.operation_id,
          correlation_id: context.correlation_id,
          breadcrumbs: context.breadcrumbs,
          duration_ns: get_operation_duration(context),
          metadata: context.metadata
        }
      },
      correlation_id: context.correlation_id,
      stacktrace: format_stacktrace(stacktrace)
    )
  end

  defp format_stacktrace(stacktrace) do
    stacktrace
    # Limit depth
    |> Enum.take(10)
    |> Enum.map(fn
      {module, function, arity, location} ->
        %{
          module: module,
          function: function,
          arity: arity,
          file: Keyword.get(location, :file),
          line: Keyword.get(location, :line)
        }

      entry ->
        %{raw: inspect(entry)}
    end)
  end
end
</file>

<file path="foundation/error.ex">
defmodule Foundation.Error do
  @moduledoc """
  Enhanced error handling with hierarchical error codes and comprehensive error management.

  Phase 1 Implementation:
  - Hierarchical error code system (C000-C999, S000-S999, etc.)
  - Enhanced error context and recovery strategies
  - Standardized error propagation patterns
  - Error metrics and analysis capabilities

  All errors must have a code, error_type, message, and severity.
  Other fields provide additional context and are optional.

  See `@type t` for the complete type specification.

  ## Examples

      iex> error = Foundation.Error.new(:config_not_found, "Config missing")
      iex> error.error_type
      :config_not_found

      iex> error = Foundation.Error.new(:network_error, nil, context: %{url: "http://example.com"})
      iex> is_map(error.context)
      true
  """

  @typedoc "Specific error type identifier"
  @type error_code :: atom()

  @typedoc "Additional context information for the error"
  @type error_context :: map()

  @typedoc "Stack trace information as a list of maps"
  @type stacktrace_info :: [map()]

  @typedoc "High-level error category"
  @type error_category :: :config | :system | :data | :external

  @typedoc "Specific error subcategory within a category"
  @type error_subcategory :: :structure | :validation | :access | :runtime

  @typedoc "Error severity level"
  @type error_severity :: :low | :medium | :high | :critical

  @typedoc "Strategy for retrying failed operations"
  @type retry_strategy :: :no_retry | :immediate | :fixed_delay | :exponential_backoff

  @enforce_keys [:code, :error_type, :message, :severity]
  defstruct [
    :code,
    :error_type,
    :message,
    :severity,
    :context,
    :correlation_id,
    :timestamp,
    :stacktrace,
    :category,
    :subcategory,
    :retry_strategy,
    :recovery_actions
  ]

  @type t :: %__MODULE__{
          code: pos_integer(),
          error_type: error_code(),
          message: String.t(),
          severity: error_severity(),
          context: error_context() | nil,
          correlation_id: String.t() | nil,
          timestamp: DateTime.t() | nil,
          stacktrace: stacktrace_info() | nil,
          category: error_category() | nil,
          subcategory: error_subcategory() | nil,
          retry_strategy: retry_strategy() | nil,
          recovery_actions: [String.t()] | nil
        }

  @error_definitions %{
    # Configuration Errors
    {:config, :structure, :invalid_config_structure} =>
      {1101, :high, "Configuration structure is invalid"},
    {:config, :structure, :missing_required_field} =>
      {1102, :high, "Required configuration field missing"},
    {:config, :validation, :invalid_config_value} =>
      {1201, :medium, "Configuration value failed validation"},
    {:config, :validation, :constraint_violation} =>
      {1202, :medium, "Configuration constraint violated"},
    {:config, :validation, :range_error} => {1203, :low, "Value outside acceptable range"},
    {:config, :access, :config_not_found} => {1301, :high, "Configuration not found"},
    {:config, :runtime, :config_update_forbidden} =>
      {1401, :medium, "Configuration update not allowed"},

    # System Errors
    {:system, :initialization, :initialization_failed} =>
      {2101, :critical, "System initialization failed"},
    {:system, :initialization, :service_unavailable} =>
      {2102, :high, "Required service unavailable"},
    {:system, :resources, :resource_exhausted} => {2201, :high, "System resources exhausted"},
    {:system, :dependencies, :dependency_failed} => {2301, :high, "Required dependency failed"},
    {:system, :runtime, :internal_error} => {2401, :critical, "Internal system error"},

    # Data Errors
    {:data, :serialization, :serialization_failed} => {3101, :medium, "Data serialization failed"},
    {:data, :serialization, :deserialization_failed} =>
      {3102, :medium, "Data deserialization failed"},
    {:data, :validation, :type_mismatch} => {3201, :low, "Data type mismatch"},
    {:data, :validation, :format_error} => {3202, :low, "Data format error"},
    {:data, :corruption, :data_corruption} => {3301, :critical, "Data corruption detected"},
    {:data, :not_found, :data_not_found} => {3401, :low, "Requested data not found"},

    # External Errors
    {:external, :network, :network_error} => {4101, :medium, "Network communication error"},
    {:external, :service, :external_service_error} => {4201, :medium, "External service error"},
    {:external, :timeout, :timeout} => {4301, :medium, "Operation timeout"},
    {:external, :auth, :authentication_failed} => {4401, :high, "Authentication failed"},

    # Validation-specific errors
    {:data, :validation, :validation_failed} => {3203, :medium, "Data validation failed"},
    {:data, :validation, :invalid_input} => {3204, :low, "Invalid input provided"}
  }

  # Additional error definitions needed by tests
  @additional_error_definitions %{
    {:system, :initialization, :service_unavailable} =>
      {2102, :high, "Required service unavailable"}
  }

  # Combined error definitions
  @all_error_definitions Map.merge(@error_definitions, @additional_error_definitions)

  @doc """
  Create a new error with the given error type and optional message.

  ## Parameters
  - `error_type`: The specific error type atom
  - `message`: Custom error message (optional, will use default if nil)
  - `opts`: Additional options including context, correlation_id, stacktrace

  ## Examples

      iex> error = Foundation.Error.new(:config_not_found)
      iex> error.error_type
      :config_not_found

      iex> error = Foundation.Error.new(:timeout, "Request timed out", context: %{timeout: 5000})
      iex> error.message
      "Request timed out"

  ## Raises
  - `KeyError` if required fields are missing
  """
  @spec new(error_code(), String.t() | nil, keyword()) :: t()
  def new(error_type, message \\ nil, opts \\ []) do
    {code, severity, default_message} = get_error_definition(error_type)
    {category, subcategory} = categorize_error(error_type)

    %__MODULE__{
      code: code,
      error_type: error_type,
      message: message || default_message,
      severity: severity,
      context: Keyword.get(opts, :context, %{}),
      correlation_id: Keyword.get(opts, :correlation_id),
      timestamp: DateTime.utc_now(),
      stacktrace: format_stacktrace(Keyword.get(opts, :stacktrace)),
      category: category,
      subcategory: subcategory,
      retry_strategy: determine_retry_strategy(error_type, severity),
      recovery_actions: suggest_recovery_actions(error_type, opts)
    }
  end

  @doc """
  Create an error result tuple.

  ## Parameters
  - `error_type`: The specific error type atom
  - `message`: Custom error message (optional)
  - `opts`: Additional options

  ## Examples

      iex> Foundation.Error.error_result(:data_not_found)
      {:error, %Foundation.Error{error_type: :data_not_found, ...}}
  """
  @spec error_result(error_code(), String.t() | nil, keyword()) :: {:error, t()}
  def error_result(error_type, message \\ nil, opts \\ []) do
    {:error, new(error_type, message, opts)}
  end

  @doc """
  Wrap an existing result with additional error context.

  ## Parameters
  - `result`: The result to potentially wrap
  - `error_type`: The wrapper error type
  - `message`: Custom error message (optional)
  - `opts`: Additional options

  ## Examples

      iex> result = {:error, :timeout}
      iex> Foundation.Error.wrap_error(result, :external_service_error)
      {:error, %Foundation.Error{...}}
  """
  @spec wrap_error(term(), error_code(), String.t() | nil, keyword()) :: term()
  def wrap_error(result, error_type, message \\ nil, opts \\ []) do
    case result do
      {:error, existing_error} when is_struct(existing_error, __MODULE__) ->
        # Chain errors while preserving original context
        enhanced_error = %{
          existing_error
          | context:
              Map.merge(existing_error.context || %{}, %{
                wrapped_by: error_type,
                wrapper_message: message,
                wrapper_context: Keyword.get(opts, :context, %{})
              })
        }

        {:error, enhanced_error}

      {:error, reason} ->
        # Wrap raw error reasons
        error_result(
          error_type,
          message,
          Keyword.put(
            opts,
            :context,
            Map.merge(
              Keyword.get(opts, :context, %{}),
              %{original_reason: reason}
            )
          )
        )

      other ->
        other
    end
  end

  ## Error Analysis and Recovery

  def is_retryable?(%__MODULE__{retry_strategy: strategy}) do
    strategy != :no_retry
  end

  def retry_delay(%__MODULE__{retry_strategy: :exponential_backoff}, attempt) do
    min(1000 * :math.pow(2, attempt), 30_000) |> round()
  end

  def retry_delay(%__MODULE__{retry_strategy: :fixed_delay}, _attempt), do: 1000
  def retry_delay(%__MODULE__{retry_strategy: :immediate}, _attempt), do: 0
  def retry_delay(%__MODULE__{retry_strategy: :no_retry}, _attempt), do: :infinity

  def should_escalate?(%__MODULE__{severity: severity}) do
    severity in [:high, :critical]
  end

  ## String Representation

  def to_string(%__MODULE__{} = error) do
    base = "[#{error.code}:#{error.error_type}] #{error.message}"

    context_str =
      if map_size(error.context) > 0 do
        " | Context: #{inspect(error.context, limit: 3)}"
      else
        ""
      end

    severity_str = " | Severity: #{error.severity}"

    base <> context_str <> severity_str
  end

  ## Error Metrics Collection

  def collect_error_metrics(%__MODULE__{} = error) do
    # Emit telemetry for error tracking
    Foundation.Telemetry.emit_counter(
      [:foundation, :errors, error.category, error.subcategory],
      %{
        error_type: error.error_type,
        severity: error.severity,
        code: error.code
      }
    )
  end

  ## Private Implementation

  @spec get_error_definition(error_code()) :: {pos_integer(), error_severity(), String.t()}
  defp get_error_definition(error_type) do
    # Find the error definition by searching for matching error_type
    case Enum.find(@all_error_definitions, fn {key, _value} ->
           case key do
             {_category, _subcategory, ^error_type} -> true
             _ -> false
           end
         end) do
      {_key, definition} -> definition
      nil -> {9999, :medium, "Unknown error"}
    end
  end

  @spec categorize_error(error_code()) :: {error_category(), error_subcategory()}
  defp categorize_error(error_type) do
    case Enum.find(@all_error_definitions, fn {key, _value} ->
           case key do
             {_category, _subcategory, ^error_type} -> true
             _ -> false
           end
         end) do
      {{category, subcategory, _error_type}, _definition} -> {category, subcategory}
      nil -> {:unknown, :unknown}
    end
  end

  @spec determine_retry_strategy(error_code(), error_severity()) :: retry_strategy()
  defp determine_retry_strategy(error_type, severity) do
    case {error_type, severity} do
      {:timeout, _} -> :exponential_backoff
      {:network_error, _} -> :exponential_backoff
      {:external_service_error, _} -> :fixed_delay
      {_, :critical} -> :no_retry
      {_, :high} -> :immediate
      # Config and data validation errors usually aren't retryable
      {:invalid_config_value, _} -> :no_retry
      {:data_corruption, _} -> :no_retry
      # Resource exhaustion should allow immediate retry
      {:resource_exhausted, _} -> :immediate
      _ -> :fixed_delay
    end
  end

  @spec suggest_recovery_actions(error_code(), keyword()) :: [String.t()]
  defp suggest_recovery_actions(error_type, opts) do
    base_actions =
      case error_type do
        :config_not_found ->
          ["Check configuration file", "Verify configuration path"]

        :invalid_config_value ->
          ["Check configuration format", "Validate against schema", "Review documentation"]

        :service_unavailable ->
          ["Check service health", "Verify network connectivity", "Review service logs"]

        :resource_exhausted ->
          ["Check memory usage", "Review resource limits", "Scale resources if needed"]

        :network_error ->
          ["Check network connectivity", "Retry with exponential backoff"]

        :timeout ->
          ["Increase timeout value", "Check network latency", "Review service performance"]

        :data_corruption ->
          ["Restore from backup", "Contact system administrator"]

        _ ->
          ["Check logs for details", "Review error context", "Contact support if needed"]
      end

    # Add context-specific actions
    context_actions =
      case Keyword.get(opts, :context) do
        %{operation: operation} -> ["Review #{operation} implementation"]
        _ -> []
      end

    base_actions ++ context_actions
  end

  @spec format_stacktrace(list() | nil) :: stacktrace_info() | nil
  defp format_stacktrace(nil), do: nil

  defp format_stacktrace(stacktrace) when is_list(stacktrace) do
    stacktrace
    # Limit stacktrace depth to 10 entries
    |> Enum.take(10)
    |> Enum.map(fn
      {module, function, arity, location} ->
        %{
          module: module,
          function: function,
          arity: arity,
          file: Keyword.get(location, :file),
          line: Keyword.get(location, :line)
        }

      other ->
        inspect(other)
    end)
  end
end
</file>

<file path="foundation/events.ex">
defmodule Foundation.Events do
  @moduledoc """
  Public API for event management and storage.

  Thin wrapper around EventStore that provides a clean, documented interface.
  All business logic is delegated to the service layer.
  """

  @behaviour Foundation.Contracts.EventStore

  alias Foundation.Services.EventStore
  alias Foundation.Types.{Event, Error}

  @type event_id :: Event.event_id()
  @type correlation_id :: Event.correlation_id()
  @type event_query :: map()

  @doc """
  Initialize the event store service.

  ## Examples

      iex> Foundation.Events.initialize()
      :ok
  """
  @spec initialize() :: :ok | {:error, Error.t()}
  def initialize() do
    EventStore.initialize()
  end

  @doc """
  Get event store service status.

  ## Examples

      iex> Foundation.Events.status()
      {:ok, %{status: :running, uptime: 12345}}
  """
  @spec status() :: {:ok, map()} | {:error, Error.t()}
  def status() do
    EventStore.status()
  end

  @doc """
  Check if the Events service is available.

  ## Examples

      iex> Foundation.Events.available?()
      true
  """
  @spec available?() :: boolean()
  def available?() do
    case GenServer.whereis(EventStore) do
      pid when is_pid(pid) -> true
      nil -> false
    end
  end

  @doc """
  Create a new event with the given type and data.

  ## Examples

      iex> Foundation.Events.new_event(:test_event, %{key: "value"})
      {:ok, %Event{event_type: :test_event, data: %{key: "value"}}}

      iex> Foundation.Events.new_event(:invalid, nil)
      {:error, %Error{error_type: :invalid_event_data}}
  """
  @spec new_event(atom(), term()) :: {:ok, Event.t()} | {:error, Error.t()}
  def new_event(event_type, data, opts \\ []) do
    alias Foundation.Logic.EventLogic
    EventLogic.create_event(event_type, data, opts)
  end

  @doc """
  Create a new event for debugging/testing (returns event directly, not tuple).

  ## Examples

      iex> Foundation.Events.debug_new_event(:test_event, %{key: "value"})
      %Event{event_type: :test_event, data: %{key: "value"}}
  """
  @spec debug_new_event(atom(), term(), keyword()) :: Event.t()
  def debug_new_event(event_type, data, opts \\ []) do
    case new_event(event_type, data, opts) do
      {:ok, event} -> event
      {:error, _error} -> raise "Failed to create debug event"
    end
  end

  @doc """
  Serialize an event to binary format.

  ## Examples

      iex> {:ok, event} = Events.new_event(:test, %{})
      iex> Foundation.Events.serialize(event)
      {:ok, <<...>>}
  """
  @spec serialize(Event.t()) :: {:ok, binary()} | {:error, Error.t()}
  def serialize(event) do
    alias Foundation.Logic.EventLogic
    EventLogic.serialize_event(event)
  end

  @doc """
  Deserialize binary data back to an event.

  ## Examples

      iex> {:ok, serialized} = Events.serialize(event)
      iex> Foundation.Events.deserialize(serialized)
      {:ok, %Event{...}}
  """
  @spec deserialize(binary()) :: {:ok, Event.t()} | {:error, Error.t()}
  def deserialize(binary) do
    alias Foundation.Logic.EventLogic
    EventLogic.deserialize_event(binary)
  end

  @doc """
  Calculate the serialized size of an event.

  ## Examples

      iex> {:ok, event} = Events.new_event(:test, %{})
      iex> Foundation.Events.serialized_size(event)
      {:ok, 156}
  """
  @spec serialized_size(Event.t()) :: {:ok, non_neg_integer()} | {:error, Error.t()}
  def serialized_size(event) do
    alias Foundation.Logic.EventLogic
    EventLogic.calculate_serialized_size(event)
  end

  @doc """
  Create a function entry event (convenience function).

  ## Examples

      iex> Foundation.Events.function_entry(MyModule, :my_func, 2, [arg1, arg2])
      {:ok, %Event{event_type: :function_entry, ...}}
  """
  @spec function_entry(module(), atom(), arity(), [term()], keyword()) ::
          {:ok, Event.t()} | {:error, Error.t()}
  def function_entry(module, function, arity, args, opts \\ []) do
    alias Foundation.Logic.EventLogic
    EventLogic.create_function_entry(module, function, arity, args, opts)
  end

  @doc """
  Create a function exit event (convenience function).

  ## Examples

      iex> Foundation.Events.function_exit(MyModule, :my_func, 2, 123, :ok, 1000, :normal)
      {:ok, %Event{event_type: :function_exit, ...}}
  """
  @spec function_exit(module(), atom(), arity(), pos_integer(), term(), non_neg_integer(), atom()) ::
          {:ok, Event.t()} | {:error, Error.t()}
  def function_exit(module, function, arity, call_id, result, duration_ns, exit_reason) do
    alias Foundation.Logic.EventLogic

    EventLogic.create_function_exit(
      module,
      function,
      arity,
      call_id,
      result,
      duration_ns,
      exit_reason
    )
  end

  @doc """
  Create a state change event (convenience function).

  ## Examples

      iex> Foundation.Events.state_change(self(), :handle_call, old_state, new_state)
      {:ok, %Event{event_type: :state_change, ...}}
  """
  @spec state_change(pid(), atom(), term(), term(), keyword()) ::
          {:ok, Event.t()} | {:error, Error.t()}
  def state_change(server_pid, callback, old_state, new_state, opts \\ []) do
    alias Foundation.Logic.EventLogic
    EventLogic.create_state_change(server_pid, callback, old_state, new_state, opts)
  end

  @doc """
  Store a single event.

  ## Examples

      iex> {:ok, event} = Event.new(event_type: :test, data: %{key: "value"})
      iex> Foundation.Events.store(event)
      {:ok, 12345}
  """
  @spec store(Event.t()) :: {:ok, event_id()} | {:error, Error.t()}
  defdelegate store(event), to: EventStore

  @doc """
  Store multiple events atomically.

  ## Examples

      iex> events = [event1, event2, event3]
      iex> Foundation.Events.store_batch(events)
      {:ok, [12345, 12346, 12347]}
  """
  @spec store_batch([Event.t()]) :: {:ok, [event_id()]} | {:error, Error.t()}
  defdelegate store_batch(events), to: EventStore

  @doc """
  Retrieve an event by ID.

  ## Examples

      iex> Foundation.Events.get(12345)
      {:ok, %Event{event_id: 12345, ...}}

      iex> Foundation.Events.get(99999)
      {:error, %Error{error_type: :not_found}}
  """
  @spec get(event_id()) :: {:ok, Event.t()} | {:error, Error.t()}
  defdelegate get(event_id), to: EventStore

  @doc """
  Query events with filters and pagination.

  ## Examples

      iex> query = [event_type: :function_entry, limit: 10]
      iex> Foundation.Events.query(query)
      {:ok, [%Event{}, ...]}

      iex> query = [time_range: {start_time, end_time}, order_by: :timestamp]
      iex> Foundation.Events.query(query)
      {:ok, [%Event{}, ...]}
  """
  @spec query(event_query()) :: {:ok, [Event.t()]} | {:error, Error.t()}
  def query(query_opts) when is_map(query_opts) do
    EventStore.query(query_opts)
  end

  def query(query_opts) when is_list(query_opts) do
    query_map = Map.new(query_opts)
    EventStore.query(query_map)
  end

  @doc """
  Get events by correlation ID.

  ## Examples

      iex> Foundation.Events.get_by_correlation("req-123")
      {:ok, [%Event{correlation_id: "req-123"}, ...]}
  """
  @spec get_by_correlation(correlation_id()) :: {:ok, [Event.t()]} | {:error, Error.t()}
  defdelegate get_by_correlation(correlation_id), to: EventStore

  @doc """
  Delete events older than the specified timestamp.

  ## Examples

      iex> cutoff = System.monotonic_time() - 3600_000  # 1 hour ago
      iex> Foundation.Events.prune_before(cutoff)
      {:ok, 150}  # 150 events pruned
  """
  @spec prune_before(integer()) :: {:ok, non_neg_integer()} | {:error, Error.t()}
  defdelegate prune_before(timestamp), to: EventStore

  @doc """
  Get storage statistics.

  ## Examples

      iex> Foundation.Events.stats()
      {:ok, %{
        current_event_count: 1000,
        events_stored: 5000,
        events_pruned: 200,
        memory_usage_estimate: 1024000,
        uptime_ms: 3600000
      }}
  """
  @spec stats() :: {:ok, map()} | {:error, Error.t()}
  defdelegate stats(), to: EventStore

  @doc """
  Extract correlation chain from stored events.

  ## Examples

      iex> Foundation.Events.get_correlation_chain("req-123")
      {:ok, [%Event{}, ...]}  # Events in chronological order
  """
  @spec get_correlation_chain(correlation_id()) :: {:ok, [Event.t()]} | {:error, Error.t()}
  def get_correlation_chain(correlation_id) do
    case get_by_correlation(correlation_id) do
      {:ok, events} ->
        alias Foundation.Logic.EventLogic
        chain = EventLogic.extract_correlation_chain(events, correlation_id)
        {:ok, chain}

      {:error, _} = error ->
        error
    end
  end

  @doc """
  Get events within a time range.

  ## Examples

      iex> start_time = System.monotonic_time() - 3600_000
      iex> end_time = System.monotonic_time()
      iex> Foundation.Events.get_time_range(start_time, end_time)
      {:ok, [%Event{}, ...]}
  """
  @spec get_time_range(integer(), integer()) :: {:ok, [Event.t()]} | {:error, Error.t()}
  def get_time_range(start_time, end_time) do
    query(%{
      time_range: {start_time, end_time},
      order_by: :timestamp
    })
  end

  @doc """
  Get recent events with optional limit.

  ## Examples

      iex> Foundation.Events.get_recent(50)
      {:ok, [%Event{}, ...]}  # Last 50 events
  """
  @spec get_recent(non_neg_integer()) :: {:ok, [Event.t()]} | {:error, Error.t()}
  def get_recent(limit \\ 100) do
    query(%{
      order_by: :timestamp,
      limit: limit
    })
  end
end

# defmodule Foundation.Events do
#   @moduledoc """
#   Core event system for Foundation.

#   Provides structured event creation, serialization, and basic event management.
#   This is the foundation for all event-driven communication within Foundation.
#   """

#   require Logger

#   alias Foundation.{Types, Utils, Error, ErrorContext}

#   @type event_id :: Types.event_id()
#   @type timestamp :: Types.timestamp()
#   @type correlation_id :: Types.correlation_id()

#   # Base event structure
#   defstruct [
#     :event_id,
#     :event_type,
#     :timestamp,
#     :wall_time,
#     :node,
#     :pid,
#     :correlation_id,
#     :parent_id,
#     :data
#   ]

#   @type t :: %__MODULE__{
#           event_id: event_id(),
#           event_type: atom(),
#           timestamp: timestamp(),
#           wall_time: DateTime.t(),
#           node: node(),
#           pid: pid(),
#           correlation_id: correlation_id() | nil,
#           parent_id: event_id() | nil,
#           data: term()
#         }

#   ## System Management

#   @spec initialize() :: :ok
#   def initialize do
#     Logger.debug("Foundation.Events initialized")
#     :ok
#   end

#   @spec status() :: :ok
#   def status, do: :ok

#   ## Event Creation

#   @spec new_event(atom(), term(), keyword()) :: t() | {:error, Error.t()}
#   def new_event(event_type, data, opts \\ []) do
#     context =
#       ErrorContext.new(__MODULE__, :new_event, metadata: %{event_type: event_type, opts: opts})

#     ErrorContext.with_context(context, fn ->
#       event = %__MODULE__{
#         event_id: Utils.generate_id(),
#         event_type: event_type,
#         timestamp: Utils.monotonic_timestamp(),
#         wall_time: DateTime.utc_now(),
#         node: Node.self(),
#         pid: self(),
#         correlation_id: Keyword.get(opts, :correlation_id),
#         parent_id: Keyword.get(opts, :parent_id),
#         data: data
#       }

#       case validate_event(event) do
#         :ok -> event
#         {:error, _} = error -> error
#       end
#     end)
#   end

#   @spec function_entry(module(), atom(), arity(), [term()], keyword()) :: t() | {:error, Error.t()}
#   def function_entry(module, function, arity, args, opts \\ []) do
#     data = %{
#       call_id: Utils.generate_id(),
#       module: module,
#       function: function,
#       arity: arity,
#       args: Utils.truncate_if_large(args),
#       caller_module: Keyword.get(opts, :caller_module),
#       caller_function: Keyword.get(opts, :caller_function),
#       caller_line: Keyword.get(opts, :caller_line)
#     }

#     new_event(:function_entry, data, opts)
#   end

#   @spec function_exit(module(), atom(), arity(), event_id(), term(), non_neg_integer(), atom()) ::
#           t() | {:error, Error.t()}
#   def function_exit(module, function, arity, call_id, result, duration_ns, exit_reason) do
#     data = %{
#       call_id: call_id,
#       module: module,
#       function: function,
#       arity: arity,
#       result: Utils.truncate_if_large(result),
#       duration_ns: duration_ns,
#       exit_reason: exit_reason
#     }

#     new_event(:function_exit, data)
#   end

#   @spec state_change(pid(), atom(), term(), term(), keyword()) :: t() | {:error, Error.t()}
#   def state_change(server_pid, callback, old_state, new_state, opts \\ []) do
#     data = %{
#       server_pid: server_pid,
#       callback: callback,
#       old_state: Utils.truncate_if_large(old_state),
#       new_state: Utils.truncate_if_large(new_state),
#       state_diff: compute_state_diff(old_state, new_state),
#       trigger_message: Keyword.get(opts, :trigger_message),
#       trigger_call_id: Keyword.get(opts, :trigger_call_id)
#     }

#     new_event(:state_change, data)
#   end

#   ## Event Serialization

#   @spec serialize(t()) :: binary() | {:error, Error.t()}
#   # def serialize(%__MODULE__{} = event) do
#   #   context = ErrorContext.new(__MODULE__, :serialize)

#   #   ErrorContext.with_context(context, fn ->
#   #     :erlang.term_to_binary(event, [:compressed])
#   #   end)
#   # end
#   def serialize(event) do
#     IO.puts("🔧 Events.serialize called with: #{inspect(event, limit: :infinity)}")

#     try do
#       # Check if event contains unserializable data BEFORE trying to serialize
#       case event.data do
#         %{bad_field: pid} when is_pid(pid) ->
#           IO.puts("💥 Found PID in data, forcing serialization failure")
#           raise ArgumentError, "Cannot serialize PID in event data"

#         _ ->
#           :ok
#       end

#       result = :erlang.term_to_binary(event)
#       IO.puts("✅ Events.serialize SUCCESS - binary size: #{byte_size(result)}")
#       result
#     rescue
#       error ->
#         IO.puts("❌ Events.serialize FAILED: #{inspect(error)}")
#         {:error, error}
#     catch
#       :throw, value ->
#         IO.puts("❌ Events.serialize THROWN: #{inspect(value)}")
#         {:error, value}

#       :exit, reason ->
#         IO.puts("❌ Events.serialize EXIT: #{inspect(reason)}")
#         {:error, reason}
#     end
#   end

#   def debug_new_event(event_type, data, opts \\ []) do
#     IO.puts("🔧 Events.new_event called")
#     IO.puts("📥 event_type: #{inspect(event_type)}")
#     IO.puts("📥 data: #{inspect(data, limit: :infinity)}")
#     IO.puts("📥 opts: #{inspect(opts)}")

#     result = new_event(event_type, data, opts)
#     IO.puts("📤 new_event result: #{inspect(result, limit: :infinity)}")
#     result
#   end

#   @spec deserialize(binary()) :: t() | {:error, Error.t()}
#   def deserialize(binary) when is_binary(binary) do
#     context = ErrorContext.new(__MODULE__, :deserialize)

#     ErrorContext.with_context(context, fn ->
#       event = :erlang.binary_to_term(binary)

#       case validate_event(event) do
#         :ok -> event
#         {:error, _} = error -> error
#       end
#     end)
#   end

#   @spec serialized_size(t()) :: non_neg_integer()
#   def serialized_size(%__MODULE__{} = event) do
#     case serialize(event) do
#       {:error, _} -> 0
#       binary when is_binary(binary) -> byte_size(binary)
#     end
#   end

#   ## Private Validation

#   @spec validate_event(t()) :: :ok | {:error, Error.t()}
#   defp validate_event(%__MODULE__{} = event) do
#     cond do
#       is_nil(event.event_id) ->
#         Error.error_result(:validation_failed, "Event ID cannot be nil")

#       not is_atom(event.event_type) ->
#         Error.error_result(:type_mismatch, "Event type must be an atom")

#       not is_integer(event.timestamp) ->
#         Error.error_result(:type_mismatch, "Timestamp must be an integer")

#       true ->
#         :ok
#     end
#   end

#   defp validate_event(_) do
#     Error.error_result(:type_mismatch, "Expected Events struct")
#   end

#   ## Private Helper Functions

#   @spec compute_state_diff(term(), term()) :: :no_change | :changed
#   defp compute_state_diff(old_state, new_state) do
#     if old_state == new_state do
#       :no_change
#     else
#       :changed
#     end
#   end
# end

# # ## Event Creation

# # @spec new_event(atom(), term(), keyword()) :: t()
# # def new_event(event_type, data, opts \\ []) do
# #   %__MODULE__{
# #     event_id: Utils.generate_id(),
# #     event_type: event_type,
# #     timestamp: Utils.monotonic_timestamp(),
# #     wall_time: DateTime.utc_now(),
# #     node: Node.self(),
# #     pid: self(),
# #     correlation_id: Keyword.get(opts, :correlation_id),
# #     parent_id: Keyword.get(opts, :parent_id),
# #     data: data
# #   }
# # end

# # @spec function_entry(module(), atom(), arity(), [term()], keyword()) :: t()
# # def function_entry(module, function, arity, args, opts \\ []) do
# #   data = %{
# #     call_id: Utils.generate_id(),
# #     module: module,
# #     function: function,
# #     arity: arity,
# #     args: Utils.truncate_if_large(args),
# #     caller_module: Keyword.get(opts, :caller_module),
# #     caller_function: Keyword.get(opts, :caller_function),
# #     caller_line: Keyword.get(opts, :caller_line)
# #   }

# #   new_event(:function_entry, data, opts)
# # end

# # @spec function_exit(module(), atom(), arity(), event_id(), term(), non_neg_integer(), atom()) :: t()
# # def function_exit(module, function, arity, call_id, result, duration_ns, exit_reason) do
# #   data = %{
# #     call_id: call_id,
# #     module: module,
# #     function: function,
# #     arity: arity,
# #     result: Utils.truncate_if_large(result),
# #     duration_ns: duration_ns,
# #     exit_reason: exit_reason
# #   }

# #   new_event(:function_exit, data)
# # end

# # @spec state_change(pid(), atom(), term(), term(), keyword()) :: t()
# # def state_change(server_pid, callback, old_state, new_state, opts \\ []) do
# #   data = %{
# #     server_pid: server_pid,
# #     callback: callback,
# #     old_state: Utils.truncate_if_large(old_state),
# #     new_state: Utils.truncate_if_large(new_state),
# #     state_diff: compute_state_diff(old_state, new_state),
# #     trigger_message: Keyword.get(opts, :trigger_message),
# #     trigger_call_id: Keyword.get(opts, :trigger_call_id)
# #   }

# #   new_event(:state_change, data)
# # end

# # ## Event Processing

# # @spec serialize(t()) :: binary()
# # def serialize(%__MODULE__{} = event) do
# #   :erlang.term_to_binary(event, [:compressed])
# # end

# # @spec deserialize(binary()) :: t()
# # def deserialize(binary) when is_binary(binary) do
# #   :erlang.binary_to_term(binary)
# # end

# # @spec serialized_size(t()) :: non_neg_integer()
# # def serialized_size(%__MODULE__{} = event) do
# #   event |> serialize() |> byte_size()
# # end

# ## Private Functions

# # defp compute_state_diff(old_state, new_state) do
# #   if old_state == new_state do
# #     :no_change
# #   else
# #     :changed
# #   end
# # end
# # end
</file>

<file path="foundation/graceful_degradation.ex">
defmodule Foundation.Config.GracefulDegradation do
  @moduledoc """
  Graceful degradation functionality for configuration management.
  Provides fallback mechanisms when the primary config service is unavailable.
  """

  alias Foundation.Config
  alias Foundation.Types.Error
  require Logger

  @fallback_table :config_fallback_cache
  # 5 minutes
  @cache_ttl 300

  @doc """
  Initialize the fallback system with ETS table for caching.
  """
  def initialize_fallback_system do
    case :ets.whereis(@fallback_table) do
      :undefined ->
        :ets.new(@fallback_table, [:named_table, :public, :set, {:read_concurrency, true}])
        Logger.info("Fallback system initialized with ETS table: #{@fallback_table}")
        :ok

      _table ->
        Logger.debug("Fallback system already initialized")
        :ok
    end
  end

  @doc """
  Clean up the fallback system and remove ETS tables.
  """
  def cleanup_fallback_system do
    try do
      case :ets.info(@fallback_table) do
        :undefined ->
          :ok

        _ ->
          :ets.delete(@fallback_table)
          :ok
      end
    rescue
      ArgumentError ->
        # Table doesn't exist or already deleted
        :ok
    end
  end

  @doc """
  Get configuration value with fallback to cached values.
  """
  # Dialyzer warning suppressed: Config.get/1 success inferred but error handling
  # is required for graceful degradation when service becomes unavailable
  @dialyzer {:nowarn_function, get_with_fallback: 1}
  def get_with_fallback(path) when is_list(path) do
    case Config.get(path) do
      {:ok, value} ->
        # Cache successful result
        cache_key = {:config_cache, path}
        timestamp = System.system_time(:second)
        :ets.insert(@fallback_table, {cache_key, value, timestamp})
        {:ok, value}

      {:error, _reason} ->
        # Try fallback from cache
        get_from_cache(path)
    end
  end

  @doc """
  Update configuration with fallback caching of pending updates.
  """
  # Dialyzer warning suppressed: Config.update/2 success inferred but error handling
  # is essential for pending update caching when service disruptions occur
  @dialyzer {:nowarn_function, update_with_fallback: 2}
  def update_with_fallback(path, value) when is_list(path) do
    case Config.update(path, value) do
      :ok ->
        # Remove any pending update for this path
        pending_key = {:pending_update, path}
        :ets.delete(@fallback_table, pending_key)
        :ok

      {:error, reason} ->
        # Cache as pending update
        timestamp = System.system_time(:second)
        pending_key = {:pending_update, path}
        :ets.insert(@fallback_table, {pending_key, value, timestamp})

        Logger.warning(
          "Config update failed, cached as pending: #{inspect(path)} -> #{inspect(value)}"
        )

        {:error, reason}
    end
  end

  @doc """
  Clean up expired cache entries based on TTL.
  """
  # Dialyzer warning suppressed: ETS operations may not fail in current context,
  # but error patterns ensure robust cleanup in edge cases
  @dialyzer {:nowarn_function, cleanup_expired_cache: 0}
  def cleanup_expired_cache do
    current_time = System.system_time(:second)

    # Get all entries and remove expired ones
    :ets.foldl(
      fn
        {key, _value, timestamp}, _acc when is_integer(timestamp) ->
          if current_time - timestamp > @cache_ttl do
            :ets.delete(@fallback_table, key)
          end

          :ok

        _entry, _acc ->
          :ok
      end,
      :ok,
      @fallback_table
    )

    Logger.debug("Expired cache entries cleaned up")
    :ok
  end

  @doc """
  Retry all pending configuration updates.
  """
  # Dialyzer warning suppressed: Config.update/2 success inferred in retry context,
  # but error handling is necessary for persistent failures and retry logic
  @dialyzer {:nowarn_function, retry_pending_updates: 0}
  def retry_pending_updates do
    # Get all pending updates
    pending_updates =
      :ets.select(@fallback_table, [
        {{{:pending_update, :"$1"}, :"$2", :"$3"}, [], [{{:"$1", :"$2"}}]}
      ])

    Logger.debug("Retrying #{length(pending_updates)} pending updates")

    # Try to apply each pending update
    results =
      Enum.map(pending_updates, fn {path, value} ->
        case Config.update(path, value) do
          :ok ->
            # Remove from pending updates
            :ets.delete(@fallback_table, {:pending_update, path})
            Logger.debug("Successfully applied pending update: #{inspect(path)}")
            {:ok, path}

          {:error, reason} ->
            # Keep in pending updates for next retry
            Logger.debug("Pending update still failed: #{inspect(path)} - #{inspect(reason)}")
            {:error, {path, reason}}
        end
      end)

    successful = Enum.count(results, &match?({:ok, _}, &1))
    Logger.info("Retry completed: #{successful}/#{length(pending_updates)} updates successful")

    :ok
  end

  @doc """
  Get current cache statistics.
  """
  def get_cache_stats do
    try do
      size = :ets.info(@fallback_table, :size)
      memory = :ets.info(@fallback_table, :memory)

      # Count different types of entries
      config_entries =
        :ets.select_count(@fallback_table, [
          {{{:config_cache, :"$1"}, :"$2", :"$3"}, [], [true]}
        ])

      pending_entries =
        :ets.select_count(@fallback_table, [
          {{{:pending_update, :"$1"}, :"$2", :"$3"}, [], [true]}
        ])

      %{
        total_entries: size,
        memory_words: memory,
        config_cache_entries: config_entries,
        pending_update_entries: pending_entries
      }
    catch
      :error, :badarg ->
        %{error: :table_not_found}
    end
  end

  # Private helper functions

  # Dialyzer warning suppressed: Function may appear unused but is called from
  # get_with_fallback/1 when Config.get/1 fails and cache fallback is needed
  @dialyzer {:nowarn_function, get_from_cache: 1}
  defp get_from_cache(path) do
    cache_key = {:config_cache, path}
    current_time = System.system_time(:second)

    case :ets.lookup(@fallback_table, cache_key) do
      [{^cache_key, value, timestamp}] ->
        if current_time - timestamp <= @cache_ttl do
          Logger.debug("Using cached config value for path: #{inspect(path)}")
          {:ok, value}
        else
          # Cache expired
          :ets.delete(@fallback_table, cache_key)
          Logger.warning("Cache expired for path: #{inspect(path)}")

          {:error,
           Error.new(
             error_type: :config_unavailable,
             message: "Configuration cache expired",
             context: %{path: path},
             category: :config,
             subcategory: :access,
             severity: :medium
           )}
        end

      [] ->
        Logger.warning("No cached value available for path: #{inspect(path)}")

        {:error,
         Error.new(
           error_type: :config_unavailable,
           message: "Configuration not available",
           context: %{path: path},
           category: :config,
           subcategory: :access,
           severity: :medium
         )}
    end
  end
end

defmodule Foundation.Events.GracefulDegradation do
  @moduledoc """
  Graceful degradation for Events service when serialization or storage fails.
  """

  alias Foundation.{Events, Utils}
  alias Foundation.Types.Event
  require Logger

  @doc """
  Create an event safely, handling problematic data.
  """
  def new_event_safe(event_type, data) do
    try do
      # Try normal event creation first
      case Events.new_event(event_type, data) do
        {:ok, event} ->
          event

        other ->
          Logger.warning("Unexpected result from Events.new_event: #{inspect(other)}")
          create_minimal_event(event_type, data)
      end
    rescue
      error ->
        Logger.warning("Primary event creation failed: #{Exception.message(error)}")
        # Fallback: create event with sanitized data
        sanitized_data = sanitize_data(data)

        try do
          case Events.new_event(event_type, sanitized_data) do
            {:ok, event} -> event
            _other -> create_minimal_event(event_type, data)
          end
        rescue
          fallback_error ->
            Logger.error(
              "Fallback event creation also failed: #{Exception.message(fallback_error)}"
            )

            # Last resort: create minimal event
            create_minimal_event(event_type, data)
        end
    end
  end

  @doc """
  Serialize event safely with fallback to JSON.
  """
  def serialize_safe(event) do
    Logger.debug("🚀 GracefulDegradation.serialize_safe called")
    Logger.debug("📥 Input event: #{inspect(event)}")

    try do
      # Try normal serialization first
      case Events.serialize(event) do
        {:ok, binary} ->
          Logger.debug("✅ Events.serialize succeeded - binary size: #{byte_size(binary)}")
          Logger.debug("📤 Final serialize_safe result: #{inspect(binary, limit: 50)}")
          binary

        {:error, _reason} ->
          Logger.warning("Events.serialize returned error, using fallback")
          fallback_serialize(event)
      end
    rescue
      error ->
        Logger.warning("Primary serialization failed: #{Exception.message(error)}")
        # Fallback to JSON
        fallback_serialize(event)
    end
  end

  @doc """
  Deserialize event safely with fallback handling.
  """
  def deserialize_safe(binary) when is_binary(binary) do
    try do
      # Try normal deserialization first
      Events.deserialize(binary)
    rescue
      error ->
        Logger.warning("Primary deserialization failed: #{Exception.message(error)}")
        # Try JSON fallback
        fallback_deserialize(binary)
    end
  end

  @doc """
  Store event safely with fallback mechanisms.
  """
  def store_safe(event) do
    try do
      # Try normal storage first
      Events.store(event)
    rescue
      error ->
        Logger.warning("Primary event storage failed: #{Exception.message(error)}")
        # Fallback: store in memory buffer
        store_in_fallback_buffer(event)
    end
  end

  @doc """
  Query events safely with graceful degradation.
  """
  def query_safe(query_params) do
    try do
      # Try normal query first
      Events.query(query_params)
    rescue
      error ->
        Logger.warning("Primary event query failed: #{Exception.message(error)}")
        # Fallback: return empty results with warning
        {:ok, [], %{warning: "Service temporarily unavailable"}}
    end
  end

  # Private helper functions

  defp sanitize_data(data) when is_map(data) do
    Enum.reduce(data, %{}, fn {key, value}, acc ->
      sanitized_value = sanitize_value(value)
      Map.put(acc, key, sanitized_value)
    end)
  end

  defp sanitize_data(data), do: data

  defp sanitize_value(value) when is_pid(value) do
    inspect(value)
  end

  defp sanitize_value(value) when is_reference(value) do
    inspect(value)
  end

  defp sanitize_value(value) when is_function(value) do
    "#Function<#{inspect(value)}>"
  end

  defp sanitize_value(value) when is_port(value) do
    inspect(value)
  end

  defp sanitize_value(value) when is_map(value) do
    sanitize_data(value)
  end

  defp sanitize_value(value) when is_list(value) do
    Enum.map(value, &sanitize_value/1)
  end

  defp sanitize_value(value), do: value

  defp create_minimal_event(event_type, original_data) do
    %Event{
      event_type: event_type,
      event_id: Utils.generate_id(),
      timestamp: System.system_time(:microsecond),
      wall_time: DateTime.utc_now(),
      node: Node.self(),
      pid: self(),
      correlation_id: nil,
      parent_id: nil,
      data: %{
        original_data_error: "Failed to serialize original data",
        fallback_info: %{
          original_data_type: data_type(original_data),
          sanitization_attempted: true,
          timestamp: DateTime.utc_now()
        }
      }
    }
  end

  defp fallback_serialize(event) do
    try do
      # Convert to JSON-safe format
      json_safe_event = make_json_safe(event)
      json_data = Jason.encode!(json_safe_event)
      :erlang.term_to_binary({:json_fallback, json_data})
    rescue
      json_error ->
        Logger.error("JSON fallback serialization failed: #{Exception.message(json_error)}")
        # Absolute fallback: minimal binary representation
        minimal_data = %{
          event_type: event.event_type,
          timestamp: event.timestamp,
          error: "Serialization failed"
        }

        :erlang.term_to_binary({:minimal_fallback, minimal_data})
    end
  end

  defp fallback_deserialize(binary) do
    try do
      case :erlang.binary_to_term(binary) do
        {:json_fallback, json_data} ->
          event_data = Jason.decode!(json_data, keys: :atoms)
          {:ok, struct(Event, event_data)}

        {:minimal_fallback, minimal_data} ->
          {:ok, %{fallback_data: minimal_data, warning: "Minimal fallback used"}}

        other ->
          Logger.warning("Unknown fallback format: #{inspect(other)}")
          {:error, :unknown_fallback_format}
      end
    rescue
      error ->
        Logger.error("Fallback deserialization failed: #{Exception.message(error)}")
        {:error, :deserialization_failed}
    end
  end

  defp make_json_safe(%Event{} = event) do
    %{
      event_type: event.event_type,
      event_id: event.event_id,
      timestamp: event.timestamp,
      wall_time: DateTime.to_iso8601(event.wall_time),
      node: to_string(event.node),
      pid: inspect(event.pid),
      correlation_id: event.correlation_id,
      parent_id: event.parent_id,
      data: sanitize_data(event.data)
    }
  end

  defp make_json_safe(data), do: sanitize_data(data)

  defp store_in_fallback_buffer(event) do
    # Store in process dictionary as last resort
    current_buffer = Process.get(:event_fallback_buffer, [])
    # Keep max 100 events
    updated_buffer = [event | Enum.take(current_buffer, 99)]
    Process.put(:event_fallback_buffer, updated_buffer)

    Logger.info("Event stored in fallback buffer (#{length(updated_buffer)} events buffered)")
    :ok
  end

  defp data_type(data) when is_map(data), do: :map
  defp data_type(data) when is_list(data), do: :list
  defp data_type(data) when is_binary(data), do: :binary
  defp data_type(data) when is_atom(data), do: :atom
  defp data_type(data) when is_number(data), do: :number
  defp data_type(data) when is_pid(data), do: :pid
  defp data_type(data) when is_reference(data), do: :reference
  defp data_type(data) when is_function(data), do: :function
  defp data_type(_data), do: :unknown
end
</file>

<file path="foundation/process_registry.ex">
defmodule Foundation.ProcessRegistry do
  @moduledoc """
  Centralized process registry for Foundation layer.

  Provides namespace isolation using Elixir's native Registry to enable concurrent testing
  and prevent naming conflicts between production and test environments.

  ## Performance Characteristics

  - **Storage**: ETS-based partitioned table for high concurrent throughput
  - **Partitions**: `#{System.schedulers_online()}` partitions (matches CPU cores)
  - **Lookup Time**: O(1) average case, < 1ms typical latency
  - **Registration**: Atomic operations with automatic process monitoring
  - **Memory**: Minimal overhead per registered process (~100 bytes)

  ## Registry Architecture

  Uses Elixir's native Registry module with optimized settings:
  - **Keys**: `:unique` - Each {namespace, service} key maps to exactly one process
  - **Partitioning**: CPU-optimized for concurrent access patterns
  - **Monitoring**: Automatic cleanup when processes terminate
  - **Fault Tolerance**: ETS table survives individual process crashes

  ## Supported Namespaces

  - `:production` - For normal operation
  - `{:test, reference()}` - For test isolation with unique references

  ## Examples

      # Register a service in production
      :ok = ProcessRegistry.register(:production, :config_server, self())

      # Register in test namespace
      test_ref = make_ref()
      :ok = ProcessRegistry.register({:test, test_ref}, :config_server, self())

      # Lookup services
      {:ok, pid} = ProcessRegistry.lookup(:production, :config_server)
  """

  @type namespace :: :production | {:test, reference()}
  @type service_name ::
          :config_server
          | :event_store
          | :telemetry_service
          | :test_supervisor

  @type registry_key :: {namespace(), service_name()}

  @doc """
  Child specification for supervision tree integration.
  """
  def child_spec(_opts) do
    %{
      id: Registry,
      start:
        {Registry, :start_link,
         [
           [
             keys: :unique,
             name: __MODULE__,
             partitions: System.schedulers_online()
           ]
         ]},
      type: :supervisor,
      restart: :permanent,
      shutdown: :infinity
    }
  end

  @doc """
  Register a service in the given namespace.

  ## Parameters
  - `namespace`: The namespace for service isolation
  - `service`: The service name to register
  - `pid`: The process PID to register

  ## Returns
  - `:ok` if registration succeeds
  - `{:error, {:already_registered, pid}}` if name already taken

  ## Examples

      iex> ProcessRegistry.register(:production, :config_server, self())
      :ok

      iex> ProcessRegistry.register(:production, :config_server, self())
      {:error, {:already_registered, #PID<0.123.0>}}
  """
  @spec register(namespace(), service_name(), pid()) :: :ok | {:error, {:already_registered, pid()}}
  def register(namespace, service, pid) when is_pid(pid) do
    registry_key = {namespace, service}

    # Always use the backup table for all registrations for consistency
    # This ensures both self() and external process registrations work the same way
    ensure_backup_registry()

    # Debug: Log registration attempts
    if Application.get_env(:foundation, :debug_registry, false) do
      require Logger

      Logger.debug(
        "ProcessRegistry.register: attempting to register #{inspect(registry_key)} -> #{inspect(pid)}"
      )
    end

    # Check if already registered
    case :ets.lookup(:process_registry_backup, registry_key) do
      [{^registry_key, existing_pid}] ->
        if Process.alive?(existing_pid) do
          if existing_pid == pid do
            if Application.get_env(:foundation, :debug_registry, false) do
              require Logger

              Logger.debug(
                "ProcessRegistry.register: already registered correctly #{inspect(registry_key)} -> #{inspect(pid)}"
              )
            end

            # Already registered correctly
            :ok
          else
            if Application.get_env(:foundation, :debug_registry, false) do
              require Logger

              Logger.debug(
                "ProcessRegistry.register: already registered to different pid #{inspect(registry_key)} -> #{inspect(existing_pid)}"
              )
            end

            {:error, {:already_registered, existing_pid}}
          end
        else
          # Dead process registered, replace with new one
          :ets.insert(:process_registry_backup, {registry_key, pid})

          if Application.get_env(:foundation, :debug_registry, false) do
            require Logger

            Logger.debug(
              "ProcessRegistry.register: replaced dead process #{inspect(registry_key)} -> #{inspect(pid)}"
            )
          end

          :ok
        end

      [] ->
        # Not registered, add new registration
        :ets.insert(:process_registry_backup, {registry_key, pid})

        if Application.get_env(:foundation, :debug_registry, false) do
          require Logger

          Logger.debug(
            "ProcessRegistry.register: new registration #{inspect(registry_key)} -> #{inspect(pid)}"
          )
        end

        :ok
    end
  end

  @doc """
  Look up a service in the given namespace.

  ## Parameters
  - `namespace`: The namespace to search in
  - `service`: The service name to lookup

  ## Returns
  - `{:ok, pid}` if service found
  - `:error` if service not found

  ## Examples

      iex> ProcessRegistry.lookup(:production, :config_server)
      {:ok, #PID<0.123.0>}

      iex> ProcessRegistry.lookup(:production, :nonexistent)
      :error
  """
  @spec lookup(namespace(), service_name()) :: {:ok, pid()} | :error
  def lookup(namespace, service) do
    registry_key = {namespace, service}

    # First try the native Registry lookup (for via_tuple registered services)
    case Registry.lookup(__MODULE__, registry_key) do
      [{pid, _value}] when is_pid(pid) ->
        if Process.alive?(pid) do
          {:ok, pid}
        else
          :error
        end

      [] ->
        # Fall back to backup table lookup
        # Ensure backup table exists before trying to use it
        ensure_backup_registry()

        case :ets.lookup(:process_registry_backup, registry_key) do
          [{^registry_key, pid}] ->
            # Verify the process is still alive
            if Process.alive?(pid) do
              {:ok, pid}
            else
              # Clean up dead process and return error
              :ets.delete(:process_registry_backup, registry_key)
              :error
            end

          [] ->
            # Debug: Log when service not found in backup table
            if Application.get_env(:foundation, :debug_registry, false) do
              require Logger
              all_entries = :ets.tab2list(:process_registry_backup)

              Logger.debug(
                "ProcessRegistry.lookup: service #{inspect(registry_key)} not found in backup table"
              )

              Logger.debug("ProcessRegistry.lookup: available entries: #{inspect(all_entries)}")
            end

            :error
        end
    end
  end

  @doc """
  Unregister a service from the given namespace.

  Note: This is typically not needed as Registry automatically
  unregisters when the process dies.

  ## Parameters
  - `namespace`: The namespace containing the service
  - `service`: The service name to unregister

  ## Returns
  - `:ok` regardless of whether service was registered

  ## Examples

      iex> ProcessRegistry.unregister(:production, :config_server)
      :ok
  """
  @spec unregister(namespace(), service_name()) :: :ok
  def unregister(namespace, service) do
    registry_key = {namespace, service}

    # Remove from backup table if it exists
    case :ets.info(:process_registry_backup) do
      :undefined -> :ok
      _ -> :ets.delete(:process_registry_backup, registry_key)
    end

    # Also remove from original Registry
    Registry.unregister(__MODULE__, registry_key)
  end

  @doc """
  List all services registered in a namespace.

  ## Parameters
  - `namespace`: The namespace to list services for

  ## Returns
  - List of service names registered in the namespace

  ## Examples

      iex> ProcessRegistry.list_services(:production)
      [:config_server, :event_store, :telemetry_service]

      iex> ProcessRegistry.list_services({:test, test_ref})
      []
  """
  @spec list_services(namespace()) :: [service_name()]
  def list_services(namespace) do
    # Get all services from both sources
    all_services = get_all_services(namespace)
    Map.keys(all_services)
  end

  @doc """
  Get all registered services with their PIDs for a namespace.

  ## Parameters
  - `namespace`: The namespace to get services for

  ## Returns
  - Map of service_name => pid

  ## Examples

      iex> ProcessRegistry.get_all_services(:production)
      %{
        config_server: #PID<0.123.0>,
        event_store: #PID<0.124.0>
      }
  """
  @spec get_all_services(namespace()) :: %{service_name() => pid()}
  def get_all_services(namespace) do
    # Check both Registry and backup table for complete coverage

    # First, get services from Registry (via_tuple registrations)
    registry_services =
      Registry.select(__MODULE__, [
        {{{namespace, :"$1"}, :"$2", :"$3"}, [], [{{:"$1", :"$2"}}]}
      ])
      |> Enum.filter(fn {_service_name, pid} -> Process.alive?(pid) end)
      |> Enum.into(%{})

    # Then, get services from backup table (direct registrations)
    # Ensure backup table exists before trying to use it
    ensure_backup_registry()

    # Use tab2list to avoid match specification issues
    backup_services =
      :ets.tab2list(:process_registry_backup)
      |> Enum.filter(fn {{entry_namespace, _service}, pid} ->
        entry_namespace == namespace and Process.alive?(pid)
      end)
      |> Enum.map(fn {{_namespace, service}, pid} -> {service, pid} end)
      |> Enum.into(%{})

    # Merge both sources, with backup table taking precedence for conflicts
    Map.merge(registry_services, backup_services)
  end

  @doc """
  Check if a service is registered in a namespace.

  ## Parameters
  - `namespace`: The namespace to check
  - `service`: The service name to check

  ## Returns
  - `true` if service is registered
  - `false` if service is not registered

  ## Examples

      iex> ProcessRegistry.registered?(namespace, :config_server)
      true
  """
  @spec registered?(namespace(), service_name()) :: boolean()
  def registered?(namespace, service) do
    case lookup(namespace, service) do
      {:ok, _pid} -> true
      :error -> false
    end
  end

  @doc """
  Count the number of services in a namespace.

  ## Parameters
  - `namespace`: The namespace to count services in

  ## Returns
  - Non-negative integer count of services

  ## Examples

      iex> ProcessRegistry.count_services(:production)
      3
  """
  @spec count_services(namespace()) :: non_neg_integer()
  # Dialyzer warning suppressed: Success typing is more specific in test context
  # but spec is correct for general usage
  @dialyzer {:nowarn_function, count_services: 1}
  def count_services(namespace) do
    # Use get_all_services for consistency
    all_services = get_all_services(namespace)
    map_size(all_services)
  end

  @doc """
  Create a via tuple for GenServer registration.

  This is used in GenServer.start_link/3 for automatic registration.

  ## Parameters
  - `namespace`: The namespace for the service
  - `service`: The service name

  ## Returns
  - Via tuple for GenServer registration

  ## Examples

      iex> via = ProcessRegistry.via_tuple(:production, :config_server)
      iex> GenServer.start_link(MyServer, [], name: via)
  """
  @spec via_tuple(namespace(), service_name()) :: {:via, Registry, {atom(), registry_key()}}
  def via_tuple(namespace, service) do
    {:via, Registry, {__MODULE__, {namespace, service}}}
  end

  @doc """
  Cleanup all services in a test namespace.

  This is useful for test cleanup - terminates all processes
  registered in the given test namespace.

  ## Parameters
  - `test_ref`: The test reference used in namespace

  ## Returns
  - `:ok` after cleanup is complete

  ## Examples

      iex> test_ref = make_ref()
      iex> # ... register services in {:test, test_ref} ...
      iex> ProcessRegistry.cleanup_test_namespace(test_ref)
      :ok
  """
  @spec cleanup_test_namespace(reference()) :: :ok
  def cleanup_test_namespace(test_ref) do
    namespace = {:test, test_ref}

    # Use backup table as primary source for consistency
    backup_pids =
      case :ets.info(:process_registry_backup) do
        :undefined ->
          []

        _ ->
          # Use tab2list instead of select to avoid match specification issues
          :ets.tab2list(:process_registry_backup)
          |> Enum.filter(fn {{entry_namespace, _service}, _pid} ->
            entry_namespace == namespace
          end)
          |> Enum.map(fn {{_namespace, _service}, pid} -> pid end)
      end

    # Terminate each process more safely to avoid test process termination
    Enum.each(backup_pids, fn pid ->
      if Process.alive?(pid) do
        # Spawn a separate process to handle the termination
        # This isolates the test process from any exit signals
        spawn(fn ->
          try do
            # Set trap_exit to handle any exit signals gracefully
            Process.flag(:trap_exit, true)

            # Try gentle shutdown first
            GenServer.stop(pid, :shutdown, 100)
          catch
            # If that fails, force termination
            :exit, _ ->
              Process.exit(pid, :shutdown)

              # Wait briefly then force kill if still alive
              Process.sleep(50)

              if Process.alive?(pid) do
                Process.exit(pid, :kill)
              end
          end
        end)
      end
    end)

    # Wait for all processes to be terminated
    Process.sleep(200)

    # Clean up backup table entries for this namespace and count cleaned entries
    cleanup_count =
      case :ets.info(:process_registry_backup) do
        :undefined ->
          0

        _ ->
          # Find all keys that match this namespace pattern
          # The backup table stores entries as {{namespace, service}, pid}
          all_entries = :ets.tab2list(:process_registry_backup)

          keys_to_delete =
            for {{entry_namespace, service}, pid} <- all_entries,
                entry_namespace == namespace do
              # Only delete if process is actually dead
              if not Process.alive?(pid) do
                {entry_namespace, service}
              else
                nil
              end
            end
            |> Enum.reject(&is_nil/1)

          Enum.each(keys_to_delete, fn key ->
            :ets.delete(:process_registry_backup, key)
          end)

          length(keys_to_delete)
      end

    # Log cleanup summary
    require Logger

    # Only log if there were actually services to clean up
    if cleanup_count > 0 do
      Logger.debug("Cleaned up #{cleanup_count} services from test namespace #{inspect(test_ref)}")
    end

    :ok
  end

  @doc """
  Get registry statistics for monitoring and performance analysis.

  ## Returns
  - Map with comprehensive registry statistics including:
    - Service counts by namespace type
    - Performance characteristics
    - Memory usage information
    - Partition utilization

  ## Examples

      iex> ProcessRegistry.stats()
      %{
        total_services: 15,
        production_services: 3,
        test_namespaces: 4,
        partitions: 8,
        partition_count: 8,
        memory_usage_bytes: 4096,
        ets_table_info: %{...}
      }
  """
  @spec stats() :: %{
          total_services: non_neg_integer(),
          production_services: non_neg_integer(),
          test_namespaces: non_neg_integer(),
          partitions: pos_integer(),
          partition_count: pos_integer(),
          memory_usage_bytes: non_neg_integer(),
          ets_table_info: map()
        }
  def stats() do
    # Ensure backup table exists before trying to use it
    ensure_backup_registry()

    # Get services from Registry (via_tuple registrations)
    # Registry stores: {{namespace, service}, pid, value}
    registry_services =
      Registry.select(__MODULE__, [
        {{{:"$1", :"$2"}, :"$3", :"$4"}, [], [{{:"$1", :"$2"}}]}
      ])
      |> Enum.map(fn {namespace, service} -> {namespace, service} end)

    # Get all entries from backup table using tab2list (simpler and more reliable)
    # Backup table stores: {{namespace, service}, pid}
    backup_services =
      :ets.tab2list(:process_registry_backup)
      |> Enum.filter(fn {{_namespace, _service}, pid} -> Process.alive?(pid) end)
      |> Enum.map(fn {{namespace, service}, _pid} -> {namespace, service} end)

    # Combine services from both sources, removing duplicates
    all_services =
      (registry_services ++ backup_services)
      |> Enum.uniq()

    # Count services by namespace type
    {production_count, test_namespaces} =
      Enum.reduce(all_services, {0, MapSet.new()}, fn
        {:production, _service}, {prod_count, test_set} ->
          {prod_count + 1, test_set}

        {{:test, ref}, _service}, {prod_count, test_set} ->
          {prod_count, MapSet.put(test_set, ref)}
      end)

    # Get ETS table information for performance monitoring
    ets_info =
      try do
        case :ets.info(:process_registry_backup) do
          :undefined ->
            %{backup_table_exists: false}

          info when is_list(info) ->
            %{
              backup_table_exists: true,
              table_size: Keyword.get(info, :size, 0),
              memory_words: Keyword.get(info, :memory, 0)
            }
        end
      rescue
        _ -> %{}
      end

    memory_usage =
      case ets_info do
        %{memory_words: words} when is_integer(words) -> words * :erlang.system_info(:wordsize)
        _ -> 0
      end

    %{
      total_services: length(all_services),
      production_services: production_count,
      test_namespaces: MapSet.size(test_namespaces),
      partitions: System.schedulers_online(),
      partition_count: System.schedulers_online(),
      memory_usage_bytes: memory_usage,
      ets_table_info: ets_info
    }
  end

  # Private helper functions

  @spec ensure_backup_registry() :: :ok
  defp ensure_backup_registry() do
    case :ets.info(:process_registry_backup) do
      :undefined ->
        :ets.new(:process_registry_backup, [:named_table, :public, :set])
        :ok

      _ ->
        :ok
    end
  end
end
</file>

<file path="foundation/service_registry.ex">
defmodule Foundation.ServiceRegistry do
  @moduledoc """
  High-level service registration API for Foundation layer.

  Provides a clean interface for service registration and discovery,
  wrapping the lower-level ProcessRegistry with error handling,
  logging, and convenience functions.

  ## Examples

      # Register a service
      :ok = ServiceRegistry.register(:production, :config_server, self())

      # Lookup a service
      {:ok, pid} = ServiceRegistry.lookup(:production, :config_server)

      # List services in a namespace
      [:config_server, :event_store] = ServiceRegistry.list_services(:production)
  """

  require Logger

  alias Foundation.ProcessRegistry
  alias Foundation.Types.Error

  @type namespace :: :production | {:test, reference()}
  @type service_name :: :config_server | :event_store | :telemetry_service | :test_supervisor
  @type registration_result :: :ok | {:error, {:already_registered, pid()}}
  @type lookup_result :: {:ok, pid()} | {:error, Error.t()}

  @doc """
  Register a service in the given namespace with error handling and logging.

  ## Parameters
  - `namespace`: The namespace for service isolation
  - `service`: The service name to register
  - `pid`: The process PID to register

  ## Returns
  - `:ok` if registration succeeds
  - `{:error, reason}` if registration fails

  ## Examples

      iex> ServiceRegistry.register(:production, :config_server, self())
      :ok

      iex> ServiceRegistry.register(:production, :config_server, self())
      {:error, {:already_registered, #PID<0.123.0>}}
  """
  @spec register(namespace(), service_name(), pid()) :: registration_result()
  def register(namespace, service, pid) when is_pid(pid) do
    Logger.debug("Registering service #{inspect(service)} in namespace #{inspect(namespace)}")

    result =
      case ProcessRegistry.register(namespace, service, pid) do
        :ok ->
          Logger.info(
            "Successfully registered service #{inspect(service)} in namespace #{inspect(namespace)}"
          )

          :ok

        {:error, {:already_registered, existing_pid}} = error ->
          Logger.warning(
            "Failed to register service #{inspect(service)} in namespace #{inspect(namespace)}: " <>
              "already registered to PID #{inspect(existing_pid)}"
          )

          error
      end

    # Emit telemetry
    emit_registration_telemetry(namespace, service, result)

    result
  end

  @doc """
  Lookup a service with optional error handling and telemetry.

  Includes telemetry events for monitoring Registry performance and usage patterns.

  ## Parameters
  - `namespace`: The namespace to search in
  - `service`: The service name to lookup

  ## Returns
  - `{:ok, pid()}` if service is found and healthy
  - `{:error, Error.t()}` if service not found or unhealthy

  ## Telemetry Events
  - `[:foundation, :foundation, :registry, :lookup]` - Emitted for all lookup operations
    - Measurements: `%{duration: integer()}` (in native time units)
    - Metadata: `%{namespace: term(), service: atom(), result: :ok | :error}`

  ## Examples

      {:ok, pid} = ServiceRegistry.lookup(:production, :config_server)
      {:error, %Error{}} = ServiceRegistry.lookup(:production, :nonexistent)
  """
  @spec lookup(namespace(), service_name()) :: lookup_result()
  def lookup(namespace, service) do
    start_time = System.monotonic_time()

    result =
      case ProcessRegistry.lookup(namespace, service) do
        {:ok, pid} -> {:ok, pid}
        :error -> {:error, create_service_not_found_error(namespace, service)}
      end

    # Emit telemetry
    emit_lookup_telemetry(namespace, service, result, start_time)

    result
  end

  @doc """
  Safely unregister a service from the given namespace.

  ## Parameters
  - `namespace`: The namespace containing the service
  - `service`: The service name to unregister

  ## Returns
  - `:ok` regardless of whether service was registered

  ## Examples

      iex> ServiceRegistry.unregister(:production, :config_server)
      :ok
  """
  @spec unregister(namespace(), service_name()) :: :ok
  def unregister(namespace, service) do
    Logger.debug("Unregistering service #{inspect(service)} from namespace #{inspect(namespace)}")

    result = ProcessRegistry.unregister(namespace, service)

    Logger.info("Unregistered service #{inspect(service)} from namespace #{inspect(namespace)}")
    result
  end

  @doc """
  List all services registered in a namespace.

  ## Parameters
  - `namespace`: The namespace to list services for

  ## Returns
  - List of service names registered in the namespace

  ## Examples

      iex> ServiceRegistry.list_services(:production)
      [:config_server, :event_store, :telemetry_service]
  """
  @spec list_services(namespace()) :: [service_name()]
  def list_services(namespace) do
    Logger.debug("Listing services in namespace #{inspect(namespace)}")

    services = ProcessRegistry.list_services(namespace)

    Logger.debug("Found #{length(services)} services in namespace #{inspect(namespace)}")
    services
  end

  @doc """
  Check if a service is available and healthy in a namespace.

  This goes beyond simple registration checking - it verifies
  the process is alive and optionally calls a health check.

  ## Parameters
  - `namespace`: The namespace to check
  - `service`: The service name to check
  - `opts`: Options for health checking

  ## Options
  - `:health_check` - Function to call for health verification
  - `:timeout` - Timeout for health check (default: 5000ms)

  ## Returns
  - `{:ok, pid}` if service is healthy
  - `{:error, reason}` if service is unhealthy or not found

  ## Examples

      iex> ServiceRegistry.health_check(:production, :config_server)
      {:ok, #PID<0.123.0>}

      iex> ServiceRegistry.health_check(:production, :config_server,
      ...>   health_check: fn pid -> GenServer.call(pid, :health) end)
      {:ok, #PID<0.123.0>}
  """
  @spec health_check(namespace(), service_name(), keyword()) ::
          {:ok, pid()}
          | {:error,
             :health_check_timeout
             | :process_dead
             | {:health_check_crashed, term()}
             | {:health_check_error, term()}
             | {:health_check_failed, term()}
             | Error.t()}
  def health_check(namespace, service, opts \\ []) do
    # Only log debug for health checks if explicitly requested
    if Keyword.get(opts, :debug_health_check, false) do
      Logger.debug("Health checking service #{inspect(service)} in namespace #{inspect(namespace)}")
    end

    case lookup(namespace, service) do
      {:ok, pid} ->
        if Process.alive?(pid) do
          case Keyword.get(opts, :health_check) do
            nil ->
              {:ok, pid}

            health_check_fun when is_function(health_check_fun, 1) ->
              timeout = Keyword.get(opts, :timeout, 5000)

              # Trap exits to handle health check crashes gracefully
              original_trap_exit = Process.flag(:trap_exit, true)

              try do
                task =
                  Task.async(fn ->
                    start_time = System.monotonic_time(:microsecond)
                    result = health_check_fun.(pid)
                    end_time = System.monotonic_time(:microsecond)
                    {end_time - start_time, result}
                  end)

                case Task.await(task, timeout) do
                  {time_us, :ok} ->
                    Logger.debug("Health check passed for #{inspect(service)} in #{time_us}μs")
                    {:ok, pid}

                  {time_us, {:ok, _result}} ->
                    Logger.debug("Health check passed for #{inspect(service)} in #{time_us}μs")
                    {:ok, pid}

                  {time_us, true} ->
                    Logger.debug("Health check passed for #{inspect(service)} in #{time_us}μs")
                    {:ok, pid}

                  {_time_us, false} ->
                    Logger.warning("Health check failed for #{inspect(service)}: returned false")
                    {:error, {:health_check_failed, false}}

                  {_time_us, error} ->
                    Logger.warning("Health check failed for #{inspect(service)}: #{inspect(error)}")
                    {:error, {:health_check_failed, error}}
                end
              catch
                :exit, {:timeout, _} ->
                  Logger.warning(
                    "Health check timed out for #{inspect(service)} after #{timeout}ms"
                  )

                  {:error, :health_check_timeout}

                :exit, {{%RuntimeError{} = error, _stacktrace}, {Task, :await, _}} ->
                  Logger.warning("Health check errored for #{inspect(service)}: #{inspect(error)}")
                  {:error, {:health_check_error, error}}

                :exit, {{error, _stacktrace}, {Task, :await, _}} ->
                  Logger.warning("Health check crashed for #{inspect(service)}: #{inspect(error)}")
                  {:error, {:health_check_crashed, error}}

                :exit, {reason, {Task, :await, _}} ->
                  Logger.warning("Health check crashed for #{inspect(service)}: #{inspect(reason)}")
                  {:error, {:health_check_crashed, reason}}

                :exit, reason ->
                  Logger.warning("Health check crashed for #{inspect(service)}: #{inspect(reason)}")
                  {:error, {:health_check_crashed, reason}}

                :error, reason ->
                  Logger.warning("Health check errored for #{inspect(service)}: #{inspect(reason)}")
                  {:error, {:health_check_error, reason}}
              after
                # Restore original trap_exit setting
                Process.flag(:trap_exit, original_trap_exit)
              end
          end
        else
          Logger.warning("Service #{inspect(service)} found but process is dead")
          {:error, :process_dead}
        end

      {:error, _reason} = error ->
        error
    end
  end

  @doc """
  Wait for a service to become available in a namespace.

  ## Parameters
  - `namespace`: The namespace to monitor
  - `service`: The service name to wait for
  - `timeout`: Maximum time to wait in milliseconds (default: 5000)

  ## Returns
  - `{:ok, pid}` if service becomes available
  - `{:error, :timeout}` if timeout is reached

  ## Examples

      iex> ServiceRegistry.wait_for_service(:production, :config_server, 1000)
      {:ok, #PID<0.123.0>}
  """
  @spec wait_for_service(namespace(), service_name(), pos_integer()) ::
          {:ok, pid()} | {:error, :timeout}
  def wait_for_service(namespace, service, timeout \\ 5000) do
    Logger.debug(
      "Waiting for service #{inspect(service)} in namespace #{inspect(namespace)} (timeout: #{timeout}ms)"
    )

    start_time = System.monotonic_time(:millisecond)
    wait_for_service_loop(namespace, service, timeout, start_time)
  end

  @doc """
  Get comprehensive service information for a namespace.

  ## Parameters
  - `namespace`: The namespace to analyze

  ## Returns
  - Map with detailed service information

  ## Examples

      iex> ServiceRegistry.get_service_info(:production)
      %{
        namespace: :production,
        services: %{
          config_server: %{pid: #PID<0.123.0>, alive: true, uptime_ms: 12345},
          event_store: %{pid: #PID<0.124.0>, alive: true, uptime_ms: 12344}
        },
        total_services: 2,
        healthy_services: 2
      }
  """
  @spec get_service_info(namespace()) :: %{
          namespace: namespace(),
          services: map(),
          total_services: non_neg_integer(),
          healthy_services: non_neg_integer()
        }
  def get_service_info(namespace) do
    # Only log debug info if debug_registry is explicitly enabled
    if Application.get_env(:foundation, :debug_registry, false) do
      Logger.debug("Getting service info for namespace #{inspect(namespace)}")
    end

    services_map = ProcessRegistry.get_all_services(namespace)

    service_details =
      Enum.into(services_map, %{}, fn {service, pid} ->
        {service, analyze_service(pid)}
      end)

    healthy_count =
      service_details
      |> Map.values()
      |> Enum.count(& &1.alive)

    %{
      namespace: namespace,
      services: service_details,
      total_services: map_size(service_details),
      healthy_services: healthy_count
    }
  end

  @doc """
  Cleanup services in a test namespace with detailed logging.

  ## Parameters
  - `test_ref`: The test reference used in namespace

  ## Returns
  - `:ok` after cleanup is complete

  ## Examples

      iex> test_ref = make_ref()
      iex> ServiceRegistry.cleanup_test_namespace(test_ref)
      :ok
  """
  @spec cleanup_test_namespace(reference()) :: :ok
  def cleanup_test_namespace(test_ref) do
    namespace = {:test, test_ref}

    # Only log if not in test mode
    test_mode = Application.get_env(:foundation, :test_mode, false)

    unless test_mode do
      Logger.info("Starting cleanup of test namespace #{inspect(namespace)}")
    end

    # Get service info before cleanup
    service_info = get_service_info(namespace)
    service_count = service_info.total_services

    if service_count > 0 do
      # Always log when there's actual work to do
      Logger.info("Cleaning up #{service_count} services in test namespace")
      ProcessRegistry.cleanup_test_namespace(test_ref)

      unless test_mode do
        Logger.info("Cleanup completed for test namespace #{inspect(namespace)}")
      end
    end

    :ok
  end

  @doc """
  Create a via tuple for service registration.

  Convenience wrapper around ProcessRegistry.via_tuple/2.

  ## Parameters
  - `namespace`: The namespace for the service
  - `service`: The service name

  ## Returns
  - Via tuple for GenServer registration
  """
  @spec via_tuple(namespace(), service_name()) ::
          {:via, Registry, {Foundation.ProcessRegistry, {namespace(), service_name()}}}
  def via_tuple(namespace, service) do
    ProcessRegistry.via_tuple(namespace, service)
  end

  ## Private Functions

  @spec wait_for_service_loop(namespace(), service_name(), pos_integer(), integer()) ::
          {:ok, pid()} | {:error, :timeout}
  defp wait_for_service_loop(namespace, service, timeout, start_time) do
    case lookup(namespace, service) do
      {:ok, pid} ->
        elapsed = System.monotonic_time(:millisecond) - start_time
        Logger.debug("Service #{inspect(service)} became available after #{elapsed}ms")
        {:ok, pid}

      {:error, _reason} ->
        elapsed = System.monotonic_time(:millisecond) - start_time

        if elapsed >= timeout do
          Logger.warning("Timeout waiting for service #{inspect(service)} after #{elapsed}ms")
          {:error, :timeout}
        else
          # Wait a short time before retrying
          Process.sleep(10)
          wait_for_service_loop(namespace, service, timeout, start_time)
        end
    end
  end

  @spec analyze_service(pid()) :: %{pid: pid(), alive: boolean(), uptime_ms: integer()}
  defp analyze_service(pid) do
    alive = Process.alive?(pid)

    uptime_ms =
      if alive do
        case Process.info(pid, :reductions) do
          {_, _} ->
            # Process is alive, calculate approximate uptime
            # This is a rough estimate based on when we checked
            System.monotonic_time(:millisecond)

          nil ->
            0
        end
      else
        0
      end

    %{
      pid: pid,
      alive: alive,
      uptime_ms: uptime_ms
    }
  end

  @spec create_service_not_found_error(namespace(), service_name()) :: Error.t()
  defp create_service_not_found_error(namespace, service) do
    Error.new(
      code: 5001,
      error_type: :service_not_found,
      message: "Service #{inspect(service)} not found in namespace #{inspect(namespace)}",
      severity: :medium,
      category: :system,
      subcategory: :discovery,
      context: %{
        namespace: namespace,
        service: service
      }
    )
  end

  @spec emit_lookup_telemetry(namespace(), service_name(), lookup_result(), integer()) :: :ok
  defp emit_lookup_telemetry(namespace, service, result, start_time) do
    duration = System.monotonic_time() - start_time

    result_status =
      case result do
        {:ok, _} -> :ok
        {:error, _} -> :error
      end

    :telemetry.execute(
      [:foundation, :foundation, :registry, :lookup],
      %{duration: duration},
      %{namespace: namespace, service: service, result: result_status}
    )
  rescue
    # Don't let telemetry failures break Registry operations
    _ -> :ok
  end

  @spec emit_registration_telemetry(namespace(), service_name(), registration_result()) :: :ok
  defp emit_registration_telemetry(namespace, service, result) do
    result_status =
      case result do
        :ok -> :ok
        {:error, _} -> :error
      end

    :telemetry.execute(
      [:foundation, :foundation, :registry, :register],
      %{count: 1},
      %{namespace: namespace, service: service, result: result_status}
    )
  rescue
    _ -> :ok
  end
end
</file>

<file path="foundation/telemetry.ex">
defmodule Foundation.Telemetry do
  @moduledoc """
  Public API for telemetry and metrics collection.

  Thin wrapper around TelemetryService that provides a clean, documented interface.
  All business logic is delegated to the service layer.
  """

  alias Foundation.Services.TelemetryService
  alias Foundation.Types.Error

  @type event_name :: [atom()]
  @type measurements :: map()
  @type metadata :: map()
  @type metric_value :: number()

  @doc """
  Initialize the telemetry service.

  ## Examples

      iex> Foundation.Telemetry.initialize()
      :ok
  """
  @spec initialize() :: :ok | {:error, Error.t()}
  def initialize() do
    TelemetryService.initialize()
  end

  @doc """
  Get telemetry service status.

  ## Examples

      iex> Foundation.Telemetry.status()
      {:ok, %{status: :running, uptime: 12345}}
  """
  @spec status() :: {:ok, map()} | {:error, Error.t()}
  def status() do
    TelemetryService.status()
  end

  @doc """
  Execute telemetry event with measurements.

  ## Examples

      iex> Foundation.Telemetry.execute(
      ...>   [:foundation, :function, :call],
      ...>   %{duration: 1000},
      ...>   %{module: MyModule, function: :my_func}
      ...> )
      :ok
  """
  @spec execute(event_name(), measurements(), metadata()) :: :ok
  defdelegate execute(event_name, measurements, metadata), to: TelemetryService

  @doc """
  Measure execution time and emit results.

  ## Examples

      iex> result = Foundation.Telemetry.measure(
      ...>   [:foundation, :query, :execution],
      ...>   %{query_type: :complex},
      ...>   fn -> expensive_operation() end
      ...> )
      :operation_result
  """
  @spec measure(event_name(), metadata(), (-> result)) :: result when result: var
  defdelegate measure(event_name, metadata, fun), to: TelemetryService

  @doc """
  Emit a counter metric.

  ## Examples

      iex> Foundation.Telemetry.emit_counter(
      ...>   [:foundation, :events, :processed],
      ...>   %{event_type: :function_entry}
      ...> )
      :ok
  """
  @spec emit_counter(event_name(), metadata()) :: :ok
  defdelegate emit_counter(event_name, metadata), to: TelemetryService

  @doc """
  Emit a gauge metric.

  ## Examples

      iex> Foundation.Telemetry.emit_gauge(
      ...>   [:foundation, :memory, :usage],
      ...>   1024000,
      ...>   %{unit: :bytes}
      ...> )
      :ok
  """
  @spec emit_gauge(event_name(), metric_value(), metadata()) :: :ok
  defdelegate emit_gauge(event_name, value, metadata), to: TelemetryService

  @doc """
  Get collected metrics.

  ## Examples

      iex> Foundation.Telemetry.get_metrics()
      {:ok, %{
        [:foundation, :function, :call] => %{
          timestamp: 123456789,
          measurements: %{duration: 1500},
          count: 42
        }
      }}
  """
  @spec get_metrics() :: {:ok, map()} | {:error, Error.t()}
  defdelegate get_metrics(), to: TelemetryService

  @doc """
  Attach event handlers for specific events.

  ## Examples

      iex> events = [[:foundation, :function, :call], [:foundation, :query, :execution]]
      iex> Foundation.Telemetry.attach_handlers(events)
      :ok
  """
  @spec attach_handlers([event_name()]) :: :ok | {:error, Error.t()}
  defdelegate attach_handlers(event_names), to: TelemetryService

  @doc """
  Detach event handlers.

  ## Examples

      iex> events = [[:foundation, :function, :call]]
      iex> Foundation.Telemetry.detach_handlers(events)
      :ok
  """
  @spec detach_handlers([event_name()]) :: :ok
  defdelegate detach_handlers(event_names), to: TelemetryService

  @doc """
  Check if telemetry is available.

  ## Examples

      iex> Foundation.Telemetry.available?()
      true
  """
  @spec available?() :: boolean()
  defdelegate available?(), to: TelemetryService

  @doc """
  Time a function execution and emit telemetry.

  Convenience function that automatically creates appropriate event names.

  ## Examples

      iex> Foundation.Telemetry.time_function(
      ...>   MyModule, :expensive_function,
      ...>   fn -> MyModule.expensive_function(arg1, arg2) end
      ...> )
      :function_result
  """
  @spec time_function(module(), atom(), (-> result)) :: result when result: var
  def time_function(module, function, fun) when is_atom(module) and is_atom(function) do
    event_name = [:foundation, :function, :execution]
    metadata = %{module: module, function: function}
    measure(event_name, metadata, fun)
  end

  @doc """
  Emit a performance metric with automatic categorization.

  ## Examples

      iex> Foundation.Telemetry.emit_performance(
      ...>   :query_duration, 1500, %{query_type: :complex}
      ...> )
      :ok
  """
  @spec emit_performance(atom(), metric_value(), metadata()) :: :ok
  def emit_performance(metric_name, value, metadata \\ %{}) when is_atom(metric_name) do
    event_name = [:foundation, :performance, metric_name]
    emit_gauge(event_name, value, metadata)
  end

  @doc """
  Emit a system event counter.

  ## Examples

      iex> Foundation.Telemetry.emit_system_event(:error, %{error_type: :validation})
      :ok
  """
  @spec emit_system_event(atom(), metadata()) :: :ok
  def emit_system_event(event_type, metadata \\ %{}) when is_atom(event_type) do
    event_name = [:foundation, :system, event_type]
    emit_counter(event_name, metadata)
  end

  @doc """
  Get metrics for a specific event pattern.

  ## Examples

      iex> Foundation.Telemetry.get_metrics_for([:foundation, :function])
      {:ok, %{...}}  # Only metrics matching the pattern
  """
  @spec get_metrics_for(event_name()) :: {:ok, map()} | {:error, Error.t()}
  def get_metrics_for(event_pattern) when is_list(event_pattern) do
    case get_metrics() do
      {:ok, all_metrics} ->
        filtered_metrics =
          all_metrics
          |> Enum.filter(fn {event_name, _} ->
            List.starts_with?(event_name, event_pattern)
          end)
          |> Map.new()

        {:ok, filtered_metrics}

      {:error, _} = error ->
        error
    end
  end
end

# defmodule Foundation.Telemetry do
#   @moduledoc """
#   Telemetry and metrics collection for Foundation layer.

#   Provides standardized telemetry events and metrics collection
#   for monitoring Foundation performance and health.
#   """

#   require Logger
#   # , Error} #, ErrorContext}
#   alias Foundation.{Utils}

#   @telemetry_events [
#     # Configuration events
#     [:foundation, :config, :get],
#     [:foundation, :config, :update],
#     [:foundation, :config, :validate],

#     # Event system events
#     [:foundation, :events, :create],
#     [:foundation, :events, :serialize],
#     [:foundation, :events, :deserialize],

#     # Performance events
#     [:foundation, :performance, :measurement],
#     [:foundation, :performance, :memory_usage]
#   ]

#   ## Public API

#   @spec initialize() :: :ok
#   def initialize do
#     attach_default_handlers()
#     Logger.debug("Foundation.Telemetry initialized")
#     :ok
#   end

#   @spec status() :: :ok
#   def status, do: :ok

#   @spec measure_event([atom(), ...], map(), (-> t)) :: t when t: var
#   def measure_event(event_name, metadata \\ %{}, fun) when is_function(fun, 0) do
#     start_time = Utils.monotonic_timestamp()

#     # Don't wrap in ErrorContext - let exceptions propagate
#     try do
#       result = fun.()

#       end_time = Utils.monotonic_timestamp()
#       duration = end_time - start_time

#       measurements = %{duration: duration, timestamp: end_time}
#       :telemetry.execute(event_name, measurements, metadata)

#       result
#     rescue
#       exception ->
#         # Still measure the duration even if it failed
#         end_time = Utils.monotonic_timestamp()
#         duration = end_time - start_time

#         measurements = %{duration: duration, timestamp: end_time}
#         error_metadata = Map.put(metadata, :exception, exception)

#         # Emit both the normal event and an error event
#         :telemetry.execute(event_name, measurements, error_metadata)
#         emit_error_event(event_name, metadata, {:error, exception})

#         # Re-raise the exception so the test can catch it
#         reraise exception, __STACKTRACE__
#     end
#   end

#   @spec emit_counter([atom(), ...], map()) :: :ok
#   def emit_counter(event_name, metadata \\ %{}) do
#     measurements = %{count: 1, timestamp: Utils.monotonic_timestamp()}
#     :telemetry.execute(event_name, measurements, metadata)
#   end

#   @spec emit_gauge(list(atom()), number(), map()) :: :ok
#   def emit_gauge(event_name, value, metadata \\ %{}) do
#     measurements = %{value: value, timestamp: Utils.monotonic_timestamp()}
#     :telemetry.execute(event_name, measurements, metadata)
#   end

#   @spec get_metrics() :: %{
#           foundation: %{
#             uptime_ms: integer(),
#             memory_usage: non_neg_integer(),
#             process_count: non_neg_integer()
#           },
#           system: %{
#             timestamp: integer(),
#             process_count: non_neg_integer(),
#             total_memory: non_neg_integer(),
#             scheduler_count: pos_integer(),
#             otp_release: binary()
#           }
#         }
#   def get_metrics do
#     %{
#       foundation: %{
#         uptime_ms: System.monotonic_time(:millisecond),
#         memory_usage: :erlang.memory(:total),
#         process_count: :erlang.system_info(:process_count)
#       },
#       system: Utils.system_stats()
#     }
#   end

#   ## Private Functions

#   @spec attach_default_handlers() :: :ok
#   defp attach_default_handlers do
#     # Attach a default handler for debugging in development
#     if Application.get_env(:foundation, :dev, []) |> Keyword.get(:debug_mode, false) do
#       :telemetry.attach_many(
#         "foundation-debug-handler",
#         @telemetry_events,
#         &handle_debug_event/4,
#         %{}
#       )
#     end

#     :ok
#   end

#   @spec handle_debug_event(list(atom()), map(), map(), map()) :: :ok
#   defp handle_debug_event(event_name, measurements, metadata, _config) do
#     Logger.debug("""
#     Foundation Telemetry Event:
#       Event: #{inspect(event_name)}
#       Measurements: #{inspect(measurements)}
#       Metadata: #{inspect(metadata)}
#     """)
#   end

#   @spec emit_error_event([atom(), ...], map(), {:error, struct()}) :: :ok
#   defp emit_error_event(event_name, metadata, {:error, err}) do
#     error_metadata =
#       if err.__struct__ == Foundation.Error do
#         Map.merge(metadata, %{
#           error_code: err.code,
#           error_message: err.message
#         })
#       else
#         Map.merge(metadata, %{
#           error_type: :external_error,
#           error_message: inspect(err)
#         })
#       end

#     measurements = %{error_count: 1, timestamp: Utils.monotonic_timestamp()}
#     error_event_name = event_name ++ [:error]
#     :telemetry.execute(error_event_name, measurements, error_metadata)
#   end
# end
</file>

<file path="foundation/utils.ex">
defmodule Foundation.Utils do
  @moduledoc """
  Pure utility functions for the Foundation layer.

  Contains helper functions that are used across the Foundation layer.
  All functions are pure and have no side effects.
  """

  import Bitwise

  @doc """
  Generate a unique ID using monotonic time and randomness.

  ## Examples

      iex> id1 = Foundation.Utils.generate_id()
      iex> id2 = Foundation.Utils.generate_id()
      iex> id1 != id2
      true
  """
  @spec generate_id() :: pos_integer()
  def generate_id do
    # Use a combination of unique_integer and make_ref for absolute uniqueness
    # This approach guarantees uniqueness even under extreme concurrency
    unique_int = System.unique_integer([:positive])
    ref_hash = make_ref() |> :erlang.ref_to_list() |> :erlang.phash2()

    # Combine both for maximum uniqueness guarantee
    # Use bit shifting to avoid collisions between components
    unique_int * 1_000_000 + abs(ref_hash) + 1
  end

  @doc """
  Get monotonic timestamp in milliseconds.

  ## Examples

      iex> timestamp = Foundation.Utils.monotonic_timestamp()
      iex> is_integer(timestamp)
      true
  """
  @spec monotonic_timestamp() :: integer()
  def monotonic_timestamp do
    System.monotonic_time(:millisecond)
  end

  @doc """
  Generate a correlation ID string in UUID v4 format.

  ## Examples

      iex> correlation_id = Foundation.Utils.generate_correlation_id()
      iex> String.length(correlation_id)
      36
      iex> String.match?(correlation_id, ~r/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)
      true
  """
  @spec generate_correlation_id() :: String.t()
  def generate_correlation_id do
    # Generate UUID v4 format (36 characters)
    <<u0::32, u1::16, u2::16, u3::16, u4::48>> = :crypto.strong_rand_bytes(16)

    # Set version (4) and variant bits
    u2_v4 = (u2 &&& 0x0FFF) ||| 0x4000
    u3_var = (u3 &&& 0x3FFF) ||| 0x8000

    :io_lib.format(
      "~8.16.0b-~4.16.0b-~4.16.0b-~4.16.0b-~12.16.0b",
      [u0, u1, u2_v4, u3_var, u4]
    )
    |> IO.iodata_to_binary()
  end

  @doc """
  Truncate data if it's too large for storage.

  ## Examples

      iex> small_data = [1, 2, 3]
      iex> Foundation.Utils.truncate_if_large(small_data)
      [1, 2, 3]

      iex> large_data = String.duplicate("x", 100_000)
      iex> result = Foundation.Utils.truncate_if_large(large_data)
      iex> is_map(result) and Map.has_key?(result, :truncated)
      true
  """
  @spec truncate_if_large(term()) :: term()
  def truncate_if_large(data) do
    truncate_if_large(data, 10_000)
  end

  @doc """
  Truncate data if it's too large for storage with custom size limit.

  ## Examples

      iex> small_data = [1, 2, 3]
      iex> Foundation.Utils.truncate_if_large(small_data, 1000)
      [1, 2, 3]

      iex> large_data = String.duplicate("x", 2000)
      iex> result = Foundation.Utils.truncate_if_large(large_data, 1000)
      iex> is_map(result) and Map.has_key?(result, :truncated)
      true
  """
  @spec truncate_if_large(term(), pos_integer()) :: term()
  def truncate_if_large(data, max_size) do
    try do
      size = :erlang.external_size(data)

      if size > max_size do
        %{
          truncated: true,
          original_size: size,
          preview: truncate_preview(data),
          timestamp: DateTime.utc_now()
        }
      else
        data
      end
    rescue
      _ -> data
    end
  end

  @doc """
  Calculate the deep size of a term.

  ## Examples

      iex> size = Foundation.Utils.deep_size(%{a: 1, b: [1, 2, 3]})
      iex> is_integer(size) and size > 0
      true
  """
  @spec deep_size(term()) :: non_neg_integer()
  def deep_size(term) do
    try do
      :erlang.external_size(term)
    rescue
      _ -> 0
    end
  end

  @doc """
  Safely convert a term to string representation.

  ## Examples

      iex> Foundation.Utils.safe_inspect(%{key: "value"})
      "%{key: \"value\"}"

      iex> Foundation.Utils.safe_inspect(:atom)
      ":atom"
  """
  @spec safe_inspect(term()) :: String.t()
  def safe_inspect(term) do
    try do
      inspect(term, limit: 100, printable_limit: 100)
    rescue
      _ -> "<uninspectable>"
    end
  end

  @doc """
  Merge two maps recursively.

  ## Examples

      iex> map1 = %{a: %{b: 1}, c: 2}
      iex> map2 = %{a: %{d: 3}, e: 4}
      iex> Foundation.Utils.deep_merge(map1, map2)
      %{a: %{b: 1, d: 3}, c: 2, e: 4}
  """
  @spec deep_merge(map(), map()) :: map()
  def deep_merge(left, right) when is_map(left) and is_map(right) do
    Map.merge(left, right, fn _key, left_val, right_val ->
      deep_merge(left_val, right_val)
    end)
  end

  def deep_merge(_left, right), do: right

  @doc """
  Get a nested value from a map with a default.

  ## Examples

      iex> data = %{a: %{b: %{c: 42}}}
      iex> Foundation.Utils.get_nested(data, [:a, :b, :c], 0)
      42

      iex> Foundation.Utils.get_nested(data, [:x, :y], "default")
      "default"
  """
  @spec get_nested(map(), [atom()], term()) :: term()
  def get_nested(map, path, default \\ nil)
  def get_nested(map, [], _default), do: map

  def get_nested(map, [key | rest], default) when is_map(map) do
    case Map.get(map, key) do
      nil -> default
      value -> get_nested(value, rest, default)
    end
  end

  def get_nested(_not_map, _path, default), do: default

  @doc """
  Put a nested value in a map.

  ## Examples

      iex> data = %{}
      iex> Foundation.Utils.put_nested(data, [:a, :b, :c], 42)
      %{a: %{b: %{c: 42}}}
  """
  @spec put_nested(map(), [atom()], term()) :: map()
  def put_nested(_map, [], value), do: value

  def put_nested(map, [key], value) when is_map(map) do
    Map.put(map, key, value)
  end

  def put_nested(map, [key | rest], value) when is_map(map) do
    nested_map = Map.get(map, key, %{})
    Map.put(map, key, put_nested(nested_map, rest, value))
  end

  @doc """
  Sanitize a string for safe logging/display.

  ## Examples

      iex> Foundation.Utils.sanitize_string("hello\\nworld\\ttab")
      "hello world tab"
  """
  @spec sanitize_string(String.t()) :: String.t()
  def sanitize_string(str) when is_binary(str) do
    str
    |> String.replace(~r/[\r\n\t]/, " ")
    |> String.replace(~r/\s+/, " ")
    |> String.trim()
  end

  @doc """
  Convert atom keys to string keys recursively.

  ## Examples

      iex> data = %{key: %{nested: "value"}}
      iex> Foundation.Utils.atomize_keys(data)
      %{key: %{nested: "value"}}
  """
  @spec atomize_keys(map()) :: map()
  def atomize_keys(map) when is_map(map) do
    Map.new(map, fn
      {key, value} when is_binary(key) ->
        try do
          {String.to_existing_atom(key), atomize_keys(value)}
        rescue
          ArgumentError -> {key, atomize_keys(value)}
        end

      {key, value} ->
        {key, atomize_keys(value)}
    end)
  end

  def atomize_keys(list) when is_list(list) do
    Enum.map(list, &atomize_keys/1)
  end

  def atomize_keys(value), do: value

  @doc """
  Convert atom keys to string keys recursively.

  ## Examples

      iex> data = %{key: %{nested: "value"}}
      iex> Foundation.Utils.stringify_keys(data)
      %{"key" => %{"nested" => "value"}}
  """
  @spec stringify_keys(map()) :: map()
  def stringify_keys(map) when is_map(map) do
    Map.new(map, fn
      {key, value} when is_atom(key) -> {Atom.to_string(key), stringify_keys(value)}
      {key, value} -> {key, stringify_keys(value)}
    end)
  end

  def stringify_keys(list) when is_list(list) do
    Enum.map(list, &stringify_keys/1)
  end

  def stringify_keys(value), do: value

  @doc """
  Retry a function with exponential backoff.

  ## Examples

      iex> result = Foundation.Utils.retry(fn -> :ok end, max_attempts: 3)
      {:ok, :ok}

      iex> result = Foundation.Utils.retry(fn -> {:error, :failed} end, max_attempts: 2)
      {:error, :max_attempts_exceeded}
  """
  @spec retry((-> any()), Keyword.t()) :: {:error, :max_attempts_exceeded} | {:ok, any()}
  def retry(fun, opts \\ []) when is_function(fun, 0) do
    max_attempts = Keyword.get(opts, :max_attempts, 3)
    base_delay = Keyword.get(opts, :base_delay, 100)
    max_delay = Keyword.get(opts, :max_delay, 5000)

    do_retry(fun, 1, max_attempts, base_delay, max_delay)
  end

  @doc """
  Check if a term is blank (nil, empty string, empty list, etc.).

  ## Examples

      iex> Foundation.Utils.blank?(nil)
      true

      iex> Foundation.Utils.blank?("")
      true

      iex> Foundation.Utils.blank?("hello")
      false
  """
  @spec blank?(term()) :: boolean()
  def blank?(nil), do: true
  def blank?(""), do: true
  def blank?(str) when is_binary(str), do: String.trim(str) == ""
  def blank?([]), do: true
  def blank?(map) when is_map(map), do: map_size(map) == 0
  def blank?(_), do: false

  @doc """
  Check if a term is present (not blank).

  ## Examples

      iex> Foundation.Utils.present?("hello")
      true

      iex> Foundation.Utils.present?(nil)
      false
  """
  @spec present?(term()) :: boolean()
  def present?(term), do: not blank?(term)

  @doc """
  Format duration in nanoseconds to human readable string.

  ## Examples

      iex> Foundation.Utils.format_duration(1_500_000_000)
      "1.5s"

      iex> Foundation.Utils.format_duration(2_500_000)
      "2.5ms"
  """
  @spec format_duration(non_neg_integer()) :: String.t()
  def format_duration(nanoseconds) when is_integer(nanoseconds) and nanoseconds >= 0 do
    cond do
      nanoseconds >= 1_000_000_000 ->
        seconds = nanoseconds / 1_000_000_000
        "#{:erlang.float_to_binary(seconds, decimals: 1)}s"

      nanoseconds >= 1_000_000 ->
        milliseconds = nanoseconds / 1_000_000
        "#{:erlang.float_to_binary(milliseconds, decimals: 1)}ms"

      nanoseconds >= 1_000 ->
        microseconds = nanoseconds / 1_000
        "#{:erlang.float_to_binary(microseconds, decimals: 1)}μs"

      true ->
        "#{nanoseconds}ns"
    end
  end

  @doc """
  Get current wall clock timestamp.

  ## Examples

      iex> timestamp = Foundation.Utils.wall_timestamp()
      iex> is_integer(timestamp)
      true
  """
  @spec wall_timestamp() :: integer()
  def wall_timestamp do
    System.system_time(:nanosecond)
  end

  @doc """
  Measure execution time of a function in microseconds.

  ## Examples

      iex> {result, duration} = Foundation.Utils.measure(fn -> :timer.sleep(10); :ok end)
      iex> result
      :ok
      iex> duration > 10_000  # At least 10ms in microseconds
      true
  """
  @spec measure((-> result)) :: {result, non_neg_integer()} when result: any()
  def measure(func) when is_function(func, 0) do
    start = System.monotonic_time(:microsecond)
    result = func.()
    stop = System.monotonic_time(:microsecond)
    {result, stop - start}
  end

  @doc """
  Measure memory consumption before and after a function execution.

  ## Examples

      iex> {result, {before, after, diff}} = Foundation.Utils.measure_memory(fn -> "test" end)
      iex> result
      "test"
      iex> is_integer(before) and is_integer(after) and is_integer(diff)
      true
  """
  @spec measure_memory((-> result)) :: {result, {non_neg_integer(), non_neg_integer(), integer()}}
        when result: any()
  def measure_memory(func) when is_function(func, 0) do
    :erlang.garbage_collect()
    before_memory = :erlang.memory(:total)
    result = func.()
    :erlang.garbage_collect()
    after_memory = :erlang.memory(:total)
    {result, {before_memory, after_memory, after_memory - before_memory}}
  end

  @doc """
  Format byte size into human-readable string.

  ## Examples

      iex> Foundation.Utils.format_bytes(1024)
      "1.0 KB"
      
      iex> Foundation.Utils.format_bytes(1536)
      "1.5 KB"
      
      iex> Foundation.Utils.format_bytes(1048576)
      "1.0 MB"
  """
  @spec format_bytes(non_neg_integer()) :: String.t()
  def format_bytes(bytes) when is_integer(bytes) and bytes >= 0 do
    cond do
      bytes < 1024 -> "#{bytes} B"
      bytes < 1024 * 1024 -> "#{Float.round(bytes / 1024, 1)} KB"
      bytes < 1024 * 1024 * 1024 -> "#{Float.round(bytes / (1024 * 1024), 1)} MB"
      true -> "#{Float.round(bytes / (1024 * 1024 * 1024), 1)} GB"
    end
  end

  @doc """
  Returns process statistics for the current process.

  ## Examples

      iex> stats = Foundation.Utils.process_stats()
      iex> Map.has_key?(stats, :memory)
      true
      iex> Map.has_key?(stats, :message_queue_len)
      true
  """
  @spec process_stats() :: %{
          garbage_collection: any(),
          memory: any(),
          message_queue_len: any(),
          reductions: any(),
          status: any()
        }
  def process_stats do
    info = Process.info(self())

    %{
      memory: Keyword.get(info, :memory, 0),
      message_queue_len: Keyword.get(info, :message_queue_len, 0),
      reductions: Keyword.get(info, :reductions, 0),
      garbage_collection: Keyword.get(info, :garbage_collection, %{}),
      status: Keyword.get(info, :status, :unknown)
    }
  end

  @doc """
  Returns system statistics.

  ## Examples

      iex> stats = Foundation.Utils.system_stats()
      iex> Map.has_key?(stats, :process_count)
      true
      iex> Map.has_key?(stats, :memory)
      true
  """
  @spec system_stats() :: %{
          atom_count: any(),
          memory: [
            {:atom
             | :atom_used
             | :binary
             | :code
             | :ets
             | :processes
             | :processes_used
             | :system
             | :total, non_neg_integer()},
            ...
          ],
          process_count: non_neg_integer(),
          scheduler_count: pos_integer(),
          scheduler_online: pos_integer()
        }
  def system_stats do
    %{
      process_count: :erlang.system_info(:process_count),
      atom_count: :erlang.system_info(:atom_count),
      memory: :erlang.memory(),
      scheduler_count: :erlang.system_info(:schedulers),
      scheduler_online: :erlang.system_info(:schedulers_online)
    }
  end

  @doc """
  Check if a value is a valid positive integer.

  ## Examples

      iex> Foundation.Utils.valid_positive_integer?(42)
      true
      
      iex> Foundation.Utils.valid_positive_integer?(0)
      false
      
      iex> Foundation.Utils.valid_positive_integer?(-1)
      false
      
      iex> Foundation.Utils.valid_positive_integer?("42")
      false
  """
  @spec valid_positive_integer?(term()) :: boolean()
  def valid_positive_integer?(value) do
    is_integer(value) and value > 0
  end

  ## Private Functions

  defp truncate_preview(data) when is_binary(data) do
    String.slice(data, 0, 100) <> "..."
  end

  defp truncate_preview(data) when is_list(data) do
    data
    |> Enum.take(10)
    |> Enum.map(&safe_inspect/1)
  end

  defp truncate_preview(data) when is_map(data) do
    data
    |> Enum.take(5)
    |> Map.new()
  end

  defp truncate_preview(data) do
    safe_inspect(data)
  end

  defp do_retry(fun, attempt, max_attempts, base_delay, max_delay) do
    case fun.() do
      {:ok, result} ->
        {:ok, result}

      :ok ->
        {:ok, :ok}

      {:error, _} when attempt < max_attempts ->
        delay = min(base_delay * :math.pow(2, attempt - 1), max_delay) |> round()
        Process.sleep(delay)
        do_retry(fun, attempt + 1, max_attempts, base_delay, max_delay)

      {:error, _reason} ->
        {:error, :max_attempts_exceeded}

      result ->
        {:ok, result}
    end
  end
end

# defmodule Foundation.Utils do
#   @moduledoc """
#   Core utility functions for Foundation layer.

#   Provides essential utilities for ID generation, time measurement,
#   data inspection, and performance monitoring.
#   """

#   import Bitwise
#   alias Foundation.{Types}

#   @type measurement_result(t) :: {t, non_neg_integer()}

#   ## ID Generation

#   @spec generate_id() :: Types.event_id()
#   def generate_id do
#     # Use a combination of timestamp and random value for uniqueness
#     timestamp = System.monotonic_time(:nanosecond)
#     random = :rand.uniform(1_000_000)

#     # Combine timestamp and random for globally unique ID
#     # Use abs to handle negative monotonic time
#     abs(timestamp) * 1_000_000 + random
#   end

#   @spec generate_correlation_id() :: Types.correlation_id()
#   def generate_correlation_id do
#     # Generate UUID v4 format
#     <<u0::32, u1::16, u2::16, u3::16, u4::48>> = :crypto.strong_rand_bytes(16)

#     # Set version (4) and variant bits
#     u2_v4 = (u2 &&& 0x0FFF) ||| 0x4000
#     u3_var = (u3 &&& 0x3FFF) ||| 0x8000

#     :io_lib.format(
#       "~8.16.0b-~4.16.0b-~4.16.0b-~4.16.0b-~12.16.0b",
#       [u0, u1, u2_v4, u3_var, u4]
#     )
#     |> IO.iodata_to_binary()
#   end

#   @spec id_to_timestamp(Types.event_id()) :: Types.timestamp()
#   def id_to_timestamp(id) when is_integer(id) do
#     # Extract timestamp component from ID
#     div(id, 1_000_000)
#   end

#   ## Time Utilities

#   @spec monotonic_timestamp() :: Types.timestamp()
#   def monotonic_timestamp do
#     System.monotonic_time(:nanosecond)
#   end

#   @spec wall_timestamp() :: Types.timestamp()
#   def wall_timestamp do
#     System.os_time(:nanosecond)
#   end

#   @spec format_timestamp(Types.timestamp()) :: String.t()
#   def format_timestamp(timestamp_ns) when is_integer(timestamp_ns) do
#     timestamp_us = div(timestamp_ns, 1_000)
#     datetime = DateTime.from_unix!(timestamp_us, :microsecond)

#     # Format with nanosecond precision
#     nanoseconds = rem(timestamp_ns, 1_000_000)
#     formatted_base = DateTime.to_iso8601(datetime)

#     # Replace microseconds with full nanosecond precision
#     String.replace(formatted_base, ~r/\.\d{6}/, ".#{:io_lib.format("~6..0B", [nanoseconds])}")
#   end

#   ## Measurement

#   @spec measure((-> t)) :: measurement_result(t) when t: var
#   def measure(fun) when is_function(fun, 0) do
#     start_time = monotonic_timestamp()
#     result = fun.()
#     end_time = monotonic_timestamp()

#     {result, end_time - start_time}
#   end

#   @spec measure_memory((-> t)) :: {t, {non_neg_integer(), non_neg_integer(), integer()}} when t: var
#   def measure_memory(fun) when is_function(fun, 0) do
#     memory_before = :erlang.memory(:total)
#     result = fun.()
#     memory_after = :erlang.memory(:total)

#     {result, {memory_before, memory_after, memory_after - memory_before}}
#   end

#   ## Data Inspection

#   @spec safe_inspect(term(), keyword()) :: String.t()
#   def safe_inspect(term, opts \\ []) do
#     limit = Keyword.get(opts, :limit, 50)
#     inspect(term, limit: limit, printable_limit: 100, pretty: true)
#   end

#   @spec truncate_if_large(term(), non_neg_integer()) :: term() | Types.truncated_data()
#   def truncate_if_large(term, size_limit \\ 1000) do
#     estimated_size = term_size(term)

#     if estimated_size <= size_limit do
#       term
#     else
#       type_hint = get_type_hint(term)
#       {:truncated, estimated_size, type_hint}
#     end
#   end

#   @spec term_size(term()) :: non_neg_integer()
#   def term_size(term) do
#     :erlang.external_size(term)
#   end

#   ## Process and System Stats

#   @spec process_stats(pid()) :: map()
#   def process_stats(pid \\ self()) do
#     case Process.info(pid, [:memory, :reductions, :message_queue_len]) do
#       nil ->
#         %{error: :process_not_found, timestamp: monotonic_timestamp()}

#       info ->
#         info
#         |> Keyword.put(:timestamp, monotonic_timestamp())
#         |> Enum.into(%{})
#     end
#   end

#   @spec system_stats() :: %{
#           timestamp: integer(),
#           process_count: non_neg_integer(),
#           total_memory: non_neg_integer(),
#           scheduler_count: pos_integer(),
#           otp_release: binary()
#         }
#   def system_stats do
#     %{
#       timestamp: monotonic_timestamp(),
#       process_count: :erlang.system_info(:process_count),
#       total_memory: :erlang.memory(:total),
#       scheduler_count: :erlang.system_info(:schedulers),
#       otp_release: :erlang.system_info(:otp_release) |> List.to_string()
#     }
#   end

#   ## Formatting

#   @spec format_bytes(non_neg_integer()) :: String.t()
#   def format_bytes(bytes) when is_integer(bytes) and bytes >= 0 do
#     cond do
#       bytes < 1024 -> "#{bytes} B"
#       bytes < 1024 * 1024 -> "#{Float.round(bytes / 1024, 1)} KB"
#       bytes < 1024 * 1024 * 1024 -> "#{Float.round(bytes / (1024 * 1024), 1)} MB"
#       true -> "#{Float.round(bytes / (1024 * 1024 * 1024), 1)} GB"
#     end
#   end

#   @spec format_duration(non_neg_integer()) :: String.t()
#   def format_duration(nanoseconds) when is_integer(nanoseconds) and nanoseconds >= 0 do
#     cond do
#       nanoseconds < 1_000 -> "#{nanoseconds} ns"
#       nanoseconds < 1_000_000 -> "#{Float.round(nanoseconds / 1_000, 1)} μs"
#       nanoseconds < 1_000_000_000 -> "#{Float.round(nanoseconds / 1_000_000, 1)} ms"
#       true -> "#{Float.round(nanoseconds / 1_000_000_000, 1)} s"
#     end
#   end

#   ## Validation

#   @spec valid_positive_integer?(term()) :: boolean()
#   def valid_positive_integer?(value) do
#     is_integer(value) and value > 0
#   end

#   @spec valid_percentage?(term()) :: boolean()
#   def valid_percentage?(value) do
#     is_number(value) and value >= 0 and value <= 1
#   end

#   @spec valid_pid?(term()) :: boolean()
#   def valid_pid?(value) do
#     is_pid(value) and Process.alive?(value)
#   end

#   ## Private Functions

#   @spec get_type_hint(term()) :: String.t()
#   defp get_type_hint(term) do
#     cond do
#       is_binary(term) -> "binary data"
#       is_list(term) -> "list with #{length(term)} elements"
#       is_map(term) -> "map with #{map_size(term)} keys"
#       is_tuple(term) -> "tuple with #{tuple_size(term)} elements"
#       true -> "#{inspect(term.__struct__ || :unknown)} data"
#     end
#   rescue
#     _ -> "complex data structure"
#   end
# end
</file>

<file path="foundation.ex">
defmodule Foundation do
  @moduledoc """
  Main public API for Foundation layer.

  Provides unified access to all Foundation services including configuration,
  events, telemetry, and error handling. This is the main entry point for
  interacting with the Foundation layer.
  """

  alias Foundation.{Config, Events, Telemetry, ErrorContext}
  alias Foundation.Types.Error

  @doc """
  Initialize the entire Foundation layer.

  Starts all Foundation services in the correct order and ensures
  they are properly configured and ready for use.

  ## Examples

      iex> Foundation.initialize()
      :ok

      iex> Foundation.initialize(config: [debug_mode: true])
      :ok
  """
  @spec initialize(keyword()) :: :ok | {:error, Error.t()}
  def initialize(opts \\ []) do
    with :ok <- Config.initialize(Keyword.get(opts, :config, [])),
         :ok <- Events.initialize(),
         :ok <- Telemetry.initialize() do
      :ok
    else
      {:error, _} = error -> error
    end
  end

  @doc """
  Get the status of all Foundation services.

  Returns a map containing the status of each core service.

  ## Examples

      iex> Foundation.status()
      {:ok, %{
        config: %{status: :running, uptime_ms: 12345},
        events: %{status: :running, events_count: 1000},
        telemetry: %{status: :running, metrics_count: 50}
      }}
  """
  @spec status() :: {:ok, map()} | {:error, Error.t()}
  def status() do
    with {:ok, config_status} <- Config.status(),
         {:ok, events_status} <- Events.status(),
         {:ok, telemetry_status} <- Telemetry.status() do
      {:ok,
       %{
         config: config_status,
         events: events_status,
         telemetry: telemetry_status
       }}
    else
      {:error, _} = error -> error
    end
  end

  @doc """
  Check if all Foundation services are available and ready.

  ## Examples

      iex> Foundation.available?()
      true
  """
  @spec available?() :: boolean()
  def available?() do
    Config.available?() and Events.available?() and Telemetry.available?()
  end

  @doc """
  Get version information for the Foundation layer.

  ## Examples

      iex> Foundation.version()
      "0.1.0"
  """
  @spec version() :: String.t()
  def version() do
    Application.spec(:foundation, :vsn) |> to_string()
  end

  @doc """
  Start Foundation (alias for initialize/1).
  """
  @spec start_link(keyword()) :: {:ok, pid()}
  def start_link(opts \\ []) do
    context = ErrorContext.new(__MODULE__, :start_link, metadata: %{opts: opts})

    ErrorContext.with_context(context, fn ->
      :ok = initialize(opts)
      {:ok, self()}
    end)
  end

  @doc """
  Shutdown the Foundation layer gracefully.

  This stops all Foundation services in reverse order and ensures
  proper cleanup of resources.

  ## Examples

      iex> Foundation.shutdown()
      :ok
  """
  @spec shutdown() :: :ok
  def shutdown() do
    # Stop services in reverse order
    # Let the supervision tree handle the actual shutdown
    case Process.whereis(Foundation.Supervisor) do
      nil ->
        :ok

      pid ->
        Supervisor.stop(pid, :normal)
        :ok
    end
  end

  @doc """
  Get comprehensive health information for the Foundation layer.

  Returns detailed health and performance metrics for monitoring.

  ## Examples

      iex> Foundation.health()
      {:ok, %{
        status: :healthy,
        uptime_ms: 3600000,
        services: %{...},
        metrics: %{...}
      }}
  """
  @spec health() :: {:ok, map()} | {:error, Error.t()}
  def health() do
    case status() do
      {:ok, service_status} ->
        health_info = %{
          status: determine_overall_health(service_status),
          timestamp: System.monotonic_time(:millisecond),
          services: service_status,
          foundation_available: available?(),
          elixir_version: System.version(),
          otp_release: System.otp_release()
        }

        {:ok, health_info}

      {:error, _} = error ->
        error
    end
  end

  ## Private Functions

  defp determine_overall_health(service_status) do
    all_running =
      service_status
      |> Map.values()
      |> Enum.all?(fn status -> Map.get(status, :status) == :running end)

    if all_running, do: :healthy, else: :degraded
  end
end
</file>

</files>
