;; sx-proxy — SX-based Reverse Proxy
;; Plan: a Caddy-style reverse proxy where routes, TLS, middleware,
;; and load balancing are SX s-expressions manipulated by macros.

(defcomp ()
  (~docs/page :title "sx-proxy: Reverse Proxy in SX"

    (~docs/section :title "Vision" :id "vision"
      (p "A reverse proxy where routing rules, TLS configuration, middleware chains, "
         "and load balancing policies are SX. Not a config file that gets parsed — "
         "actual SX that gets evaluated. Macros generate routes from service definitions. "
         "The proxy config is derived from the same stack definition as the deployment.")
      (p "Caddy's Caddyfile is close — it's almost a language. But it stops short. "
         "No functions, no macros, no computed values, no integration with the rest of the stack. "
         "sx-proxy goes all the way: the proxy configuration " (em "is") " the application."))

    (~docs/section :title "Why" :id "why"
      (p "Every proxy config language reinvents the same features badly:")
      (ul (~tw :tokens "space-y-2 text-stone-600 list-disc pl-5")
        (li "Nginx: custom DSL with if-is-evil, no real conditionals, include for composition")
        (li "Caddy: Caddyfile is nice but no functions, no macros, no types")
        (li "Traefik: labels on Docker containers — stringly-typed, no validation")
        (li "HAProxy: line-oriented config, ACLs as string matching, no abstraction"))
      (p "All of them: separate config from application. "
         "SX unifies them. The proxy reads the same service definitions "
         "that the orchestrator deploys."))

    (~docs/section :title "Route Definitions" :id "routes"
      (p "Routes as SX, with macros for common patterns:")
      (highlight ";; Basic route definition
(route blog.rose-ash.com
  :upstream \"http://blog:8000\"
  :tls :auto)

;; Service route macro — generates from service definition
(defmacro service-route (service)
  (let ((domain (service-domain service))
        (port (service-port service)))
    `(route ,domain
       :upstream ,(str \"http://\" (get service \"name\") \":\" port)
       :tls :auto
       :headers {:X-Forwarded-Proto \"https\"
                 :X-Real-IP (client-ip)})))

;; Generate all routes from the stack definition
(define routes
  (map service-route (get rose-ash-stack \"services\")))" "lisp")
      (p "One macro generates all routes from the same service definitions "
         "that sx-swarm uses for deployment. Change the service, the route updates."))

    (~docs/section :title "Middleware as Composition" :id "middleware"
      (p "Middleware chains are function composition:")
      (highlight ";; Middleware are functions: request -> response -> response
(define rate-limit
  (middleware :name :rate-limit
    :window \"1m\" :max 100 :key (client-ip)))

(define cors
  (middleware :name :cors
    :origins (list \"*.rose-ash.com\")
    :methods (list :GET :POST :PUT :DELETE)
    :headers (list :Authorization :Content-Type)))

(define auth-required
  (middleware :name :auth
    :provider :oauth2
    :redirect \"https://account.rose-ash.com/login\"))

;; Compose middleware chains
(define public-chain
  (chain rate-limit cors))

(define private-chain
  (chain rate-limit cors auth-required))

;; Apply to routes
(route account.rose-ash.com
  :upstream \"http://account:8000\"
  :middleware private-chain
  :tls :auto)" "lisp")
      (p "Middleware is data. Chains compose with " (code "chain") ". "
         "Apply different chains to different routes. "
         "No nginx location blocks, no Caddy handle nesting."))

    (~docs/section :title "TLS Configuration" :id "tls"
      (p "TLS as SX with macro-generated cert management:")
      (highlight ";; Auto TLS via ACME (like Caddy)
(define tls-auto
  {:provider :acme
   :ca \"https://acme-v02.api.letsencrypt.org/directory\"
   :email \"admin@rose-ash.com\"
   :storage \"/data/certs\"})

;; Wildcard via DNS challenge
(define tls-wildcard
  {:provider :acme
   :ca \"https://acme-v02.api.letsencrypt.org/directory\"
   :dns-challenge {:provider :cloudflare
                   :api-token (from-secret \"cf-token\")}
   :domains (list \"*.rose-ash.com\")})

;; Per-route TLS macro
(defmacro with-tls (config &rest routes)
  `(map (fn (r) (assoc r :tls ,config)) (list ,@routes)))" "lisp"))

    (~docs/section :title "Load Balancing" :id "load-balancing"
      (p "Load balancing policies as SX values:")
      (highlight ";; Load balancing strategies
(define round-robin (lb :strategy :round-robin))
(define least-conn  (lb :strategy :least-connections))
(define ip-hash     (lb :strategy :ip-hash))

;; Health-checked upstream pool
(define blog-pool
  (upstream-pool
    :backends (list \"blog-1:8000\" \"blog-2:8000\" \"blog-3:8000\")
    :strategy least-conn
    :health-check {:path \"/health\"
                   :interval \"10s\"
                   :timeout \"3s\"
                   :unhealthy-threshold 3}))

(route blog.rose-ash.com
  :upstream blog-pool
  :tls :auto)" "lisp"))

    (~docs/section :title "Dynamic Reconfiguration" :id "dynamic"
      (p "The proxy evaluates SX — so config can be dynamic:")
      (ul (~tw :tokens "space-y-2 text-stone-600 list-disc pl-5")
        (li (strong "Hot reload") " — change the SX, proxy re-evaluates. No restart.")
        (li (strong "API-driven") " — sx-post new routes. The proxy is an SX app with endpoints.")
        (li (strong "Swarm-aware") " — subscribe to swarm-status changes, "
            "auto-update upstreams when services scale or move.")
        (li (strong "Feature flags") " — route traffic based on SX predicates "
            "(header match, percentage split, user attribute).")
        (li (strong "A/B testing") " — split traffic via SX expressions, "
            "not nginx map blocks or Traefik weighted services."))
      (highlight ";; Traffic splitting — 90% to v2, 10% to v1
(route blog.rose-ash.com
  :upstream (traffic-split
    {:weight 90 :backend \"blog-v2:8000\"}
    {:weight 10 :backend \"blog-v1:8000\"})
  :tls :auto)

;; Feature flag routing
(route blog.rose-ash.com
  :upstream (if (header? \"X-Beta\" \"true\")
              \"blog-beta:8000\"
              \"blog-prod:8000\")
  :tls :auto)" "lisp"))

    (~docs/section :title "Integration with sx-swarm" :id "integration"
      (p "sx-proxy and sx-swarm share the same service definitions:")
      (highlight ";; One source of truth
(defservice blog
  :image \"rose-ash/blog:latest\"
  :port 8000
  :domain \"blog.rose-ash.com\"
  :replicas 2)

;; sx-swarm reads: image, port, replicas -> container orchestration
;; sx-proxy reads: port, domain -> route generation
;; sx-ci reads: image -> build pipeline
;; sx-forge reads: domain -> webhook URLs

;; Everything derived from the same definition.
;; Change it once, everything updates." "lisp")
      (p "This is the point of using one language for everything. "
         "The proxy, the orchestrator, the CI, and the forge all consume "
         "the same SX service definitions. No YAML-to-Caddyfile-to-Dockerfile translation."))

    (~docs/section :title "Implementation Path" :id "implementation"
      (ol (~tw :tokens "space-y-3 text-stone-600 list-decimal pl-5")
        (li (strong "Phase 1: Config compiler") " — SX route definitions compiled to Caddy JSON API. "
            "Use Caddy as the runtime, SX as the config language.")
        (li (strong "Phase 2: Middleware macros") " — defmacro for common middleware patterns. "
            "Chain composition. Apply to route groups.")
        (li (strong "Phase 3: Swarm integration") " — auto-generate routes from sx-swarm service defs. "
            "Hot reload on swarm state changes.")
        (li (strong "Phase 4: Dynamic routing") " — traffic splitting, feature flags, A/B testing. "
            "SX predicates evaluated per-request.")
        (li (strong "Phase 5: Native proxy") " — replace Caddy with SX-native HTTP proxy. "
            "Event loop in the host language, routing logic in SX. "
            "Like phase 5 of sx-swarm: the ambitious endgame."))
      (p "Phase 1-3 are practical today — Caddy's JSON API is a clean compilation target. "
         "The SX layer adds macros, composition, and integration that Caddyfile lacks."))))
