(ns ajet.chat.tui-sm.core
  "TUI session manager — http-kit SSE for terminal clients.

   Manages the full lifecycle: NATS connection, connection tracker,
   ping scheduler, presence flusher, and HTTP server.

   System state held in a single atom for REPL-driven development.

   No direct PostgreSQL access — all data flows through the API service
   via the shared api-client, and real-time events via NATS."
  (: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.eventbus :as eventbus]
            [ajet.chat.tui-sm.routes :as routes]
            [ajet.chat.tui-sm.sse :as sse]
            [ajet.chat.tui-sm.presence :as presence])
  (:gen-class))

(defonce system (atom nil))

;;; ---------------------------------------------------------------------------
;;; Ping Scheduler
;;; ---------------------------------------------------------------------------

(defn- start-ping-scheduler!
  "Start a background thread that sends keepalive pings to all connected
   clients at the configured interval (default 30s).

   Returns the future running the scheduler."
  [interval-ms]
  (let [running (volatile! true)]
    (future
      (log/info "Ping scheduler started, interval:" interval-ms "ms")
      (while @running
        (try
          (Thread/sleep interval-ms)
          (sse/ping-all!)
          (catch InterruptedException _
            (vreset! running false))
          (catch Exception e
            (log/error e "Error in ping scheduler"))))
      (log/info "Ping scheduler stopped"))))

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

(defn start!
  "Start the TUI session manager service.

   Connects to NATS, initializes connection tracking, starts the ping
   scheduler and presence flusher, then starts the HTTP server."
  [& [{:keys [config-overrides]}]]
  (when @system
    (log/warn "System already started -- call (stop!) first")
    (throw (ex-info "System already running" {})))
  (let [cfg        (config/load-config {:resource "tui-sm-config.edn"})
        cfg        (if config-overrides
                     (merge cfg config-overrides)
                     cfg)
        _          (log/info "Loaded config:" (config/redact cfg))

        ;; NATS connection
        nats       (eventbus/connect! (:nats cfg))
        _          (log/info "Connected to NATS")

        ;; Connection tracker (atom lives in sse ns)
        _          (clojure.core/reset! sse/connections {})
        _          (log/info "Connection tracker initialized")

        ;; System map (no DB, no S3 for TUI SM)
        sys        {:config      cfg
                    :nats        nats
                    :connections sse/connections}

        ;; HTTP server
        handler    (routes/app sys)
        port       (get-in cfg [:server :port] 3003)
        host       (get-in cfg [:server :host] "0.0.0.0")
        server     (http-kit/run-server handler {:port port :ip host
                                                  :max-body (* 1 1024 1024)})
        _          (log/info (str "TUI SM HTTP server started on " host ":" port))

        ;; Ping scheduler
        ping-ms    (get-in cfg [:sse :ping-interval-ms] 30000)
        ping-fut   (start-ping-scheduler! ping-ms)
        _          (log/info "Ping scheduler started")

        ;; Presence flush scheduler
        pres-ms    (get-in cfg [:presence :batch-interval-ms] 60000)
        pres-fut   (presence/start-flush-scheduler!
                     sse/broadcast-presence-changes!
                     pres-ms)
        _          (log/info "Presence flush scheduler started")]

    (clojure.core/reset! system (assoc sys
                          :server   server
                          :port     port
                          :ping-fut ping-fut
                          :pres-fut pres-fut))
    (log/info (str "TUI session manager fully started on port " port))
    @system))

(defn stop!
  "Stop the TUI session manager service.

   Shuts down HTTP server, ping scheduler, presence flusher, NATS,
   and clears all connection state."
  []
  (when-let [sys @system]
    (log/info "Shutting down TUI session manager...")

    ;; Stop ping scheduler
    (when-let [f (:ping-fut sys)]
      (future-cancel f)
      (log/info "Ping scheduler stopped"))

    ;; Stop presence flush scheduler
    (presence/stop-flush-scheduler!)
    (log/info "Presence flush scheduler stopped")

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

    ;; Clean up all SSE connections (unsubscribe NATS)
    (when-let [nats (:nats sys)]
      (sse/reset-connections! nats)
      (log/info "All SSE connections cleaned up"))

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

    ;; Reset presence state
    (presence/reset-state!)

    (clojure.core/reset! system nil)
    (log/info "TUI session manager 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))
