(ns ajet.chat.web.routes
  "Reitit router for the Web Session Manager.

   Routes fall into three categories:
   1. Page routes (GET /app/*) -- full Hiccup page renders
   2. SSE endpoint (GET /sse/events) -- Datastar SSE stream
   3. Signal handlers (POST /web/*) -- browser actions proxied to API"
  (:require [clojure.tools.logging :as log]
            [clojure.data.json :as json]
            [reitit.ring :as ring]
            [ring.middleware.params :refer [wrap-params]]
            [ring.middleware.multipart-params :refer [wrap-multipart-params]]
            [ring.middleware.keyword-params :refer [wrap-keyword-params]]
            [ajet.chat.shared.api-client :as api]
            [ajet.chat.shared.logging :as logging]
            [ajet.chat.web.handlers :as handlers]
            [ajet.chat.web.layout :as layout]
            [ajet.chat.web.sse :as sse]))

;;; ---------------------------------------------------------------------------
;;; Middleware
;;; ---------------------------------------------------------------------------

(defn wrap-system
  "Inject system components into each request under :system."
  [handler system]
  (fn [request]
    (handler (assoc request :system system))))

(defn wrap-json-body
  "Parse JSON request bodies and merge into :params.
   Datastar v1 sends signals as application/json POST bodies."
  [handler]
  (fn [request]
    (if (and (some-> (get-in request [:headers "content-type"])
                     (.contains "application/json"))
             (:body request))
      (try
        (let [body-str (if (string? (:body request))
                         (:body request)
                         (slurp (:body request)))
              parsed   (when-not (clojure.string/blank? body-str)
                         (json/read-str body-str :key-fn keyword))]
          (handler (-> request
                       (assoc :body-params (or parsed {}))
                       (update :params merge (or parsed {})))))
        (catch Exception _
          (handler request)))
      (handler request))))

(defn wrap-user-context
  "Extract Auth GW headers into request keys for convenience."
  [handler]
  (fn [request]
    (let [headers (:headers request)
          user-id      (get headers "x-user-id")
          user-role    (get headers "x-user-role")
          community-id (get headers "x-community-id")]
      (handler (cond-> request
                 user-id      (assoc :user-id user-id)
                 user-role    (assoc :user-role user-role)
                 community-id (assoc :community-id community-id))))))

(defn wrap-exception-handler
  "Catch-all exception handler. Returns a simple HTML error page."
  [handler]
  (fn [request]
    (try
      (handler request)
      (catch clojure.lang.ExceptionInfo e
        (let [data (ex-data e)]
          (if (= :ajet.chat/api-error (:type data))
            {:status (:status data 500)
             :headers {"Content-Type" "text/html; charset=utf-8"}
             :body (str "<html><body><h1>Error " (:status data) "</h1>"
                        "<p>" (ex-message e) "</p></body></html>")}
            (do
              (log/error e "Unhandled exception" (pr-str data))
              {:status 500
               :headers {"Content-Type" "text/html; charset=utf-8"}
               :body "<html><body><h1>Internal Server Error</h1></body></html>"}))))
      (catch Exception e
        (log/error e "Unhandled exception")
        {:status 500
         :headers {"Content-Type" "text/html; charset=utf-8"}
         :body "<html><body><h1>Internal Server Error</h1></body></html>"}))))

;;; ---------------------------------------------------------------------------
;;; Page Handlers
;;; ---------------------------------------------------------------------------

(defn index-handler
  "GET / -- redirect to /app if user has communities, /setup otherwise."
  [request]
  (let [ctx (handlers/build-api-ctx request)]
    (try
      (let [communities (api/get-communities ctx)]
        (if (seq communities)
          {:status 302 :headers {"Location" "/app"} :body ""}
          {:status 302 :headers {"Location" "/setup"} :body ""}))
      (catch Exception _
        {:status 302 :headers {"Location" "/setup"} :body ""}))))

(defn app-handler
  "GET /app -- main chat page. Fetches communities, channels, messages for initial render."
  [request]
  (let [ctx          (handlers/build-api-ctx request)
        user         (api/get-me ctx)
        communities  (api/get-communities ctx)
        community    (first communities)
        community-id (when community (str (:id community)))
        channels     (when community-id
                       (api/get-channels ctx community-id))
        channel      (first (filter #(= "text" (name (or (:type %) :text))) channels))
        channel-id   (when channel (str (:id channel)))
        messages     (when channel-id
                       (api/get-messages ctx channel-id {:limit 50}))
        categories   (when community-id
                       (api/get-categories ctx community-id))
        unread       (try (api/get-unread-count ctx) (catch Exception _ {:count 0}))]
    {:status  200
     :headers {"Content-Type" "text/html; charset=utf-8"}
     :body    (layout/app-page {:user         user
                                :communities  communities
                                :community    community
                                :channels     channels
                                :channel      channel
                                :messages     (or messages [])
                                :categories   categories
                                :unread-count (:count unread 0)})}))

(defn channel-handler
  "GET /app/channel/:id -- direct link to a specific channel."
  [request]
  (let [channel-id   (get-in request [:path-params :id])
        ctx          (handlers/build-api-ctx request)
        user         (api/get-me ctx)
        channel      (api/get-channel ctx channel-id)
        community-id (str (:community-id channel))
        communities  (api/get-communities ctx)
        community    (first (filter #(= (str (:id %)) community-id) communities))
        channels     (api/get-channels ctx community-id)
        messages     (api/get-messages ctx channel-id {:limit 50})
        categories   (api/get-categories ctx community-id)
        unread       (try (api/get-unread-count ctx) (catch Exception _ {:count 0}))]
    {:status  200
     :headers {"Content-Type" "text/html; charset=utf-8"}
     :body    (layout/app-page {:user         user
                                :communities  communities
                                :community    community
                                :channels     channels
                                :channel      channel
                                :messages     (or messages [])
                                :categories   categories
                                :unread-count (:count unread 0)})}))

(defn dm-handler
  "GET /app/dm/:id -- direct link to a DM channel."
  [request]
  (let [dm-id        (get-in request [:path-params :id])
        ctx          (handlers/build-api-ctx request)
        user         (api/get-me ctx)
        communities  (api/get-communities ctx)
        dms          (api/get-dms ctx)
        active-dm    (api/get-channel ctx dm-id)
        messages     (api/get-messages ctx dm-id {:limit 50})
        unread       (try (api/get-unread-count ctx) (catch Exception _ {:count 0}))]
    {:status  200
     :headers {"Content-Type" "text/html; charset=utf-8"}
     :body    (layout/app-page {:user         user
                                :communities  communities
                                :community    nil
                                :channels     []
                                :channel      active-dm
                                :messages     (or messages [])
                                :categories   []
                                :unread-count (:count unread 0)
                                :dm-view?     true
                                :dms          dms})}))

(defn setup-handler
  "GET /setup -- community creation wizard."
  [request]
  {:status  200
   :headers {"Content-Type" "text/html; charset=utf-8"}
   :body    (layout/setup-page)})

;;; ---------------------------------------------------------------------------
;;; Router
;;; ---------------------------------------------------------------------------

(defn routes []
  [["/" {:get {:handler index-handler}}]

   ["/app" {:get {:handler app-handler}}]
   ["/app/channel/:id" {:get {:handler channel-handler}}]
   ["/app/dm/:id" {:get {:handler dm-handler}}]

   ["/setup" {:get {:handler setup-handler}}]

   ;; SSE stream for real-time updates
   ["/sse/events" {:get {:handler sse/sse-handler}}]

   ;; Signal handlers (POST actions from browser)
   ["/web"
    ["/messages" {:post {:handler handlers/send-message-handler}}]
    ["/messages/older" {:post {:handler handlers/load-older-handler}}]
    ["/messages/:id/edit" {:post {:handler handlers/edit-message-handler}}]
    ["/messages/:id/delete" {:post {:handler handlers/delete-message-handler}}]
    ["/reactions" {:post {:handler handlers/add-reaction-handler}}]
    ["/reactions/remove" {:post {:handler handlers/remove-reaction-handler}}]
    ["/navigate" {:post {:handler handlers/navigate-handler}}]
    ["/read" {:post {:handler handlers/mark-read-handler}}]
    ["/upload" {:post {:handler handlers/upload-handler}}]
    ["/typing" {:post {:handler handlers/typing-handler}}]
    ["/search" {:post {:handler handlers/search-handler}}]
    ["/command" {:post {:handler handlers/command-handler}}]
    ["/communities" {:post {:handler handlers/create-community-handler}}]
    ["/channels" {:post {:handler handlers/create-channel-handler}}]
    ["/health" {:get {:handler handlers/health-handler}}]]])

(defn app
  "Build the Ring handler with all middleware applied."
  [system]
  (ring/ring-handler
    (ring/router (routes))
    (ring/create-default-handler
      {:not-found (constantly {:status 404
                               :headers {"Content-Type" "text/html; charset=utf-8"}
                               :body "<html><body><h1>404 Not Found</h1></body></html>"})})
    {:middleware [[wrap-system system]
                  wrap-json-body
                  wrap-params
                  wrap-keyword-params
                  wrap-multipart-params
                  wrap-user-context
                  logging/wrap-request-logging
                  wrap-exception-handler]}))
