# Copyright © 2025-26 l5yth & contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# frozen_string_literal: true

require "logger"
require "time"

module PotatoMesh
  # Logging utilities shared across the web application.
  module Logging
    LOGGER_NAME = "potato-mesh" # :nodoc:

    module_function

    # Build a logger configured with the potato-mesh formatter.
    #
    # @param io [#write] destination for log output.
    # @return [Logger] configured logger instance.
    def build_logger(io = $stdout)
      logger = Logger.new(io)
      logger.progname = LOGGER_NAME
      logger.formatter = method(:formatter)
      logger
    end

    # Format log entries with a consistent structure understood by the UI.
    #
    # @param severity [String] Ruby logger severity constant (e.g., "DEBUG").
    # @param time [Time] timestamp when the log entry was created.
    # @param progname [String, nil] optional application name emitting the log.
    # @param message [String] body of the log message.
    # @return [String] formatted log entry.
    def formatter(severity, time, progname, message)
      timestamp = time.utc.iso8601(3)
      body = message.is_a?(String) ? message : message.inspect
      "[#{timestamp}] [#{progname || LOGGER_NAME}] [#{severity.downcase}] #{body}\n"
    end

    # Emit a structured log entry to the provided logger instance.
    #
    # @param logger [Logger, nil] logger to emit against.
    # @param severity [Symbol] target severity (e.g., :debug, :info).
    # @param message [String] primary message text.
    # @param context [String, nil] logical component generating the entry.
    # @param metadata [Hash] supplemental structured data for the log.
    # @return [void]
    def log(logger, severity, message, context: nil, **metadata)
      return unless logger

      parts = []
      parts << "context=#{context}" if context
      metadata.each do |key, value|
        parts << format_metadata_pair(key, value)
      end
      parts << message

      logger.public_send(severity, parts.join(" "))
    end

    # Retrieve the canonical logger for the web application.
    #
    # @param target [Object, nil] object with optional +settings.logger+ accessor.
    # @return [Logger, nil] logger instance when available.
    def logger_for(target = nil)
      if target.respond_to?(:settings) && target.settings.respond_to?(:logger)
        return target.settings.logger
      end

      if defined?(PotatoMesh::Application) &&
         PotatoMesh::Application.respond_to?(:settings) &&
         PotatoMesh::Application.settings.respond_to?(:logger)
        return PotatoMesh::Application.settings.logger
      end

      nil
    end

    # Format metadata key/value pairs for structured logging output.
    #
    # @param key [Symbol, String]
    # @param value [Object]
    # @return [String]
    def format_metadata_pair(key, value)
      "#{key}=#{value.inspect}"
    end

    private_class_method :format_metadata_pair
  end
end
