(ns ajet.chat.api.core
  "REST API service — http-kit + reitit.

   Manages the full lifecycle: DB pool, NATS, MinIO, HTTP server.
   System state held in a single atom for REPL-driven development."
  (:refer-clojure :exclude [reset!])
  (:require [clojure.tools.logging :as log]
            [org.httpkit.server :as http-kit]
            [ajet.chat.shared.config :as config]
            [ajet.chat.shared.db :as db]
            [ajet.chat.shared.eventbus :as eventbus]
            [ajet.chat.shared.storage :as storage]
            [ajet.chat.api.routes :as routes])
  (:gen-class))

(defonce system (atom nil))

;;; ---------------------------------------------------------------------------
;;; Lifecycle
;;; ---------------------------------------------------------------------------

(defn start!
  "Start the API service. Connects to PG, NATS, MinIO and starts HTTP server."
  [& [{:keys [config-overrides]}]]
  (when @system
    (log/warn "System already started — call (stop!) first")
    (throw (ex-info "System already running" {})))
  (let [config     (config/load-config {:resource "api-config.edn"})
        config     (if config-overrides
                     (merge config config-overrides)
                     config)
        _          (log/info "Loaded config:" (config/redact config))

        ;; Database
        ds         (db/make-datasource (:db config))
        _          (log/info "Database connection pool created")
        _          (when (get-in config [:db :migrations :enabled] true)
                     (db/migrate! ds (get-in config [:db :migrations])))

        ;; NATS
        nats       (eventbus/connect! (:nats config))
        _          (log/info "Connected to NATS")
        _          (eventbus/ensure-stream! nats)

        ;; MinIO / S3
        s3-client  (storage/make-client (:minio config))
        bucket     (get-in config [:minio :bucket] "ajet-chat")
        _          (storage/ensure-bucket! s3-client bucket)
        _          (log/info "MinIO connected, bucket ensured:" bucket)

        ;; System map
        sys        {:config config
                    :ds     ds
                    :nats   nats
                    :s3     s3-client
                    :bucket bucket}

        ;; HTTP server
        handler    (routes/app sys)
        port       (get-in config [:server :port] 3001)
        host       (get-in config [:server :host] "0.0.0.0")
        server     (http-kit/run-server handler {:port port :ip host
                                                 :max-body (* 12 1024 1024)})]
    (clojure.core/reset! system (assoc sys :server server :port port))
    (log/info (str "API service started on port " port))
    @system))

(defn stop!
  "Stop the API service. Shuts down HTTP, NATS, DB pool in order."
  []
  (when-let [sys @system]
    (log/info "Shutting down API service...")

    ;; Stop HTTP server (wait up to 30s for in-flight requests)
    (when-let [server (:server sys)]
      (server :timeout 30000)
      (log/info "HTTP server stopped"))

    ;; Close NATS
    (when-let [nats (:nats sys)]
      (try
        (eventbus/close! nats)
        (log/info "NATS connection closed")
        (catch Exception e
          (log/error e "Error closing NATS connection"))))

    ;; Close S3 client
    (when-let [s3 (:s3 sys)]
      (try
        (storage/close-client s3)
        (log/info "S3 client closed")
        (catch Exception e
          (log/error e "Error closing S3 client"))))

    ;; Close DB pool
    (when-let [ds (:ds sys)]
      (try
        (db/close-datasource ds)
        (log/info "Database connection pool closed")
        (catch Exception e
          (log/error e "Error closing database pool"))))

    (clojure.core/reset! system nil)
    (log/info "API service stopped")))

(defn reset!
  "Stop then start the system (REPL convenience)."
  []
  (stop!)
  (start!))

;;; ---------------------------------------------------------------------------
;;; Entry point
;;; ---------------------------------------------------------------------------

(defn -main [& _args]
  (start!)

  ;; Graceful shutdown hook
  (.addShutdownHook
    (Runtime/getRuntime)
    (Thread. ^Runnable (fn []
                         (log/info "Shutdown hook triggered")
                         (stop!))))

  ;; Block main thread
  @(promise))
