(ns ajet.chat.api.handlers.admin
  "Admin endpoints for managing OAuth providers.

   Authorization: user must be owner of at least one community.
   These endpoints allow post-setup management of OAuth providers
   stored in the database."
  (:require [clojure.string :as str]
            [clojure.tools.logging :as log]
            [ajet.chat.shared.db :as db]
            [ajet.chat.api.middleware :as mw])
  (:import [java.util UUID]))

;;; ---------------------------------------------------------------------------
;;; Helpers
;;; ---------------------------------------------------------------------------

(defn- require-admin!
  "Verify the user is an owner of at least one community.
   Returns the user-id or throws 403."
  [request]
  (let [user-id (or (:user-id request)
                    (throw (ex-info "Authentication required"
                                    {:type :ajet.chat/forbidden})))
        ds      (get-in request [:system :ds])
        owner?  (db/execute-one! ds
                  {:select [[:1 :exists]]
                   :from   [:community-members]
                   :where  [:and
                            [:= :user-id [:cast user-id :uuid]]
                            [:= :role "owner"]]})]
    (when-not owner?
      (throw (ex-info "Admin access required (must be community owner)"
                      {:type :ajet.chat/forbidden})))
    user-id))

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

(defn list-providers
  "GET /api/admin/oauth-providers — list all OAuth providers (including disabled)."
  [request]
  (require-admin! request)
  (let [ds        (get-in request [:system :ds])
        providers (db/execute! ds
                    {:select [:id :provider-type :display-name :slug
                              :client-id :base-url :issuer-url
                              :enabled :sort-order :created-at :updated-at]
                     :from   [:oauth-providers]
                     :order-by [[:sort-order :asc] [:created-at :asc]]})]
    (mw/json-response providers)))

(defn create-provider
  "POST /api/admin/oauth-providers — create a new OAuth provider."
  [request]
  (require-admin! request)
  (let [ds     (get-in request [:system :ds])
        params (:body-params request)
        {:keys [provider-type display-name slug client-id client-secret
                base-url issuer-url enabled sort-order]} params]
    (when (or (str/blank? provider-type) (str/blank? display-name)
              (str/blank? slug) (str/blank? client-id) (str/blank? client-secret))
      (throw (ex-info "provider-type, display-name, slug, client-id, and client-secret are required"
                      {:type :ajet.chat/validation-error})))
    (when-not (#{"github" "gitea" "oidc"} provider-type)
      (throw (ex-info "provider-type must be github, gitea, or oidc"
                      {:type :ajet.chat/validation-error})))
    (let [provider (db/execute-one! ds
                     {:insert-into :oauth-providers
                      :values [(cond-> {:provider-type provider-type
                                        :display-name  display-name
                                        :slug          slug
                                        :client-id     client-id
                                        :client-secret client-secret}
                                 base-url   (assoc :base-url base-url)
                                 issuer-url (assoc :issuer-url issuer-url)
                                 (some? enabled)    (assoc :enabled enabled)
                                 (some? sort-order) (assoc :sort-order sort-order))]
                      :returning [:id :provider-type :display-name :slug
                                  :client-id :base-url :issuer-url
                                  :enabled :sort-order :created-at :updated-at]})]
      (log/info "Admin created OAuth provider" slug provider-type)
      (mw/json-response 201 provider))))

(defn update-provider
  "PUT /api/admin/oauth-providers/:id — update an existing OAuth provider."
  [request]
  (require-admin! request)
  (let [ds          (get-in request [:system :ds])
        provider-id (get-in request [:path-params :id])
        params      (:body-params request)
        updates     (cond-> {}
                      (:display-name params)  (assoc :display-name (:display-name params))
                      (:slug params)          (assoc :slug (:slug params))
                      (:client-id params)     (assoc :client-id (:client-id params))
                      (:client-secret params) (assoc :client-secret (:client-secret params))
                      (:base-url params)      (assoc :base-url (:base-url params))
                      (:issuer-url params)    (assoc :issuer-url (:issuer-url params))
                      (some? (:enabled params))    (assoc :enabled (:enabled params))
                      (some? (:sort-order params)) (assoc :sort-order (:sort-order params)))]
    (when (empty? updates)
      (throw (ex-info "No fields to update" {:type :ajet.chat/validation-error})))
    (let [result (db/execute-one! ds
                   {:update :oauth-providers
                    :set    (assoc updates :updated-at [:now])
                    :where  [:= :id [:cast provider-id :uuid]]
                    :returning [:id :provider-type :display-name :slug
                                :client-id :base-url :issuer-url
                                :enabled :sort-order :created-at :updated-at]})]
      (if result
        (do (log/info "Admin updated OAuth provider" provider-id)
            (mw/json-response result))
        (mw/error-response 404 "NOT_FOUND" "OAuth provider not found")))))

(defn delete-provider
  "DELETE /api/admin/oauth-providers/:id — delete an OAuth provider."
  [request]
  (require-admin! request)
  (let [ds          (get-in request [:system :ds])
        provider-id (get-in request [:path-params :id])]
    (db/execute! ds
      {:delete-from :oauth-providers
       :where [:= :id [:cast provider-id :uuid]]})
    (log/info "Admin deleted OAuth provider" provider-id)
    (mw/json-response 204 nil)))
