;; Social navigation, header, post cards, timeline, compose

;; --- Navigation ---

(defcomp ~federation-nav-choose-username (&key url)
  (nav :class "flex gap-3 text-sm items-center"
    (a :href url :class "px-2 py-1 rounded hover:bg-stone-200 font-bold" "Choose username")))

(defcomp ~federation-nav-notification-link (&key href cls count-url)
  (a :href href :class cls "Notifications"
    (span :sx-get count-url :sx-trigger "load, every 30s" :sx-swap "innerHTML"
      :class "absolute -top-2 -right-3 text-xs bg-red-500 text-white rounded-full px-1 empty:hidden")))

(defcomp ~federation-nav-bar (&key items)
  (nav :class "flex gap-3 text-sm items-center flex-wrap" items))

(defcomp ~federation-social-header (&key nav)
  (div :id "social-row" :class "flex flex-col items-center md:flex-row justify-center md:justify-between w-full p-1 bg-sky-400"
    (div :class "w-full flex flex-row items-center gap-2 flex-wrap" nav)))

;; --- Post card ---

(defcomp ~federation-boost-label (&key name)
  (div :class "text-sm text-stone-500 mb-2" "Boosted by " name))

;; Aliases — delegate to shared ~avatar
(defcomp ~federation-avatar-img (&key src cls)
  (~avatar :src src :cls cls))

(defcomp ~federation-avatar-placeholder (&key cls initial)
  (~avatar :cls cls :initial initial))

(defcomp ~federation-content (&key content summary)
  (if summary
    (details :class "mt-2"
      (summary :class "text-stone-500 cursor-pointer" "CW: " (~rich-text :html summary))
      (div :class "mt-2 prose prose-sm prose-stone max-w-none" (~rich-text :html content)))
    (div :class "mt-2 prose prose-sm prose-stone max-w-none" (~rich-text :html content))))

(defcomp ~federation-original-link (&key url)
  (a :href url :target "_blank" :rel "noopener"
    :class "text-sm text-stone-400 hover:underline mt-1 inline-block" "original"))

(defcomp ~federation-post-card (&key boost avatar actor-name actor-username domain time content original interactions)
  (article :class "bg-white rounded-lg shadow-sm border border-stone-200 p-4 mb-4"
    boost
    (div :class "flex items-start gap-3"
      avatar
      (div :class "flex-1 min-w-0"
        (div :class "flex items-baseline gap-2"
          (span :class "font-semibold text-stone-900" actor-name)
          (span :class "text-sm text-stone-500" "@" actor-username domain)
          (span :class "text-sm text-stone-400 ml-auto" time))
        content original interactions))))

;; --- Interaction buttons ---

(defcomp ~federation-reply-link (&key url)
  (a :href url :class "hover:text-stone-700" "Reply"))

(defcomp ~federation-like-form (&key action target oid ainbox csrf cls icon count)
  (form :sx-post action :sx-target target :sx-swap "innerHTML"
    (input :type "hidden" :name "object_id" :value oid)
    (input :type "hidden" :name "author_inbox" :value ainbox)
    (input :type "hidden" :name "csrf_token" :value csrf)
    (button :type "submit" :class cls (span icon) " " count)))

(defcomp ~federation-boost-form (&key action target oid ainbox csrf cls count)
  (form :sx-post action :sx-target target :sx-swap "innerHTML"
    (input :type "hidden" :name "object_id" :value oid)
    (input :type "hidden" :name "author_inbox" :value ainbox)
    (input :type "hidden" :name "csrf_token" :value csrf)
    (button :type "submit" :class cls (span "\u21bb") " " count)))

(defcomp ~federation-interaction-buttons (&key like boost reply)
  (div :class "flex items-center gap-4 mt-3 text-sm text-stone-500"
    like boost reply))

;; --- Timeline ---

(defcomp ~federation-scroll-sentinel (&key url)
  (div :sx-get url :sx-trigger "revealed" :sx-swap "outerHTML"))

(defcomp ~federation-compose-button (&key url)
  (a :href url :class "bg-stone-800 text-white px-4 py-2 rounded hover:bg-stone-700" "Compose"))

(defcomp ~federation-timeline-page (&key label compose timeline)
  (div :class "flex items-center justify-between mb-6"
    (h1 :class "text-2xl font-bold" label " Timeline")
    compose)
  (div :id "timeline" timeline))

;; --- Compose ---

(defcomp ~federation-compose-reply (&key reply-to)
  (input :type "hidden" :name "in_reply_to" :value reply-to)
  (div :class "text-sm text-stone-500" "Replying to " (span :class "font-mono" reply-to)))

(defcomp ~federation-compose-form (&key action csrf reply)
  (h1 :class "text-2xl font-bold mb-6" "Compose")
  (form :method "post" :action action :class "space-y-4"
    (input :type "hidden" :name "csrf_token" :value csrf)
    reply
    (textarea :name "content" :rows "6" :maxlength "5000" :required true
      :class "w-full border border-stone-300 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-stone-500"
      :placeholder "What's on your mind?")
    (div :class "flex items-center justify-between"
      (select :name "visibility" :class "border border-stone-300 rounded px-3 py-1.5 text-sm"
        (option :value "public" "Public")
        (option :value "unlisted" "Unlisted")
        (option :value "followers" "Followers only"))
      (button :type "submit" :class "bg-stone-800 text-white px-6 py-2 rounded hover:bg-stone-700" "Publish"))))

;; ---------------------------------------------------------------------------
;; Assembled social nav — replaces Python _social_nav_sx
;; ---------------------------------------------------------------------------

(defcomp ~federation-social-nav (&key actor)
  (if (not actor)
    (~federation-nav-choose-username :url (url-for "identity.choose_username_form"))
    (let* ((rp (request-path))
           (links (list
             (dict :endpoint "social.defpage_home_timeline" :label "Timeline")
             (dict :endpoint "social.defpage_public_timeline" :label "Public")
             (dict :endpoint "social.defpage_compose_form" :label "Compose")
             (dict :endpoint "social.defpage_following_list" :label "Following")
             (dict :endpoint "social.defpage_followers_list" :label "Followers")
             (dict :endpoint "social.defpage_search" :label "Search"))))
      (~federation-nav-bar
        :items (<>
          (map (lambda (lnk)
            (let* ((href (url-for (get lnk "endpoint")))
                   (bold (if (= rp href) " font-bold" "")))
              (a :href href
                 :class (str "px-2 py-1 rounded hover:bg-stone-200" bold)
                 (get lnk "label"))))
            links)
          (let* ((notif-url (url-for "social.defpage_notifications"))
                 (notif-bold (if (= rp notif-url) " font-bold" "")))
            (~federation-nav-notification-link
              :href notif-url
              :cls (str "px-2 py-1 rounded hover:bg-stone-200 relative" notif-bold)
              :count-url (url-for "social.notification_count")))
          (a :href (url-for "activitypub.actor_profile" :username (get actor "preferred_username"))
             :class "px-2 py-1 rounded hover:bg-stone-200"
             (str "@" (get actor "preferred_username"))))))))

;; ---------------------------------------------------------------------------
;; Assembled post card — replaces Python _post_card_sx
;; ---------------------------------------------------------------------------

(defcomp ~federation-post-card-from-data (&key item actor)
  (let* ((boosted-by (get item "boosted_by"))
         (actor-icon (get item "actor_icon"))
         (actor-name (or (get item "actor_name") "?"))
         (actor-username (or (get item "actor_username") ""))
         (actor-domain (or (get item "actor_domain") ""))
         (content (or (get item "content") ""))
         (summary (get item "summary"))
         (published (or (get item "published") ""))
         (url (get item "url"))
         (post-type (or (get item "post_type") ""))
         (oid (or (get item "object_id") ""))
         (safe-id (replace (replace oid "/" "_") ":" "_"))
         (initial (if (and (not actor-icon) actor-name)
                    (upper (slice actor-name 0 1)) "?")))
    (~federation-post-card
      :boost (when boosted-by (~federation-boost-label :name (escape boosted-by)))
      :avatar (~avatar
        :src actor-icon
        :cls (if actor-icon "w-10 h-10 rounded-full"
               "w-10 h-10 rounded-full bg-stone-300 flex items-center justify-center text-stone-600 font-bold text-sm")
        :initial (when (not actor-icon) initial))
      :actor-name (escape actor-name)
      :actor-username (escape actor-username)
      :domain (if actor-domain (str "@" (escape actor-domain)) "")
      :time published
      :content (if summary
                 (~federation-content :content content :summary (escape summary))
                 (~federation-content :content content))
      :original (when (and url (= post-type "remote"))
                  (~federation-original-link :url url))
      :interactions (when actor
        (let* ((csrf (csrf-token))
               (liked (get item "liked_by_me"))
               (boosted-me (get item "boosted_by_me"))
               (lcount (or (get item "like_count") 0))
               (bcount (or (get item "boost_count") 0))
               (ainbox (or (get item "author_inbox") ""))
               (target (str "#interactions-" safe-id)))
          (div :id (str "interactions-" safe-id)
            (~federation-interaction-buttons
              :like (~federation-like-form
                :action (url-for (if liked "social.unlike" "social.like"))
                :target target :oid oid :ainbox ainbox :csrf csrf
                :cls (str "flex items-center gap-1 " (if liked "text-red-500 hover:text-red-600" "hover:text-red-500"))
                :icon (if liked "\u2665" "\u2661") :count (str lcount))
              :boost (~federation-boost-form
                :action (url-for (if boosted-me "social.unboost" "social.boost"))
                :target target :oid oid :ainbox ainbox :csrf csrf
                :cls (str "flex items-center gap-1 " (if boosted-me "text-green-600 hover:text-green-700" "hover:text-green-600"))
                :count (str bcount))
              :reply (when oid
                (~federation-reply-link
                  :url (url-for "social.defpage_compose_form" :reply-to oid))))))))))

;; ---------------------------------------------------------------------------
;; Assembled timeline items — replaces Python _timeline_items_sx
;; ---------------------------------------------------------------------------

(defcomp ~federation-timeline-items (&key items timeline-type actor next-url)
  (<>
    (map (lambda (item)
      (~federation-post-card-from-data :item item :actor actor))
      items)
    (when next-url
      (~federation-scroll-sentinel :url next-url))))

;; Assembled timeline content — replaces Python _timeline_content_sx
(defcomp ~federation-timeline-content (&key items timeline-type actor)
  (let* ((label (if (= timeline-type "home") "Home" "Public")))
    (~federation-timeline-page
      :label label
      :compose (when actor
        (~federation-compose-button :url (url-for "social.defpage_compose_form")))
      :timeline (~federation-timeline-items
        :items items :timeline-type timeline-type :actor actor
        :next-url (when (not (empty? items))
          (url-for (str "social." timeline-type "_timeline_page")
                   :before (get (last items) "before_cursor")))))))

;; Assembled compose content — replaces Python _compose_content_sx
(defcomp ~federation-compose-content (&key reply-to)
  (~federation-compose-form
    :action (url-for "social.compose_submit")
    :csrf (csrf-token)
    :reply (when reply-to
      (~federation-compose-reply :reply-to (escape reply-to)))))
