(ns app
  (:require
   [reagent.core :as r]
   [reagent.dom :as rdom]
   [fuseme.core :as fuse]))

;; ── ui state ────────────────────────────────────────────────────────────────

(def state (r/atom {:stage :pick
                     :original-src nil
                     :fused-svg nil
                     :original-width nil
                     :original-height nil
                     :colors-summary nil
                     :error-msg nil}))

(defn- file->data-url [file callback]
  (let [reader (js/FileReader.)]
    (set! (.-onload reader) #(callback (.. % -target -result)))
    (.readAsDataURL reader file)))

(defn- download-svg! [svg-string filename]
  (let [blob (js/Blob. #js [svg-string] #js {:type "image/svg+xml"})
        url (js/URL.createObjectURL blob)
        a (.createElement js/document "a")]
    (set! (.-href a) url)
    (set! (.-download a) filename)
    (.click a)
    (js/URL.revokeObjectURL url)))

(defn- handle-file-selected [file]
  (swap! state assoc :stage :converting :error-msg nil)
  (file->data-url file
    (fn [data-url]
      (swap! state assoc :original-src data-url)
      (fuse/process-data-url! data-url
        (fn [result]
          (swap! state merge {:stage :done
                              :fused-svg (:svg result)
                              :original-width (:original-width result)
                              :original-height (:original-height result)
                              :colors-summary (:colors-summary result)}))
        (fn [error]
          (swap! state assoc :stage :error :error-msg error))))))

;; ── styles ──────────────────────────────────────────────────────────────────

(def app-styles
  (str
   "*{box-sizing:border-box;margin:0;padding:0}"
   "body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#1a1a2e;color:#e0e0e0;min-height:100vh}"
   ".app{max-width:1100px;margin:0 auto;padding:2rem}"
   "h1{font-size:1.8rem;margin-bottom:.5rem;color:#e94560}"
   "h2{font-size:1.4rem;margin-bottom:1rem}"
   "h3{font-size:1rem;margin-bottom:.5rem;color:#a0a0b0}"
   ".pick-container{text-align:center;padding:4rem 2rem}"
   ".pick-container p{margin-bottom:1.5rem;color:#a0a0b0}"
   ".hint{font-size:.85rem;margin-top:1rem;color:#666}"
   ".file-input-wrapper{display:inline-block;padding:2.5rem 3rem;border:3px dashed #555;border-radius:16px;cursor:pointer;transition:all .2s;background:#1e1e3a}"
   ".file-input-wrapper:hover{border-color:#e94560;background:#252545}"
   ".file-input-wrapper input{font-size:1rem;color:#e0e0e0;cursor:pointer}"
   ".converting-container{text-align:center;padding:4rem}"
   ".spinner{width:40px;height:40px;margin:2rem auto;border:3px solid #333;border-top-color:#e94560;border-radius:50%;animation:spin .8s linear infinite}"
   "@keyframes spin{to{transform:rotate(360deg)}}"
   ".preview-row{display:flex;gap:2rem;flex-wrap:wrap;margin-bottom:2rem;align-items:flex-start}"
   ".preview-box{flex:1 1 45%;min-width:300px}"
   ".preview-box h3{margin-bottom:.75rem}"
   ".original-preview{border:1px solid #333;border-radius:8px;display:block}"
   ".fused-preview{border:1px solid #333;border-radius:8px;overflow:auto;background:#16213e;padding:1rem;max-height:80vh}"
   ".fused-preview svg{display:block}"
   ".image-info{font-size:.85rem;color:#666;margin-top:.5rem}"
   ".colors-box{background:#16213e;border:1px solid #333;border-radius:8px;padding:1rem 1.5rem;margin-bottom:1.5rem}"
   ".colors-box pre{font-family:monospace;font-size:.9rem;color:#e0e0e0}"
   ".actions{display:flex;gap:1rem}"
   ".download-btn{padding:.75rem 2rem;background:#e94560;color:#fff;border:none;border-radius:8px;font-size:1rem;cursor:pointer;font-weight:600;transition:background .2s}"
   ".download-btn:hover{background:#c73a52}"
   ".reset-btn{padding:.75rem 2rem;background:#333;color:#e0e0e0;border:1px solid #444;border-radius:8px;font-size:1rem;cursor:pointer;transition:background .2s}"
   ".reset-btn:hover{background:#444}"
   ".error-container{text-align:center;padding:4rem;background:#2a1a1a;border-radius:12px}"
   ".error-container button{margin-top:1.5rem;padding:.75rem 2rem;background:#e94560;color:#fff;border:none;border-radius:8px;cursor:pointer}"))

(defn inject-styles! []
  (let [el (.createElement js/document "style")]
    (set! (.-textContent el) app-styles)
    (.appendChild (.-head js/document) el)))

;; ── views ───────────────────────────────────────────────────────────────────

(defn- pick-view []
  [:div.pick-container
   [:h1 "fuseme — pixel art to fuse bead pattern"]
   [:p "convert your pixel art into a labeled fuse bead pattern right in your browser."]
   [:div.file-input-wrapper
    [:input {:type "file"
             :accept "image/png"
             :on-change (fn [e]
                          (when-let [file (aget (.. e -target -files) 0)]
                            (handle-file-selected file)))}]]
   [:p.hint "select a .png pixel art file to get started"]])

(defn- converting-view []
  [:div.converting-container
   [:p "converting your image..."]
   [:div.spinner]])

(defn- result-view [{:keys [original-src fused-svg original-width original-height colors-summary]}]
  [:div.result-container
   [:h2 "your fuse bead pattern"]
   [:div.preview-row
    [:div.preview-box
     [:h3 "original"]
     [:img.original-preview {:src original-src
                              :style {:max-width "400px"
                                      :image-rendering "pixelated"}}]
     [:p.image-info (str original-width " x " original-height " pixels")]]
    [:div.preview-box
     [:h3 "fused pattern"]
     [:div.fused-preview {:dangerouslySetInnerHTML {:__html fused-svg}}]]]
   (when colors-summary
     [:div.colors-box
      [:h3 "color key"]
      [:pre colors-summary]])
   [:div.actions
    [:button.download-btn {:on-click #(download-svg! fused-svg "pattern.fused.svg")}
     "download svg"]
    [:button.reset-btn {:on-click #(reset! state {:stage :pick})}
     "convert another"]]])

(defn- error-view [{:keys [error-msg]}]
  [:div.error-container
   [:h2 "something went wrong"]
   [:p error-msg]
   [:button {:on-click #(reset! state {:stage :pick})} "try again"]])

(defn main-panel []
  (let [{:keys [stage] :as s} @state]
    [:div.app
     (cond
       (= stage :done) (result-view s)
       (= stage :error) (error-view s)
       (= stage :converting) (converting-view)
       :else (pick-view))]))

;; ── boot ────────────────────────────────────────────────────────────────────

(inject-styles!)
(js/console.log "fuseme: mounting app...")
(rdom/render [main-panel] (.getElementById js/document "app"))
(js/console.log "fuseme: mounted")
