(ns extract-linkedin-timestamp
  (:require [clojure.string :as str]))

(def timestamp-divisor (js/BigInt 4194304))

(defn by-id [id]
  (.getElementById js/document id))

(defn text-value [id]
  (some-> (by-id id) .-value str/trim))

(defn set-text! [id value]
  (when-let [el (by-id id)]
    (set! (.-textContent el) value)))

(defn safe-decode-uri [value]
  (try
    (js/decodeURIComponent value)
    (catch :default _
      value)))

(defn valid-id? [value]
  (and (string? value)
       (re-matches #"\d+" value)))

(defn linkedin-id->millis [id]
  (when (valid-id? id)
    (try
      (js/Number (/ (js/BigInt id) timestamp-divisor))
      (catch :default _
        nil))))

(defn date-from-id [id]
  (when-let [millis (linkedin-id->millis id)]
    (js/Date. millis)))

(defn format-local [date time-zone]
  (try
    (.toLocaleString date "en-GB"
                     #js {:timeZone time-zone
                          :year "numeric"
                          :month "2-digit"
                          :day "2-digit"
                          :hour "2-digit"
                          :minute "2-digit"
                          :second "2-digit"
                          :hour12 false})
    (catch :default _
      "Invalid time zone")))

(defn direct-activity-id [url]
  (let [decoded (safe-decode-uri url)]
    (or (second (re-find #"activity[:\-](\d+)" decoded))
        (second (re-find #"share-(\d+)" decoded)))))

(defn comment-ids [url]
  (let [decoded (safe-decode-uri url)
        [_ activity-id comment-id] (re-find #"comment:\(activity:(\d+),(\d+)" decoded)]
    {:activity-id activity-id
     :comment-id comment-id}))

(defn extract-timestamps [url]
  (let [{:keys [activity-id comment-id]} (comment-ids url)
        activity-id (or activity-id (direct-activity-id url))]
    {:activity-id activity-id
     :comment-id comment-id
     :activity-date (date-from-id activity-id)
     :comment-date (date-from-id comment-id)
     :canonical-url (when activity-id
                      (str "https://www.linkedin.com/feed/update/activity:" activity-id))}))

(defn line [label value]
  (when value
    (str label ": " value)))

(defn render-result [{:keys [activity-id comment-id activity-date comment-date canonical-url]} time-zone]
  (let [lines [(line "Activity ID" activity-id)
               (when activity-date
                 (line "Activity time UTC" (.toISOString activity-date)))
               (when activity-date
                 (line (str "Activity time " time-zone) (format-local activity-date time-zone)))
               (line "Comment ID" comment-id)
               (when comment-date
                 (line "Comment time UTC" (.toISOString comment-date)))
               (when comment-date
                 (line (str "Comment time " time-zone) (format-local comment-date time-zone)))
               (line "Canonical activity URL" canonical-url)]]
    (if activity-id
      (str/join "\n" (remove nil? lines))
      "No LinkedIn activity ID found in that URL.")))

(defn extract! []
  (let [url (text-value "linkedin-url")
        time-zone (or (not-empty (text-value "linkedin-timezone"))
                      "Europe/Prague")]
    (if (seq url)
      (set-text! "linkedin-result" (render-result (extract-timestamps url) time-zone))
      (set-text! "linkedin-result" "Paste a LinkedIn URL first."))))

(defn init! []
  (when-let [button (by-id "extract-linkedin-timestamp")]
    (.addEventListener button "click" extract!)))

(init!)
