require "kemal"
require "json"
require "ecr"
require "jennifer"
require "jennifer/adapter/postgres"

# Configure Jennifer before loading models
Jennifer::Config.configure do |conf|
  conf.host = ENV["DATABASE_URL"]
  conf.port = (ENV["DB_PORT"]? || "5432").to_i
  conf.db = ENV["DB_NAME"] || "default_db_name" # specify the database name
  conf.adapter = "postgres"
  conf.logger = Log.for("web")
  conf.logger.level = :debug if ENV["DEBUG"]?
  conf.pool_size = (ENV["DB_POOL"]? || "5").to_i
  conf.user = ENV["DB_USER"]
  conf.password = ENV["DB_PASSWORD"]
  puts "Jennifer configured with host=#{conf.host}, db=#{conf.db}, user=#{conf.user}"
end

# Add migration support
puts "running migrations..."
Jennifer::Migration::Runner.migrate

# Load models after Jennifer configuration
puts "loading models..."
require "./models/user"
require "./models/api_token"
require "./models/container_deployment"

puts "loading middleware..."
require "./middleware/auth"

puts "loading controllers..."
require "./controllers/*"

puts "loading metrics manager..."
require "./metrics_manager"

module ContainerMom::Web
  class CustomerPortal
    Log = ::Log.for("web")

    def self.start
      puts "starting kemal..."
      setup_middleware
      setup_routes
      Kemal.run
    end

    private def self.setup_middleware
      puts "setting up middleware..."
      
      # Add authentication middleware
      puts "adding auth middleware..."
      # add_handler Middleware::Auth.new

      # Add metrics tracking
      puts "adding metrics tracking..."
      before_all do |env|
        env.set "request_start_time", Time.monotonic.total_seconds.to_f64
      end

      after_all do |env|
        if start_time = env.get?("request_start_time").try(&.as(Float64))
          end_time = Time.monotonic.total_seconds.to_f64
          duration = Time::Span.new(nanoseconds: ((end_time - start_time) * 1_000_000_000).to_i64)
          WebPortalMetrics.instance.track_api_request(
            env.request.path,
            env.request.method,
            env.response.status_code,
            duration
          )
        end
      end

      # Error handling
      puts "adding error handlers..."
      error 404 do |env|
        content_type = env.request.headers["Accept"]? || "text/html"
        format = content_type.includes?("application/json") ? :json : :html
        case format
        when :json
          env.response.content_type = "application/json"
          {error: "Not Found"}.to_json
        else
          ECR.render "#{__DIR__}/views/errors/404.ecr"
        end
      end

      error 500 do |env, ex|
        WebPortalMetrics.instance.track_error(
          "web",
          ex.class.name,
          ex.message || "Unknown error"
        )

        content_type = env.request.headers["Accept"]? || "text/html"
        format = content_type.includes?("application/json") ? :json : :html
        case format
        when :json
          env.response.content_type = "application/json"
          {error: "Internal Server Error"}.to_json
        else
          ECR.render "#{__DIR__}/views/errors/500.ecr"
        end
      end
    end

    private def self.setup_routes
      puts "setting up routes..."

      # Set up authentication routes
      puts "setting up auth routes..."
      Controllers::AuthController.setup_routes

      # Health check endpoint
      puts "adding health check route..."
      get "/health" do |env|
        metrics = WebPortalMetrics.instance.health_metrics
        env.response.content_type = "application/json"
        metrics.to_json
      end

      # Prometheus metrics endpoint
      puts "adding metrics route..."
      get "/metrics" do |env|
        WebPortalMetrics.instance.metrics_handler.call(env)
      end

      # Main routes
      puts "adding main routes..."
      get "/" do |env|
        env.redirect "/deployments"
      end

      get "/deployments" do |env|
        deployments = Models::ContainerDeployment.all
        ECR.render "#{__DIR__}/views/deployments/index.ecr"
      end

      get "/deployments/new" do |env|
        ECR.render "#{__DIR__}/views/deployments/new.ecr"
      end

      post "/deployments" do |env|
        begin
          puts "handling new deployment..."
          body = env.request.body.not_nil!.gets_to_end
          json = JSON.parse(body)
          
          # Extract and validate required fields
          name = json["name"]?.try(&.as_s?) || raise "Name is required"
          source_type = json["source_type"]?.try(&.as_s?) || raise "Source type is required"
          source_url = json["source_url"]?.try(&.as_s?) || raise "Source URL is required"
          cpu = json["cpu"]?.try(&.as_s?) || raise "CPU is required"
          memory = json["memory"]?.try(&.as_s?) || raise "Memory is required"
          
          # Handle regions array
          regions = [] of String
          if regions_json = json["regions"]?
            if regions_array = regions_json.as_a?
              regions = regions_array.compact_map(&.as_s?)
            end
          end
          
          raise "At least one region is required" if regions.empty?

          user = Middleware.current_user(env)

          deployment = create_deployment(
            name: name,
            source_type: source_type,
            source_url: source_url,
            regions: regions,
            cpu: cpu,
            memory: memory,
            user: user
          )

          env.response.content_type = "application/json"
          {
            id: deployment.id,
            name: deployment.name,
            status: deployment.status,
            regions: deployment.regions,
          }.to_json
        rescue ex : JSON::ParseException
          env.response.status_code = 400
          env.response.content_type = "application/json"
          {error: "Invalid JSON format"}.to_json
        rescue ex
          env.response.status_code = 422
          env.response.content_type = "application/json"
          {error: ex.message}.to_json
        end
      end

      get "/billing" do |env|
        puts "loading billing data..."
        user = Middleware.current_user(env)
        # usage = BillingManager.instance.get_usage(user)
        ECR.render "#{__DIR__}/views/billing/index.ecr"
      end

      # Static files
      puts "setting up static file serving..."
      serve_static({"gzip" => true, "dir_listing" => false})
    end

    private def self.create_deployment(**args)
      # Implementation will be added later
      raise "Not implemented"
    end
  end
end

puts "starting customer portal..."
ContainerMom::Web::CustomerPortal.start