(ns ajet.chat.auth-gw.setup
  "Admin setup wizard — multi-step first-deployment bootstrap.

   Flow:
   1. Configure OAuth providers (no auth needed — no users exist yet)
   2. Admin logs in via one of the configured providers (normal OAuth flow)
   3. Create first community (auth required — admin is now logged in)

   After community creation, setup_completed is set to true and /setup
   proxies to Web SM for subsequent community creation."
  (:require [clojure.string :as str]
            [clojure.tools.logging :as log]
            [babashka.http-client :as http]
            [clojure.data.json :as json]
            [ajet.chat.auth-gw.auth :as auth]
            [ajet.chat.auth-gw.pages :as pages]))

;;; ---------------------------------------------------------------------------
;;; Setup state helpers
;;; ---------------------------------------------------------------------------

(defn setup-complete?
  "Check if initial setup has been completed.
   Uses the cached atom if available, otherwise queries the DB."
  [{:keys [ds setup-complete-atom]}]
  (if-let [cached @setup-complete-atom]
    (= cached :true)
    (let [val (auth/get-system-setting ds "setup_completed")
          complete? (= val "true")]
      (reset! setup-complete-atom (if complete? :true :false))
      complete?)))

(defn- mark-setup-complete!
  "Mark setup as completed in the database and update the cache."
  [{:keys [ds setup-complete-atom]}]
  (auth/set-system-setting! ds "setup_completed" "true")
  (reset! setup-complete-atom :true)
  (log/info "Setup wizard marked as complete"))

(defn reload-providers!
  "Reload OAuth providers from DB into the cached atom."
  [{:keys [ds oauth-providers-atom]}]
  (let [providers (auth/list-oauth-providers ds)]
    (reset! oauth-providers-atom providers)
    (log/info "Reloaded" (count providers) "OAuth providers from DB")
    providers))

;;; ---------------------------------------------------------------------------
;;; Wizard step determination
;;; ---------------------------------------------------------------------------

(defn- current-step
  "Determine the current wizard step based on DB state.
   - No enabled OAuth providers → :configure-providers
   - Providers exist but no users → :awaiting-login (redirect to login)
   - Users exist but setup not complete → :create-community"
  [ds]
  (let [provider-count (auth/count-oauth-providers ds)
        user-count     (auth/count-users ds)]
    (cond
      (zero? provider-count) :configure-providers
      (zero? user-count)     :awaiting-login
      :else                  :create-community)))

;;; ---------------------------------------------------------------------------
;;; Response helpers
;;; ---------------------------------------------------------------------------

(defn- require-wizard-auth
  "Validate session for authenticated wizard steps. Returns auth-info or nil."
  [{:keys [ds config]} request]
  (let [cookie-name (get-in config [:session :cookie-name] "ajet_session")]
    (auth/validate-session ds (auth/extract-session-cookie request cookie-name))))

(def ^:private redirect-to-login
  {:status 302 :headers {"Location" "/auth/login"} :body ""})

(def ^:private redirect-to-home
  {:status 302 :headers {"Location" "/"} :body ""})

(def ^:private redirect-to-setup
  {:status 302 :headers {"Location" "/setup"} :body ""})

(defn- html-response [body]
  {:status  200
   :headers {"Content-Type" "text/html; charset=utf-8"
             "Cache-Control" "no-store"}
   :body    body})

;;; ---------------------------------------------------------------------------
;;; Handlers
;;; ---------------------------------------------------------------------------

(defn wizard-page-handler
  "GET /setup — render the appropriate wizard step.
   - No providers configured: show OAuth provider configuration form
   - Providers exist, no users: redirect to /auth/login for first OAuth login
   - User authenticated, setup incomplete: show community creation form"
  [{:keys [ds] :as sys} request]
  (if (setup-complete? sys)
    redirect-to-home
    (let [step (current-step ds)]
      (case step
        :configure-providers
        (let [providers (auth/list-all-oauth-providers ds)]
          (html-response (pages/setup-configure-providers-page {:providers providers})))

        :awaiting-login
        redirect-to-login

        :create-community
        (if-let [_auth (require-wizard-auth sys request)]
          (html-response (pages/setup-create-community-page {}))
          redirect-to-login)))))

(defn create-community-page-handler
  "GET /setup/create-community — render the community creation form directly.
   Used when navigating from provider config step via 'Next' link."
  [{:keys [ds] :as sys} request]
  (if (setup-complete? sys)
    redirect-to-home
    (if-let [_auth (require-wizard-auth sys request)]
      (html-response (pages/setup-create-community-page {}))
      redirect-to-login)))

(defn add-provider-handler
  "POST /setup/providers — add an OAuth provider during initial setup.
   No auth required (no users exist yet). Validates provider fields:
   type must be github/gitea/oidc, display name + slug + client credentials
   required, base-url required for Gitea, issuer-url required for OIDC."
  [{:keys [ds] :as sys} request]
  (if (setup-complete? sys)
    redirect-to-home
    (let [params        (:params request)
          provider-type (get params "provider-type")
          display-name  (some-> (get params "display-name") str/trim)
          slug          (some-> (get params "slug") str/trim)
          client-id     (some-> (get params "client-id") str/trim)
          client-secret (some-> (get params "client-secret") str/trim)
          base-url      (some-> (get params "base-url") str/trim)
          issuer-url    (some-> (get params "issuer-url") str/trim)
          providers     (auth/list-all-oauth-providers ds)
          render-error  (fn [msg]
                          (html-response
                            (pages/setup-configure-providers-page
                              {:providers providers :error msg})))]
      (cond
        (not (#{"github" "gitea" "oidc"} provider-type))
        (render-error "Invalid provider type")

        (or (str/blank? display-name) (str/blank? slug)
            (str/blank? client-id) (str/blank? client-secret))
        (render-error "Display name, slug, client ID, and client secret are required")

        (and (= provider-type "gitea") (str/blank? base-url))
        (render-error "Base URL is required for Gitea providers")

        (and (= provider-type "oidc") (str/blank? issuer-url))
        (render-error "Issuer URL is required for OIDC providers")

        :else
        (do
          (auth/insert-oauth-provider! ds
            (cond-> {:provider-type provider-type
                     :display-name  display-name
                     :slug          slug
                     :client-id     client-id
                     :client-secret client-secret}
              (not (str/blank? base-url))   (assoc :base-url base-url)
              (not (str/blank? issuer-url)) (assoc :issuer-url issuer-url)))
          (reload-providers! sys)
          (log/info "Setup wizard: OAuth provider added" provider-type slug)
          redirect-to-setup)))))

(defn delete-provider-handler
  "POST /setup/providers/:id/delete — remove an OAuth provider during setup.
   No auth required (called during initial setup before any users exist)."
  [{:keys [ds] :as sys} request]
  (if (setup-complete? sys)
    redirect-to-home
    (let [provider-id (get-in request [:path-params :id])]
      (auth/delete-oauth-provider! ds
        (if (instance? java.util.UUID provider-id)
          provider-id
          (java.util.UUID/fromString (str provider-id))))
      (reload-providers! sys)
      (log/info "Setup wizard: OAuth provider deleted" provider-id)
      redirect-to-setup)))

(defn create-community-handler
  "POST /setup/create-community — create the first community and complete setup.
   Requires authentication (admin logged in via OAuth). Validates community
   name and slug, creates via internal API call, marks setup_completed=true."
  [{:keys [ds config] :as sys} request]
  (if (setup-complete? sys)
    redirect-to-home
    (if-let [auth-info (require-wizard-auth sys request)]
      (let [params         (:params request)
            community-name (some-> (get params "name") str/trim)
            community-slug (some-> (get params "slug") str/trim)]
        (cond
          (or (str/blank? community-name) (str/blank? community-slug))
          (html-response (pages/setup-create-community-page
                           {:error "Community name and slug are required"}))

          (not (re-matches #"[a-z0-9][a-z0-9-]*[a-z0-9]" community-slug))
          (html-response (pages/setup-create-community-page
                           {:error "Slug must be lowercase letters, digits, and hyphens (min 2 chars)"
                            :name community-name :slug community-slug}))

          :else
          (let [api-host (get-in config [:services :api :host] "localhost")
                api-port (get-in config [:services :api :port] 3001)
                api-url  (str "http://" api-host ":" api-port "/api/communities")
                resp     (http/post api-url
                           {:headers {"Content-Type" "application/json"
                                      "X-User-Id"   (str (:user-id auth-info))}
                            :body    (json/write-str {:name community-name
                                                      :slug community-slug})
                            :throw   false
                            :timeout 10000})]
            (if (= 201 (:status resp))
              (do
                (mark-setup-complete! sys)
                (reload-providers! sys)
                (log/info "Setup wizard: complete. Community created:" community-slug)
                redirect-to-home)
              (let [body   (try (json/read-str (:body resp) :key-fn keyword) (catch Exception _ nil))
                    errmsg (or (get-in body [:error :message]) "Failed to create community")]
                (html-response (pages/setup-create-community-page
                                 {:error errmsg
                                  :name  community-name
                                  :slug  community-slug})))))))
      redirect-to-login)))
