module JSONAPI::Responders

def ensure_jsonapi_accept_header

def ensure_jsonapi_accept_header
  # Allow requests without Accept header or with */* (browser defaults)
  # Only validate when Accept header is explicitly set to non-JSON:API media types
  accept_header = request.headers["Accept"]
  # Allow blank Accept header (browser default)
  return if accept_header.blank?
  # Allow */* Accept header (browser default)
  return if accept_header == "*/*"
  # Check if request accepts */* (wildcard)
  return if request.accepts.any? { |mime| mime.to_s == "*/*" }
  # Check if JSON:API media type is explicitly requested
  return if jsonapi_requested?
  # If Accept header is present and doesn't include JSON:API, return 406
  # This ensures we honor explicit Accept preferences while allowing defaults
  render_not_acceptable_error
end

def ensure_jsonapi_content_type

def ensure_jsonapi_content_type
  return if request.content_type&.include?("application/vnd.api+json")
  render json: {
    errors: [
      {
        status: "415",
        title: "Unsupported Media Type",
        detail: "Content-Type must be application/vnd.api+json",
      },
    ],
  }, status: :unsupported_media_type
end

def jsonapi_requested?

def jsonapi_requested?
  request.accepts.any? { |mime| mime.to_s.include?("application/vnd.api+json") }
end

def render_jsonapi_error(status:, title:, detail: nil, source: nil)

def render_jsonapi_error(status:, title:, detail: nil, source: nil)
  render_jsonapi_errors([{ status:, title:, detail:, source: }], status:)
end

def render_jsonapi_errors(errors, status:)

def render_jsonapi_errors(errors, status:)
  normalized_errors = Array(errors).map do |error|
    normalized = error.compact
    normalized_status = normalized[:status] || status
    normalized[:status] = status_code_for(normalized_status)
    normalized
  end
  render json: { errors: normalized_errors }, status:
end

def render_not_acceptable_error

def render_not_acceptable_error
  render_parameter_errors(
    [nil],
    title: "Not Acceptable",
    detail_proc: ->(_) { "Accept header must include application/vnd.api+json or be omitted" },
    status: :not_acceptable,
  )
end

def render_parameter_errors(values, title:, detail_proc:, source_proc: nil, status: :bad_request)

def render_parameter_errors(values, title:, detail_proc:, source_proc: nil, status: :bad_request)
  errors = Array(values).map do |value|
    error = {
      title:,
      detail: detail_proc.call(value),
    }
    error[:source] = source_proc.call(value) if source_proc
    error
  end
  render_jsonapi_errors(errors, status:)
end

def status_code_for(status)

def status_code_for(status)
  return status if status.is_a?(String) && status.match?(/\A\d+\z/)
  ::Rack::Utils::SYMBOL_TO_STATUS_CODE.fetch(status, status).to_s
end