(ns ajet.chat.api.handlers.upload
  "File upload handler.

   Accepts multipart form data with an image file. Validates content-type
   and size, uploads to MinIO/S3, creates an attachment record in the DB,
   and returns the attachment metadata."
  (:require [clojure.java.io :as io]
            [clojure.string :as str]
            [clojure.tools.logging :as log]
            [ajet.chat.shared.db :as db]
            [ajet.chat.shared.storage :as storage]
            [ajet.chat.api.middleware :as mw])
  (:import [java.util UUID]))

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

(defn- uuid [] (str (UUID/randomUUID)))

(defn- require-user! [request]
  (or (:user-id request)
      (throw (ex-info "Authentication required" {:type :ajet.chat/forbidden}))))

(defn- check-channel-member! [ds channel-id user-id]
  (or (db/execute-one! ds
        {:select [:user-id]
         :from   [:channel-members]
         :where  [:and
                  [:= :channel-id [:cast channel-id :uuid]]
                  [:= :user-id [:cast user-id :uuid]]]})
      (throw (ex-info "Not a member of this channel"
                      {:type :ajet.chat/forbidden}))))

;;; ---------------------------------------------------------------------------
;;; Handler
;;; ---------------------------------------------------------------------------

(defn upload-file
  "POST /api/channels/:id/upload
   Upload an image file. Returns attachment metadata.

   Expects a multipart form with a 'file' part."
  [request]
  (let [user-id    (require-user! request)
        ds         (get-in request [:system :ds])
        s3         (get-in request [:system :s3])
        bucket     (get-in request [:system :bucket])
        channel-id (get-in request [:path-params :id])]

    (check-channel-member! ds channel-id user-id)

    ;; Extract multipart file from request
    ;; Ring multipart puts files in :multipart-params or :params with :tempfile
    (let [file-param (or (get-in request [:multipart-params "file"])
                         (get-in request [:params :file]))
          _ (when-not file-param
              (throw (ex-info "No file uploaded. Send a multipart form with a 'file' field."
                              {:type :ajet.chat/validation-error})))
          {:keys [filename content-type tempfile size]} file-param
          size-bytes (or size (when tempfile (.length tempfile)))]

      ;; Validate content type and size
      (storage/validate-upload! content-type size-bytes)

      ;; Generate storage key
      (let [attachment-id (uuid)
            key           (storage/storage-key attachment-id filename)
            file-bytes    (if tempfile
                            (let [data (byte-array size-bytes)]
                              (with-open [is (io/input-stream tempfile)]
                                (.read is data))
                              data)
                            (throw (ex-info "Uploaded file is missing tempfile"
                                            {:type :ajet.chat/validation-error})))]

        ;; Upload to S3
        (storage/upload! s3 bucket key file-bytes content-type)

        ;; Create attachment record (not linked to a message yet)
        (db/execute! ds
          {:insert-into :attachments
           :values [{:id           [:cast attachment-id :uuid]
                     :message-id   [:cast "00000000-0000-0000-0000-000000000000" :uuid]  ;; placeholder
                     :filename     filename
                     :content-type content-type
                     :size-bytes   size-bytes
                     :storage-key  key}]})

        (mw/json-response 201
          {:id           attachment-id
           :filename     filename
           :content-type content-type
           :size-bytes   size-bytes
           :url          (str "/files/" attachment-id "/" filename)})))))
