#!/usr/bin/env bb
;; duck-rhizome-bridge.bb - Bridge between duck (Babashka/DuckDB) and rhizome (Haskell/Polysemy)
;; Seed 1069: [+1, -1, -1, +1, +1, +1, +1]
;;
;; Architecture:
;;   duck (Babashka)  <--JSON-RPC-->  rhizome (Haskell)
;;        |                                |
;;    xm.duckdb                     Polysemy effects
;;        |                                |
;;   Conversation                    VersAPI effect
;;   History                         LLM effect
;;
;; Usage:
;;   ./duck-rhizome-bridge.bb serve          # Start bridge server
;;   ./duck-rhizome-bridge.bb sync           # Sync xm.duckdb with rhizome state
;;   ./duck-rhizome-bridge.bb query <sql>    # Query xm.duckdb
;;   ./duck-rhizome-bridge.bb vms            # List VMs via rhizome
;;   ./duck-rhizome-bridge.bb fork <vm-id>   # Trigger withForkedSelf on VM

(require '[babashka.process :refer [shell process]]
         '[clojure.string :as str]
         '[cheshire.core :as json]
         '[babashka.fs :as fs]
         '[org.httpkit.server :as http])

;;; ============================================================
;;; Configuration
;;; ============================================================

(def PORT (or (some-> (System/getenv "BRIDGE_PORT") parse-long) 8069))

(def XM_DB_PATH
  (or (System/getenv "XM_DB_PATH")
      (str (System/getProperty "user.home") "/xm.duckdb")))

(def RHIZOME_PATH
  (or (System/getenv "RHIZOME_PATH")
      (str (System/getProperty "user.home") "/ies-rhizome")))

(def VERS_API_URL "https://api.vers.sh")

;; GF(3) trit values for bridge operations
(def TRIT_MINUS -1)  ;; Contraction (query/read)
(def TRIT_ERGODIC 0) ;; Transformation
(def TRIT_PLUS +1)   ;; Expansion (create/fork)

;;; ============================================================
;;; DuckDB Integration (xm.duckdb)
;;; ============================================================

(defn duck-query [sql]
  "Execute SQL against xm.duckdb"
  (let [result (shell {:out :string :err :string :continue true}
                      "duckdb" XM_DB_PATH "-json" "-c" sql)]
    (if (= 0 (:exit result))
      {:ok true
       :trit TRIT_MINUS  ;; Query is contraction
       :data (try (json/parse-string (:out result) true)
                  (catch Exception _ (:out result)))}
      {:ok false :error (:err result)})))

(defn get-neighborhood-stats []
  "Get neighborhood statistics from xm.duckdb"
  (duck-query "SELECT
    COUNT(DISTINCT neighborhood_id) as neighborhoods,
    COUNT(*) as total_interactions,
    SUM(CASE WHEN trit = 1 THEN 1 ELSE 0 END) as plus_count,
    SUM(CASE WHEN trit = 0 THEN 1 ELSE 0 END) as ergodic_count,
    SUM(CASE WHEN trit = -1 THEN 1 ELSE 0 END) as minus_count
    FROM xm_interactions"))

(defn get-cross-source-duplicates []
  "Find content that appears in multiple sources"
  (duck-query "SELECT content_hash, COUNT(DISTINCT source) as source_count,
    GROUP_CONCAT(DISTINCT source) as sources
    FROM xm_interactions
    GROUP BY content_hash
    HAVING COUNT(DISTINCT source) > 1
    ORDER BY source_count DESC
    LIMIT 20"))

(defn search-unified [term limit]
  "Search across unified history"
  (duck-query (str "SELECT source, content, timestamp, trit
    FROM xm_interactions
    WHERE content LIKE '%" term "%'
    ORDER BY timestamp DESC
    LIMIT " (or limit 10))))

;;; ============================================================
;;; VERS API Integration (via rhizome or direct)
;;; ============================================================

(defn vers-api-call [endpoint method body]
  "Make direct VERS API call"
  (let [api-key (System/getenv "VERS_API_KEY")
        url (str VERS_API_URL "/api/v1" endpoint)]
    (if api-key
      (let [result (shell {:out :string :err :string :continue true}
                          "curl" "-sS" "-X" method url
                          "-H" (str "Authorization: Bearer " api-key)
                          "-H" "Content-Type: application/json"
                          (when body ["-d" (json/generate-string body)]))]
        (if (= 0 (:exit result))
          {:ok true
           :trit (case method
                   "GET" TRIT_MINUS
                   "POST" TRIT_PLUS
                   "DELETE" TRIT_MINUS
                   TRIT_ERGODIC)
           :data (try (json/parse-string (:out result) true)
                      (catch Exception _ (:out result)))}
          {:ok false :error (:err result)}))
      {:ok false :error "VERS_API_KEY not set"})))

(defn list-vms []
  "List VMs via VERS API"
  (vers-api-call "/vms" "GET" nil))

(defn get-vm [vm-id]
  "Get VM details"
  (vers-api-call (str "/vms/" vm-id) "GET" nil))

(defn branch-vm [vm-id]
  "Branch a VM (withForkedSelf trigger)"
  (vers-api-call (str "/vms/" vm-id "/branch") "POST" {}))

(defn delete-vm [vm-id]
  "Delete a VM"
  (vers-api-call (str "/vms/" vm-id) "DELETE" nil))

;;; ============================================================
;;; Rhizome Integration (Haskell/Polysemy)
;;; ============================================================

(defn build-rhizome []
  "Build rhizome with nix"
  (println "Building rhizome...")
  (let [result (shell {:dir RHIZOME_PATH :out :string :err :string :continue true}
                      "nix" "build" ".#vers-client")]
    (if (= 0 (:exit result))
      {:ok true :message "Rhizome built successfully"}
      {:ok false :error (:err result)})))

(defn check-rhizome-health []
  "Check if rhizome is available"
  {:available (fs/exists? RHIZOME_PATH)
   :vers-client (fs/exists? (str RHIZOME_PATH "/vers-client"))
   :llm-effect (fs/exists? (str RHIZOME_PATH "/llm-effect"))
   :polysemy-extra (fs/exists? (str RHIZOME_PATH "/polysemy-extra"))})

;;; ============================================================
;;; Bridge State Synchronization
;;; ============================================================

(defn sync-vm-inventory []
  "Sync VM inventory from VERS to xm.duckdb"
  (println "Syncing VM inventory...")
  (let [vms-result (list-vms)]
    (if (:ok vms-result)
      (let [vms (get-in vms-result [:data :result :vms] [])
            insert-sql (str "INSERT OR REPLACE INTO vm_sync (vm_id, state, ip_address, alias, synced_at)
                             VALUES "
                            (->> vms
                                 (map (fn [vm]
                                        (str "('" (:id vm) "', "
                                             "'" (:state vm) "', "
                                             "'" (:ip_address vm) "', "
                                             "'" (:alias vm) "', "
                                             "CURRENT_TIMESTAMP)")))
                                 (str/join ", ")))]
        ;; Create sync table if needed
        (duck-query "CREATE TABLE IF NOT EXISTS vm_sync (
          vm_id TEXT PRIMARY KEY,
          state TEXT,
          ip_address TEXT,
          alias TEXT,
          synced_at TIMESTAMP)")
        (if (seq vms)
          (duck-query insert-sql)
          {:ok true :message "No VMs to sync"}))
      vms-result)))

;;; ============================================================
;;; JSON-RPC Server for Rhizome Integration
;;; ============================================================

(defn handle-rpc-request [req]
  "Handle JSON-RPC request from rhizome"
  (let [method (:method req)
        params (:params req)
        id (:id req)]
    (case method
      ;; xm.duckdb queries
      "xm.query"
      {:jsonrpc "2.0" :id id :result (duck-query (:sql params))}

      "xm.stats"
      {:jsonrpc "2.0" :id id :result (get-neighborhood-stats)}

      "xm.duplicates"
      {:jsonrpc "2.0" :id id :result (get-cross-source-duplicates)}

      "xm.search"
      {:jsonrpc "2.0" :id id :result (search-unified (:term params) (:limit params))}

      ;; VERS operations
      "vers.listVMs"
      {:jsonrpc "2.0" :id id :result (list-vms)}

      "vers.getVM"
      {:jsonrpc "2.0" :id id :result (get-vm (:vmId params))}

      "vers.branchVM"
      {:jsonrpc "2.0" :id id :result (branch-vm (:vmId params))}

      "vers.deleteVM"
      {:jsonrpc "2.0" :id id :result (delete-vm (:vmId params))}

      ;; Rhizome health
      "rhizome.health"
      {:jsonrpc "2.0" :id id :result (check-rhizome-health)}

      ;; Sync operations
      "bridge.sync"
      {:jsonrpc "2.0" :id id :result (sync-vm-inventory)}

      ;; Unknown method
      {:jsonrpc "2.0" :id id :error {:code -32601 :message "Method not found"}})))

(defn json-rpc-handler [{:keys [uri body request-method]}]
  "HTTP handler for JSON-RPC"
  (cond
    (and (= uri "/rpc") (= request-method :post))
    (let [req (json/parse-string (slurp body) true)
          result (handle-rpc-request req)]
      {:status 200
       :headers {"Content-Type" "application/json"}
       :body (json/generate-string result)})

    (= uri "/health")
    {:status 200
     :headers {"Content-Type" "application/json"}
     :body (json/generate-string
             {:status "ok"
              :xm-db (fs/exists? XM_DB_PATH)
              :rhizome (check-rhizome-health)
              :seed 1069
              :trit-sequence [+1 -1 -1 +1 +1 +1 +1]})}

    (= uri "/")
    {:status 200
     :headers {"Content-Type" "text/html"}
     :body "<html>
<head><title>Duck-Rhizome Bridge</title></head>
<body style='font-family: monospace; background: #0a0a0f; color: #e0e0e0; padding: 2rem;'>
<pre>
     ╔═══════════════════════════════════════════╗
     ║  🦆⟷🌿 DUCK-RHIZOME BRIDGE              ║
     ║  Seed 1069: [+1,-1,-1,+1,+1,+1,+1]       ║
     ╠═══════════════════════════════════════════╣
     ║  Babashka/DuckDB ←─JSON-RPC─→ Haskell    ║
     ║       duck           ↕           rhizome ║
     ║    xm.duckdb        ↕        Polysemy    ║
     ╚═══════════════════════════════════════════╝
</pre>
<h2>Endpoints</h2>
<ul>
  <li><a href='/health'>/health</a> - Bridge health status</li>
  <li>POST /rpc - JSON-RPC endpoint</li>
</ul>
<h2>JSON-RPC Methods</h2>
<pre>
xm.query      {sql: \"SELECT...\"}        Query xm.duckdb
xm.stats      {}                         Neighborhood stats
xm.duplicates {}                         Cross-source duplicates
xm.search     {term: \"...\", limit: 10}  Search unified history

vers.listVMs  {}                         List VERS VMs
vers.getVM    {vmId: \"...\"}             Get VM details
vers.branchVM {vmId: \"...\"}             Fork VM (withForkedSelf)
vers.deleteVM {vmId: \"...\"}             Delete VM

rhizome.health {}                        Check rhizome availability
bridge.sync    {}                        Sync VM inventory to xm.duckdb
</pre>
</body></html>"}

    :else
    {:status 404 :body "Not Found"}))

(defn start-server []
  "Start the bridge server"
  (println (str "🦆⟷🌿 Duck-Rhizome Bridge starting on port " PORT))
  (println (str "   xm.duckdb: " XM_DB_PATH))
  (println (str "   rhizome:   " RHIZOME_PATH))
  (println (str "   Seed 1069: [+1,-1,-1,+1,+1,+1,+1]"))
  (http/run-server json-rpc-handler {:port PORT})
  (println "✅ Bridge running. Press Ctrl+C to stop.")
  @(promise))

;;; ============================================================
;;; CLI
;;; ============================================================

(defn print-usage []
  (println "
🦆⟷🌿 duck-rhizome-bridge - Bridge between duck and rhizome

Usage: duck-rhizome-bridge.bb [command] [args...]

Commands:
  serve              Start JSON-RPC bridge server (port 8069)
  sync               Sync VM inventory to xm.duckdb
  query <sql>        Query xm.duckdb directly
  stats              Show neighborhood statistics
  vms                List VMs via VERS API
  get-vm <vm-id>     Get VM details
  fork <vm-id>       Branch VM (trigger withForkedSelf)
  health             Show bridge health status
  build              Build rhizome with nix

Environment:
  BRIDGE_PORT        Server port (default: 8069)
  XM_DB_PATH         Path to xm.duckdb
  RHIZOME_PATH       Path to rhizome repo
  VERS_API_KEY       VERS API key for VM operations
"))

(defn -main [& args]
  (let [cmd (first args)]
    (case cmd
      "serve" (start-server)

      "sync" (let [result (sync-vm-inventory)]
               (println (json/generate-string result {:pretty true})))

      "query" (let [sql (str/join " " (rest args))
                    result (duck-query sql)]
                (println (json/generate-string result {:pretty true})))

      "stats" (let [result (get-neighborhood-stats)]
                (println (json/generate-string result {:pretty true})))

      "vms" (let [result (list-vms)]
              (println (json/generate-string result {:pretty true})))

      "get-vm" (if-let [vm-id (second args)]
                 (let [result (get-vm vm-id)]
                   (println (json/generate-string result {:pretty true})))
                 (println "Usage: get-vm <vm-id>"))

      "fork" (if-let [vm-id (second args)]
               (do
                 (println (str "🌿 Branching VM " vm-id " (withForkedSelf trigger)..."))
                 (let [result (branch-vm vm-id)]
                   (println (json/generate-string result {:pretty true}))))
               (println "Usage: fork <vm-id>"))

      "health" (let [result {:xm-db (fs/exists? XM_DB_PATH)
                             :rhizome (check-rhizome-health)
                             :vers-api-key (boolean (System/getenv "VERS_API_KEY"))
                             :seed 1069}]
                 (println (json/generate-string result {:pretty true})))

      "build" (let [result (build-rhizome)]
                (println (json/generate-string result {:pretty true})))

      (nil "-h" "--help" "help") (print-usage)

      (do
        (println (str "Unknown command: " cmd))
        (print-usage)
        (System/exit 1)))))

(apply -main *command-line-args*)