module Middleware
  class SanastoCors
    ALLOWED_APP_ID = ENV.fetch("SANASTO_APP_ID", "app.sanasto").freeze
    APP_ID_HEADER = "HTTP_X_SANASTO_APP"

    def initialize(app)
      @app = app
    end

    def call(env)
      if allow_cors_for?(env)
        if env["REQUEST_METHOD"] == "OPTIONS"
          return preflight_response(env["HTTP_ORIGIN"], allowed_request_headers(env))
        end
      end

      status, headers, body = @app.call(env)
      if allow_cors_for?(env)
        apply_cors_headers(headers, env["HTTP_ORIGIN"])
      end
      [ status, headers, body ]
    end

    private

    def allow_cors_for?(env)
      origin = env["HTTP_ORIGIN"].to_s
      return false if origin.empty?

      if env["REQUEST_METHOD"] == "OPTIONS"
        return preflight_includes_app_id_header?(env)
      end

      app_id = env[APP_ID_HEADER].to_s
      return false if app_id.empty?

      app_id == ALLOWED_APP_ID
    end

    def preflight_includes_app_id_header?(env)
      access_control_headers = env["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"].to_s
      return false if access_control_headers.empty?

      access_control_headers
        .split(",")
        .map { |header_name| header_name.strip.downcase }
        .include?("x-sanasto-app")
    end

    def allowed_request_headers(env)
      access_control_headers = env["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"].to_s
      return default_allowed_headers if access_control_headers.empty?

      sanitized = access_control_headers
        .split(",")
        .map { |header_name| header_name.strip }
        .reject(&:empty?)
        .join(", ")

      sanitized.empty? ? default_allowed_headers : sanitized
    end

    def preflight_response(origin, allowed_headers)
      headers = {}
      apply_cors_headers(headers, origin, allowed_headers)
      headers["Access-Control-Max-Age"] = "86400"
      headers["Vary"] = [
        headers["Vary"],
        "Access-Control-Request-Headers",
        "Access-Control-Request-Method"
      ].compact.join(", ")
      [ 204, headers, [] ]
    end

    def apply_cors_headers(headers, origin, allowed_headers = default_allowed_headers)
      headers["Access-Control-Allow-Origin"] = origin
      headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, PATCH, DELETE, OPTIONS"
      headers["Access-Control-Allow-Headers"] = allowed_headers
      headers["Vary"] = [ headers["Vary"], "Origin, X-Sanasto-App" ].compact.join(", ")
    end

    def default_allowed_headers
      "Origin, Content-Type, Accept, Authorization, X-Sanasto-App"
    end
  end
end
