require "kemal"
require "../models/*"

module ContainerMom::Web::Middleware
  class Auth < Kemal::Handler
    BEARER_PATTERN = /^Bearer\s+(.+)$/
    SESSION_COOKIE = "container_mom_session"

    def call(context)
      return call_next(context) if public_path?(context.request.path)

      if user = authenticate(context)
        context.set "current_user", user
        call_next(context)
      else
        handle_unauthorized(context)
      end
    end

    private def authenticate(context) : Models::User?
      # Try API token authentication first
      if auth_header = context.request.headers["Authorization"]?
        if match = auth_header.match(BEARER_PATTERN)
          token = match[1]
          if result = Models::ApiToken.authenticate(token)
            api_token, user = result
            return user if user.active?
          end
        end
      end

      # Fall back to session authentication
      if session_token = context.cookies[SESSION_COOKIE]?
        if session = Models::Session.find_valid(session_token)
          user = session.user
          if user && user.active?
            session.touch_activity
            return user
          end
        end
      end

      nil
    end

    private def handle_unauthorized(context)
      if api_request?(context)
        context.response.status_code = 401
        context.response.content_type = "application/json"
        context.response.print({error: "Unauthorized"}.to_json)
      else
        context.response.headers["Location"] = "/login"
        context.response.status_code = 302
      end
      context
    end

    private def public_path?(path : String) : Bool
      PUBLIC_PATHS.any? { |pattern| pattern.matches?(path) }
    end

    private def api_request?(context) : Bool
      context.request.headers["Accept"]? == "application/json" ||
        context.request.path.starts_with?("/api/")
    end

    # Add paths that don't require authentication
    PUBLIC_PATHS = [
      /^\/login$/,
      /^\/signup$/,
      /^\/health$/,
      /^\/metrics$/,
      /^\/assets\//,
      /^\/favicon\.ico$/,
      /^\/robots\.txt$/
    ]
  end

  # Helper methods for controllers
  macro require_admin
    before_all do |env|
      user = env.get?("current_user").try(&.as(Models::User))
      unless user && user.admin?
        env.response.status_code = 403
        env.response.content_type = "application/json"
        env.response.print({error: "Forbidden"}.to_json)
        env.response.close
      end
    end
  end

  macro current_user
    env.get("current_user").as(Models::User)
  end

  macro current_user?
    env.get?("current_user").try(&.as(Models::User))
  end
end
