# frozen_string_literal: true

require "rack/test"
require "stringio"

RSpec.describe "Logging / Exception logging", :app_integration do
  include Rack::Test::Methods

  let(:app) { Hanami.app }

  let(:logger_stream) { StringIO.new }

  def configure_logger
    Hanami.app.config.logger.stream = logger_stream
  end

  def logs
    @logs ||= (logger_stream.rewind and logger_stream.read)
  end

  before do
    with_directory(make_tmp_directory) do
      write "config/app.rb", <<~RUBY
        module TestApp
          class App < Hanami::App
            # Disable framework-level error rendering so we can test the raw action behavior
            config.render_errors = false
            config.render_detailed_errors = false
          end
        end
      RUBY

      write "config/routes.rb", <<~RUBY
        module TestApp
          class Routes < Hanami::Routes
            root to: "test"
          end
        end
      RUBY

      require "hanami/setup"
      configure_logger

      before_prepare if respond_to?(:before_prepare)
      require "hanami/prepare"
    end
  end

  describe "unhandled exceptions" do
    def before_prepare
      write "app/actions/test.rb", <<~RUBY
        module TestApp
          module Actions
            class Test < Hanami::Action
              UnhandledError = Class.new(StandardError)

              def handle(request, response)
                raise UnhandledError, "unhandled"
              end
            end
          end
        end
      RUBY
    end

    it "logs a 500 error and full exception details when an exception is raised" do
      # Make the request with a rescue so the raised exception doesn't crash the tests
      begin
        get "/"
      rescue TestApp::Actions::Test::UnhandledError # rubocop:disable Lint/SuppressedException
      end

      expect(logs.lines.length).to be > 10
      expect(logs).to match %r{GET 500 \d+(µs|ms) 127.0.0.1 /}
      expect(logs).to include("unhandled (TestApp::Actions::Test::UnhandledError)")

      if RUBY_VERSION < "3.4"
        expect(logs).to include("app/actions/test.rb:7:in `handle'")
      else
        expect(logs).to include("app/actions/test.rb:7:in 'TestApp::Actions::Test#handle'")
      end
    end

    it "re-raises the exception" do
      expect { get "/" }.to raise_error(TestApp::Actions::Test::UnhandledError)
    end
  end

  describe "errors handled by handle_exception" do
    def before_prepare
      write "app/actions/test.rb", <<~RUBY
        module TestApp
          module Actions
            class Test < Hanami::Action
              NotFoundError = Class.new(StandardError)

              handle_exception NotFoundError => :handle_not_found_error

              def handle(request, response)
                raise NotFoundError
              end

              private

              def handle_not_found_error(request, response, exception)
                halt 404
              end
            end
          end
        end
      RUBY
    end

    it "does not log an error" do
      get "/"

      expect(logs).to match %r{GET 404 \d+(µs|ms) 127.0.0.1 /}
    end
  end
end
