class AdminLevelConstraint
  def initialize(*require)
    @require = require.map(&:to_s)
  end

  def matches?(request)
    return false unless request.session[:user_id]
    user = User.find_by(id: request.session[:user_id])
    user && @require.include?(user.admin_level)
  end
end

Rails.application.routes.draw do
  # Redirect to localhost from 127.0.0.1 to use same IP address with Vite server
  constraints(host: "127.0.0.1") do
    get "(*path)", to: redirect { |params, req|
      path = params[:path].to_s
      query = req.query_string.presence
      base = "#{req.protocol}localhost:#{req.port}/#{path}"
      query ? "#{base}?#{query}" : base
    }
  end

  mount Rswag::Api::Engine => "/api-docs"
  mount Rswag::Ui::Engine => "/api-docs"
  use_doorkeeper do
    controllers authorizations: "custom_doorkeeper/authorizations"
  end

  post "/oauth/applications/:id/rotate_secret", to: "doorkeeper/applications#rotate_secret", as: :rotate_secret_oauth_application
  root "static_pages#index"

  resources :extensions, only: [ :index ]

  constraints AdminLevelConstraint.new(:superadmin) do
    mount GoodJob::Engine => "good_job"
    mount Flipper::UI.app(Flipper) => "flipper", as: :flipper

    namespace :admin do
      resources :admin_users, only: [ :index, :update ] do
        collection do
          get :search
        end
      end
      resources :oauth_applications, only: [ :index, :show, :edit, :update ] do
        member do
          post :toggle_verified
          post :rotate_secret
        end
      end
    end
  end

  constraints AdminLevelConstraint.new(:superadmin, :admin, :viewer) do
    namespace :admin do
      get "timeline", to: "timeline#show", as: :timeline
      get "timeline/search_users", to: "timeline#search_users"
      get "timeline/leaderboard_users", to: "timeline#leaderboard_users"

      resources :trust_level_audit_logs, only: [ :index, :show ]
      resources :admin_api_keys, except: [ :edit, :update ]
      resources :deletion_requests, only: [ :index, :show ] do
        member do
          post :approve
          post :reject
        end
      end
    end
    get "/impersonate/:id", to: "sessions#impersonate", as: :impersonate_user
  end

  constraints AdminLevelConstraint.new(:superadmin) do
    namespace :admin do
      resources :permissions, only: [ :index, :update ]
    end
  end
  get "/stop_impersonating", to: "sessions#stop_impersonating", as: :stop_impersonating

  if Rails.env.development?
    mount LetterOpenerWeb::Engine, at: "/letter_opener"
  end

  # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
  # Can be used by load balancers and uptime monitors to verify that the app is live.
  get "up" => "rails/health#show", as: :rails_health_check

  # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb)
  # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
  # get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker

  resources :static_pages, only: [ :index ] do
    collection do
      get :project_durations
      get :currently_hacking
      get :currently_hacking_count
      get :streak
      # get :timeline # Removed: Old route for timeline
    end
  end

  get "/signin", to: "static_pages#signin", as: :signin

  # Auth routes
  get "/auth/hca", to: "sessions#hca_new", as: :hca_auth
  get "/auth/hca/callback", to: "sessions#hca_create"
  get "/auth/slack", to: "sessions#slack_new", as: :slack_auth
  get "/auth/slack/callback", to: "sessions#slack_create"
  get "/auth/github", to: "sessions#github_new", as: :github_auth
  get "/auth/github/callback", to: "sessions#github_create"
  delete "/auth/github/unlink", to: "sessions#github_unlink", as: :github_unlink
  post "/auth/email", to: "sessions#email", as: :email_auth
  post "/auth/email/add", to: "sessions#add_email", as: :add_email_auth
  delete "/auth/email/unlink", to: "sessions#unlink_email", as: :unlink_email_auth
  get "/auth/token/:token", to: "sessions#token", as: :auth_token
  get "/auth/close_window", to: "sessions#close_window", as: :close_window
  delete "signout", to: "sessions#destroy", as: "signout"

  get "/leaderboard", to: redirect("/leaderboards", status: 301)

  resources :leaderboards, only: [ :index ]

  # Docs routes
  # Note: llms.txt and llms-full.txt are served as static files from public/
  # Generate them with: rails docs:generate_llms
  get "docs", to: "docs#index", as: :docs
  get "docs/*path", to: "docs#show", as: :doc

  # Nested under users for admin access
  resources :users, only: [] do
    get "settings", on: :member, to: "settings/profile#show"
    patch "settings", on: :member, to: "settings/profile#update"
    member do
      patch :update_trust_level
    end
  end

  get "my/projects", to: "my/project_repo_mappings#index", as: :my_projects

  # Namespace for current user actions
  get "my/settings", to: "settings/profile#show", as: :my_settings
  patch "my/settings", to: "settings/profile#update"
  get "my/settings/profile", to: "settings/profile#show", as: :my_settings_profile
  patch "my/settings/profile", to: "settings/profile#update"
  get "my/settings/integrations", to: "settings/integrations#show", as: :my_settings_integrations
  patch "my/settings/integrations", to: "settings/integrations#update"
  get "my/settings/notifications", to: "settings/notifications#show", as: :my_settings_notifications
  patch "my/settings/notifications", to: "settings/notifications#update"
  get "my/settings/access", to: "settings/access#show", as: :my_settings_access
  patch "my/settings/access", to: "settings/access#update"
  get "my/settings/goals", to: "settings/goals#show", as: :my_settings_goals
  post "my/settings/goals", to: "settings/goals#create", as: :my_settings_goals_create
  patch "my/settings/goals/:goal_id", to: "settings/goals#update", as: :my_settings_goal_update
  delete "my/settings/goals/:goal_id", to: "settings/goals#destroy", as: :my_settings_goal_destroy
  get "my/settings/badges", to: "settings/badges#show", as: :my_settings_badges
  get "my/settings/data", to: "settings/data#show", as: :my_settings_data
  post "my/settings/rotate_api_key", to: "settings/access#rotate_api_key", as: :my_settings_rotate_api_key

  namespace :my do
    resources :heartbeat_imports, only: [ :create, :show ] do
      collection do
        get :wakatime_download_link
      end
    end

    resources :project_repo_mappings, param: :project_name, only: [ :edit, :update ], constraints: { project_name: /.+/ } do
      member do
        patch :archive
        patch :unarchive
      end
    end
    # resource :mailing_address, only: [ :show, :edit ]
    # get "mailroom", to: "mailroom#index"
    resources :heartbeats, only: [] do
      collection do
        post :export
      end
    end
  end

  get "deletion", to: "deletion_requests#show", as: :deletion
  post "deletion", to: "deletion_requests#create", as: :create_deletion
  delete "deletion", to: "deletion_requests#cancel", as: :cancel_deletion

  get "my/wakatime_setup", to: "users#wakatime_setup"
  get "my/wakatime_setup/step-2", to: "users#wakatime_setup_step_2"
  get "my/wakatime_setup/step-3", to: "users#wakatime_setup_step_3"
  get "my/wakatime_setup/step-4", to: "users#wakatime_setup_step_4"

  post "/sailors_log/slack/commands", to: "slack#create"
  post "/timedump/slack/commands", to: "slack#create"

  get "/hackatime/v1", to: redirect("/", status: 302) # some clients seem to link this as the user's dashboard instead of /api/v1/hackatime
  # API routes
  namespace :api do
    # This is our own API– don't worry about compatibility.
    namespace :v1 do
      get "leaderboard", to: "leaderboard#daily"
      get "leaderboard/daily", to: "leaderboard#daily"
      get "leaderboard/weekly", to: "leaderboard#weekly"

      get "stats", to: "stats#show"
      get "badge/:user_id/*project", to: "badges#show"

      get "users/:username/stats", to: "stats#user_stats"
      get "users/:username/heartbeats/spans", to: "stats#user_spans"
      get "users/:username/trust_factor", to: "stats#trust_factor"
      get "users/:username/projects", to: "stats#user_projects"
      get "users/:username/project/:project_name", to: "stats#user_project"
      get "users/:username/projects/details", to: "stats#user_projects_details"

      get "users/lookup_email/:email", to: "users#lookup_email", constraints: { email: /[^\/]+/ }
      get "users/lookup_slack_uid/:slack_uid", to: "users#lookup_slack_uid"

      get "banned_users/counts", to: "stats#banned_users_counts"

      # External service Slack OAuth integration
      post "external/slack/oauth", to: "external_slack#create_user"

      resources :ysws_programs, only: [ :index ] do
        post :claim, on: :collection
      end

      namespace :my do
        get "heartbeats/most_recent", to: "heartbeats#most_recent"
        get "heartbeats", to: "heartbeats#index"
      end

      # oauth authenticated namespace
      namespace :authenticated do
        resources :me, only: [ :index ]
        get "hours", to: "hours#index"
        get "streak", to: "streak#show"
        get "projects", to: "projects#index"
        # get "projects/:name", to: "projects#show", constraints: { name: /.+/ }
        get "heartbeats/latest", to: "heartbeats#latest"
        get "api_keys", to: "api_keys#index"
      end
    end

    # Admin-only API namespace
    namespace :admin do
      namespace :v1 do
        get "check", to: "admin#check"
        get "user/info", to: "admin#user_info"
        get "user/info_batch", to: "admin#user_info_batch"
        get "user/heartbeats", to: "admin#user_heartbeats"
        get "user/heartbeat_values", to: "admin#user_heartbeat_values"
        get "user/get_users_by_ip", to: "admin#get_users_by_ip"
        get "user/get_users_by_machine", to: "admin#get_users_by_machine"
        get "user/stats", to: "admin#user_stats"
        get "user/projects", to: "admin#user_projects"
        get "user/trust_logs", to: "admin#trust_logs"
        get "banned_users", to: "admin#banned_users"
        post "user/get_user_by_email", to: "admin#get_user_by_email"
        post "user/search_fuzzy", to: "admin#search_users_fuzzy"
        post "user/convict", to: "admin#user_convict"

        # Admin API Keys management
        resources :admin_api_keys, only: [ :index, :show, :create, :destroy ]

        # Trust level audit logs
        resources :trust_level_audit_logs, only: [ :index, :show ]

        # Deletion requests
        resources :deletion_requests, only: [ :index, :show ] do
          member do
            post :approve
            post :reject
          end
        end

        # Permissions management
        resources :permissions, only: [ :index ] do
          collection do
            patch ":id", to: "permissions#update", as: :update
          end
        end

        # Timeline
        get "timeline", to: "timeline#show"
        get "timeline/search_users", to: "timeline#search_users"
        get "timeline/leaderboard_users", to: "timeline#leaderboard_users"
        get "users/:id/visualization/quantized", to: "admin#visualization_quantized"
        get "alts/candidates", to: "admin#alt_candidates"
        get "alts/shared_machines", to: "admin#shared_machines"
        get "users/active", to: "admin#active_users"
        post "audit_logs/counts", to: "admin#audit_logs_counts"
      end
    end

    # wakatime compatible summary
    get "summary", to: "summary#index"

    # Everything in this namespace conforms to wakatime.com's API.
    namespace :hackatime do
      namespace :v1 do
        get "/", to: redirect("/", status: 302) # some clients seem to link this as the user's dashboard instead of /api/v1/hackatime
        get "/users/:id/statusbar/today", to: "hackatime#status_bar_today"
        post "/users/:id/heartbeats", to: "hackatime#push_heartbeats"
        get "/users/current/stats/last_7_days", to: "hackatime#stats_last_7_days"
      end
    end

    namespace :internal do
      post "revoke", to: "revocations#create"
    end
  end

  get "/@:username", to: "profiles#show", as: :profile, constraints: { username: /[A-Za-z0-9_-]+/ }
  get "/@:username/time_stats", to: "profiles#time_stats", as: :profile_time_stats, constraints: { username: /[A-Za-z0-9_-]+/ }
  get "/@:username/projects", to: "profiles#projects", as: :profile_projects, constraints: { username: /[A-Za-z0-9_-]+/ }
  get "/@:username/languages", to: "profiles#languages", as: :profile_languages, constraints: { username: /[A-Za-z0-9_-]+/ }
  get "/@:username/editors", to: "profiles#editors", as: :profile_editors, constraints: { username: /[A-Za-z0-9_-]+/ }
  get "/@:username/activity", to: "profiles#activity", as: :profile_activity, constraints: { username: /[A-Za-z0-9_-]+/ }

  # SEO routes
  get "/sitemap.xml", to: "sitemap#sitemap", defaults: { format: "xml" }
  get "/wakatime-alternative", to: "static_pages#wakatime_alternative"

  # fuck ups
  match "/400", to: "errors#bad_request", via: :all
  match "/404", to: "errors#not_found", via: :all
  match "/422", to: "errors#unprocessable_entity", via: :all
  match "/500", to: "errors#internal_server_error", via: :all
  match "/503", to: "errors#service_unavailable", via: :all
end
