;; scittle-run filter + 行の内訳 over the embedded industry rows (no build step).
;; marketplace-page.cljs と同じ形。ここが答えるのは「どの ISIC コードが、
;; どの状態で、どれだけのスコアか」だけで、数は 1 つも計算しない
;; （全部 scripts/generate-industries.cljs が埋め込んだ値をそのまま出す。
;; bp → % の割り算だけは表示のためにここでやる）。
(ns industries.page)

(def entries
  (js->clj (js/JSON.parse (.-textContent (js/document.getElementById "industries-data")))
           :keywordize-keys true))

(def by-id (into {} (map (juxt :id identity)) entries))

(defn- esc [s]
  (-> (str s) (.replaceAll "&" "&amp;") (.replaceAll "<" "&lt;") (.replaceAll ">" "&gt;")))

(defn- state-label [e]
  (if (= "implemented" (:state e)) "実装済み" "spec のみ"))

;; **未計測を 0 と書かない。** own が nil の行は「—」+ muted で出す。
(defn- score-cell [e]
  (if (some? (:own e))
    (str "<td class=\"num\">" (esc (:own e)) "</td>")
    "<td class=\"num muted\" title=\"maturity scan にこの repo が無い（スコア 0 ではない）\">未計測</td>"))

;; ── 内訳 ───────────────────────────────────────────────────────────────
;; 軸の並びは itonami-maturity の重みテーブルと同じ順。日本語の見出しは
;; ページ本文の「読み方」と同じ語を使う（同じものを 2 通りに呼ばない）。
(def axis-labels
  [[:substrate "実装量"] [:test "テスト"] [:governed "governor 部品"]
   [:ingest "出典引用"] [:docs "文書"] [:surface "公開面"] [:fresh "更新の新しさ"]])

(defn- meter-html
  "section カードと同じ .meter/.track/.fill を使う。同じ量を 2 通りの見た目で
  出すと、別の指標に見える。bp(0-10000) → %。"
  [label bp]
  (let [pct (js/Math.round (/ bp 100))]
    (str "<div class=\"meter\"><div class=\"meter-top\"><span>" (esc label)
         "</span><b>" pct "%</b></div>"
         ;; 0% でも 1% 幅は描く（track の中に何も無いと『未計測』と紛れる）。
         ;; 未計測の行はそもそもこの関数を通らない。
         "<div class=\"track\"><span class=\"fill\" style=\"width:"
         (max 1 pct) "%\"></span></div></div>")))

(defn- links-html [e]
  (let [ls (cond-> []
             (:repo e) (conj (str "<a href=\"" (esc (:repo e)) "\">repo</a>"))
             (:demo e) (conj (str "<a href=\"" (esc (:demo e)) "\">demo</a>")))]
    (if (seq ls)
      (str "<p class=\"detail-links\">" (apply str (interpose " " ls)) "</p>")
      "")))

(defn- detail-html [e]
  (str
   "<tr class=\"detail\" id=\"d-" (esc (:id e)) "\"><td colspan=\"7\">"
   (if (:axes e)
     (str
      "<div class=\"detail-grid\">"
      "<div><p class=\"detail-tier\">7 軸（観測から直接）</p>"
      "<div class=\"detail-axes\">"
      (apply str (map (fn [[k label]] (meter-html label (get (:axes e) k 0))) axis-labels))
      "</div></div>"
      "<div><p class=\"detail-tier\">畳んだ値</p><dl class=\"detail-meta\">"
      "<dt>成熟度</dt><dd>" (esc (:own e)) "</dd>"
      "<dt>依存伝播後</dt><dd>" (esc (or (:effective e) "—")) "</dd>"
      "<dt>band</dt><dd>" (esc (or (:band e) "—")) "</dd>"
      "<dt>種類</dt><dd>" (esc (or (:kind e) "—")) "</dd>"
      "<dt>層</dt><dd>" (esc (or (:layer e) "—")) "</dd>"
      "</dl>"
      "<p class=\"detail-note\">成熟度は 7 軸に重みを掛けたもので、"
      "重みは判断（repo の種類ごとに違う）。依存伝播後と band はモデルの"
      "出力で、観測ではありません。</p>"
      (links-html e)
      "</div></div>")
     (str "<p class=\"detail-note\">この repo は maturity scan に入っていないので"
          "内訳がありません。<strong>スコア 0 ではなく未計測</strong>です"
          "（平均の分母からも外してあります）。</p>"
          (links-html e)))
   "</td></tr>"))

(defn- row-html [e]
  (str "<tr><td class=\"num\">"
       "<button type=\"button\" class=\"rowtoggle\" aria-expanded=\"false\""
       " aria-controls=\"d-" (esc (:id e)) "\" data-id=\"" (esc (:id e)) "\">"
       (esc (:id e)) "</button></td>"
       "<td class=\"num\">" (esc (or (:section e) "—")) "</td>"
       "<td>" (if (:repo e)
                (str "<a href=\"" (esc (:repo e)) "\">" (esc (:name e)) "</a>")
                (esc (:name e))) "</td>"
       "<td class=\"state\">" (state-label e) "</td>"
       (score-cell e)
       "<td class=\"num" (if (:band e) "" " muted") "\">" (esc (or (:band e) "—")) "</td>"
       "<td>" (if (:demo e) (str "<a href=\"" (esc (:demo e)) "\">demo</a>") "—") "</td></tr>"))

(defn- matches? [q sec state e]
  (and (or (= q "") (.includes (.toLowerCase (str (:id e) " " (:name e))) q))
       (or (= sec "") (= sec (:section e)))
       (case state
         "" true
         "implemented" (= "implemented" (:state e))
         "spec" (= "spec" (:state e))
         "measured" (some? (:own e))
         "unmeasured" (nil? (:own e))
         true)))

(defn- render! []
  (let [q (.toLowerCase (.-value (js/document.getElementById "q")))
        sec (.-value (js/document.getElementById "sec"))
        state (.-value (js/document.getElementById "state"))
        hits (filter (partial matches? q sec state) entries)]
    ;; 絞り込みを変えると tbody を作り直すので、開いていた内訳は閉じる。
    ;; 行の同一性を保って開閉だけ残す実装もできるが、651 行を差分更新する
    ;; 価値より、状態が 1 箇所（DOM）に閉じている方が壊れにくい。
    (set! (.-innerHTML (js/document.getElementById "rows")) (apply str (map row-html hits)))
    (set! (.-hidden (js/document.getElementById "empty")) (boolean (seq hits)))))

;; 開閉は tbody への委譲。再描画のたびに 651 個の listener を張り直さない。
(defn- toggle! [btn]
  (let [id (.getAttribute btn "data-id")
        open? (= "true" (.getAttribute btn "aria-expanded"))
        row (.closest btn "tr")
        existing (js/document.getElementById (str "d-" id))]
    (if open?
      (do (.setAttribute btn "aria-expanded" "false")
          (when existing (.remove existing)))
      (do (.setAttribute btn "aria-expanded" "true")
          (when-not existing
            (.insertAdjacentHTML row "afterend" (detail-html (get by-id id))))))))

(.addEventListener (js/document.getElementById "rows") "click"
                   (fn [ev]
                     (when-let [btn (.closest (.-target ev) "button.rowtoggle")]
                       (toggle! btn))))

(doseq [id ["q" "sec" "state"]]
  (.addEventListener (js/document.getElementById id)
                     (if (= id "q") "input" "change")
                     render!))
(render!)
