# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2025, by Samuel Williams.

require "uri"
require_relative "header"
require_relative "status"

module Protocol
	module GRPC
		# @namespace
		module Metadata
			# Extract gRPC status from headers.
			# Convenience method that handles both Header::Status instances and raw values.
			# Returns Status::UNKNOWN if status is not present.
			#
			# Note: In Protocol::HTTP::Headers, trailers are merged into the headers
			# so users just access headers["grpc-status"] regardless of whether it
			# was sent as an initial header or trailer.
			#
			# @parameter headers [Protocol::HTTP::Headers]
			# @returns [Integer] Status code (0-16)
			def self.extract_status(headers)
				# Ensure policy is set - setting policy clears the index (@indexed = nil)
				# The index will be rebuilt automatically on next access via to_h
				headers.policy = Protocol::GRPC::HEADER_POLICY unless headers.policy == Protocol::GRPC::HEADER_POLICY
				
				status = headers["grpc-status"]
				return Status::UNKNOWN unless status
				
				if status.is_a?(Header::Status)
					status.to_i
				else
					# Fallback for when header policy isn't used
					# Handle Array case (may occur with external clients)
					status_value = if status.is_a?(Array)
						# Flatten and take first non-nil value, recursively handle nested arrays
						flattened = status.flatten.compact.first
						# If still an array, take first element
						flattened.is_a?(Array) ? flattened.first : flattened
					else
						status
					end
					
					# Convert to string then integer to handle various types
					# Handle case where status_value might still be an array somehow
					if status_value.is_a?(Array)
						status_value = status_value.first
					end
					status_value.to_s.to_i
				end
			end
			
			# Extract gRPC status message from headers.
			# Convenience method that handles both Header::Message instances and raw values.
			# Returns `Nil` if message is not present.
			#
			# @parameter headers [Protocol::HTTP::Headers]
			# @returns [String | Nil] Status message
			def self.extract_message(headers)
				# Ensure policy is set - setting policy clears the index (@indexed = nil)
				# The index will be rebuilt automatically on next access via to_h
				headers.policy = Protocol::GRPC::HEADER_POLICY unless headers.policy == Protocol::GRPC::HEADER_POLICY
				
				message = headers["grpc-message"]
				return nil unless message
				
				if message.is_a?(Header::Message)
					message.decode
				else
					# Fallback for when header policy isn't used
					message_value = message.is_a?(Array) ? message.first : message.to_s
					URI.decode_www_form_component(message_value)
				end
			end
			
			# Assign gRPC status, message, and optional backtrace to headers.
			#
			# Whether these become headers or trailers is controlled by the protocol layer.
			#
			# @parameter headers [Protocol::HTTP::Headers]
			# @parameter status [Integer] gRPC status code
			# @parameter message [String | Nil] Optional status message
			# @parameter error [Exception | Nil] Optional error object (used to extract backtrace)
			def self.assign_status!(headers, status: Status::OK, message: nil, error: nil)
				headers["grpc-status"] = status
				
				if error && message.nil?
					# If message is not provided but error is, use error message
					message = error.message
				end
				
				if message
					headers["grpc-message"] = message
				end
				
				# Add backtrace from error if available
				if error && error.backtrace && !error.backtrace.empty?
					# Assign backtrace array directly - Split header will handle it
					headers["backtrace"] = error.backtrace
				end
				
				return headers
			end
			
			class << self
				# Backward compatibility alias
				alias add_status! assign_status!
			end
		end
	end
end
