(define tw-spacing-props {:ml "margin-left:{v}" :mr "margin-right:{v}" :mt "margin-top:{v}" :mb "margin-bottom:{v}" :pl "padding-left:{v}" :gap-y "row-gap:{v}" :m "margin:{v}" :gap-x "column-gap:{v}" :my "margin-top:{v};margin-bottom:{v}" :px "padding-left:{v};padding-right:{v}" :pb "padding-bottom:{v}" :pr "padding-right:{v}" :p "padding:{v}" :gap "gap:{v}" :py "padding-top:{v};padding-bottom:{v}" :pt "padding-top:{v}" :mx "margin-left:{v};margin-right:{v}"})

(define tw-displays {:flex "flex" :table "table" :grid "grid" :inline-block "inline-block" :table-row "table-row" :inline "inline" :hidden "none" :block "block" :contents "contents" :inline-flex "inline-flex" :inline-grid "inline-grid" :table-cell "table-cell"})

(define tw-max-widths {:xs "20rem" :3xl "48rem" :7xl "80rem" :sm "24rem" :xl "36rem" :full "100%" :md "28rem" :6xl "72rem" :prose "65ch" :max "max-content" :5xl "64rem" :min "min-content" :lg "32rem" :2xl "42rem" :4xl "56rem" :none "none" :screen "100vw" :fit "fit-content"})

(define tw-min-widths {:full "100%" :0 "0px" :max "max-content" :min "min-content" :fit "fit-content"})

(define
  tw-resolve-layout
  (fn
    (token)
    (let
      ((parts (split token "-"))
        (head (first parts))
        (rest (slice parts 1)))
      (cond
        (and (= (len parts) 1) (not (nil? (get tw-displays head))))
        (str "display:" (get tw-displays head))
        (and (= (len parts) 2) (not (nil? (get tw-displays token))))
        (str "display:" (get tw-displays token))
        (and (get tw-spacing-props head) (= (len rest) 1))
        (let
          ((tmpl (get tw-spacing-props head))
            (v (tw-spacing-value (first rest))))
          (if (nil? v) nil (tw-template tmpl v)))
        (and
          (= (len rest) 2)
          (get tw-spacing-props (str head "-" (first rest))))
        (let
          ((tmpl (get tw-spacing-props (str head "-" (first rest))))
            (v (tw-spacing-value (last rest))))
          (if (nil? v) nil (tw-template tmpl v)))
        (and
          (= head "space")
          (= (len rest) 2)
          (or (= (first rest) "x") (= (first rest) "y")))
        (let
          ((v (tw-spacing-value (last rest))) (dir (first rest)))
          (if (nil? v) nil (if (= dir "x") {:suffix ">*+*" :css (str "margin-left:" v)} {:suffix ">*+*" :css (str "margin-top:" v)})))
        (and (= head "flex") (empty? rest))
        "display:flex"
        (and (= head "flex") (= (len rest) 1))
        (case
          (first rest)
          "row"
          "flex-direction:row"
          "col"
          "flex-direction:column"
          "wrap"
          "flex-wrap:wrap"
          "nowrap"
          "flex-wrap:nowrap"
          "1"
          "flex:1 1 0%"
          "auto"
          "flex:1 1 auto"
          "initial"
          "flex:0 1 auto"
          "none"
          "flex:none"
          :else nil)
        (and (= head "flex") (= (len rest) 2))
        (case
          (join "-" rest)
          "row-reverse"
          "flex-direction:row-reverse"
          "col-reverse"
          "flex-direction:column-reverse"
          "wrap-reverse"
          "flex-wrap:wrap-reverse"
          :else nil)
        (= head "grow")
        (if
          (empty? rest)
          "flex-grow:1"
          (if (= (first rest) "0") "flex-grow:0" nil))
        (= head "shrink")
        (if
          (empty? rest)
          "flex-shrink:1"
          (if (= (first rest) "0") "flex-shrink:0" nil))
        (and (= head "basis") (= (len rest) 1))
        (let
          ((val (first rest)))
          (cond
            (= val "auto")
            "flex-basis:auto"
            (= val "full")
            "flex-basis:100%"
            (= val "0")
            "flex-basis:0px"
            (contains? val "/")
            (let
              ((frac (split val "/")))
              (if
                (= (len frac) 2)
                (let
                  ((num (parse-int (first frac) nil))
                    (den (parse-int (nth frac 1) nil)))
                  (if
                    (or (nil? num) (nil? den))
                    nil
                    (str "flex-basis:" (* (/ num den) 100) "%")))
                nil))
            :else (let
              ((n (parse-int val nil)))
              (if (nil? n) nil (str "flex-basis:" (* n 0.25) "rem")))))
        (and (= head "justify") (= (len rest) 1))
        (case
          (first rest)
          "start"
          "justify-content:flex-start"
          "end"
          "justify-content:flex-end"
          "center"
          "justify-content:center"
          "between"
          "justify-content:space-between"
          "around"
          "justify-content:space-around"
          "evenly"
          "justify-content:space-evenly"
          "stretch"
          "justify-content:stretch"
          :else nil)
        (and (= head "items") (= (len rest) 1))
        (case
          (first rest)
          "start"
          "align-items:flex-start"
          "end"
          "align-items:flex-end"
          "center"
          "align-items:center"
          "baseline"
          "align-items:baseline"
          "stretch"
          "align-items:stretch"
          :else nil)
        (and (= head "self") (= (len rest) 1))
        (case
          (first rest)
          "auto"
          "align-self:auto"
          "start"
          "align-self:flex-start"
          "end"
          "align-self:flex-end"
          "center"
          "align-self:center"
          "stretch"
          "align-self:stretch"
          "baseline"
          "align-self:baseline"
          :else nil)
        (and (= head "content") (= (len rest) 1))
        (case
          (first rest)
          "start"
          "align-content:flex-start"
          "end"
          "align-content:flex-end"
          "center"
          "align-content:center"
          "between"
          "align-content:space-between"
          "around"
          "align-content:space-around"
          "evenly"
          "align-content:space-evenly"
          "stretch"
          "align-content:stretch"
          :else nil)
        (and (= head "order") (= (len rest) 1))
        (let
          ((val (first rest)))
          (cond
            (= val "first")
            "order:-9999"
            (= val "last")
            "order:9999"
            (= val "none")
            "order:0"
            :else (let
              ((n (parse-int val nil)))
              (if (nil? n) nil (str "order:" n)))))
        (and (= head "grid") (empty? rest))
        "display:grid"
        (and (= head "grid") (>= (len rest) 2) (= (first rest) "cols"))
        (let
          ((val (join "-" (slice rest 1))))
          (cond
            (= val "none")
            "grid-template-columns:none"
            (= val "subgrid")
            "grid-template-columns:subgrid"
            :else (let
              ((n (parse-int val nil)))
              (if
                (nil? n)
                nil
                (str "grid-template-columns:repeat(" n ",minmax(0,1fr))")))))
        (and (= head "grid") (>= (len rest) 2) (= (first rest) "rows"))
        (let
          ((val (join "-" (slice rest 1))))
          (cond
            (= val "none")
            "grid-template-rows:none"
            (= val "subgrid")
            "grid-template-rows:subgrid"
            :else (let
              ((n (parse-int val nil)))
              (if
                (nil? n)
                nil
                (str "grid-template-rows:repeat(" n ",minmax(0,1fr))")))))
        (and (= head "grid") (>= (len rest) 2) (= (first rest) "flow"))
        (case
          (nth rest 1)
          "row"
          "grid-auto-flow:row"
          "col"
          "grid-auto-flow:column"
          "dense"
          "grid-auto-flow:dense"
          :else nil)
        (and (= head "col") (>= (len rest) 2))
        (let
          ((sub (first rest)) (val (join "-" (slice rest 1))))
          (cond
            (and (= sub "span") (= val "full"))
            "grid-column:1 / -1"
            (= sub "span")
            (let
              ((n (parse-int val nil)))
              (if (nil? n) nil (str "grid-column:span " n " / span " n)))
            (= sub "start")
            (str "grid-column-start:" val)
            (= sub "end")
            (str "grid-column-end:" val)
            :else nil))
        (and (= head "row") (>= (len rest) 2))
        (let
          ((sub (first rest)) (val (join "-" (slice rest 1))))
          (cond
            (and (= sub "span") (= val "full"))
            "grid-row:1 / -1"
            (= sub "span")
            (let
              ((n (parse-int val nil)))
              (if (nil? n) nil (str "grid-row:span " n " / span " n)))
            (= sub "start")
            (str "grid-row-start:" val)
            (= sub "end")
            (str "grid-row-end:" val)
            :else nil))
        (and (= head "auto") (>= (len rest) 2))
        (let
          ((sub (first rest)) (val (join "-" (slice rest 1))))
          (cond
            (and (= sub "cols") (= val "auto"))
            "grid-auto-columns:auto"
            (and (= sub "cols") (= val "min"))
            "grid-auto-columns:min-content"
            (and (= sub "cols") (= val "max"))
            "grid-auto-columns:max-content"
            (and (= sub "cols") (= val "fr"))
            "grid-auto-columns:minmax(0,1fr)"
            (and (= sub "rows") (= val "auto"))
            "grid-auto-rows:auto"
            (and (= sub "rows") (= val "min"))
            "grid-auto-rows:min-content"
            (and (= sub "rows") (= val "max"))
            "grid-auto-rows:max-content"
            (and (= sub "rows") (= val "fr"))
            "grid-auto-rows:minmax(0,1fr)"
            :else nil))
        (and
          (= (len parts) 1)
          (or
            (= head "relative")
            (= head "absolute")
            (= head "fixed")
            (= head "sticky")
            (= head "static")))
        (str "position:" head)
        (and
          (or
            (= head "top")
            (= head "right")
            (= head "bottom")
            (= head "left"))
          (= (len rest) 1))
        (let
          ((v (tw-spacing-value (first rest))))
          (if (nil? v) nil (str head ":" v)))
        (and (= head "inset") (= (len rest) 1))
        (let
          ((v (tw-spacing-value (first rest))))
          (if (nil? v) nil (str "inset:" v)))
        (and (= head "inset") (= (len rest) 2))
        (let
          ((dir (first rest)) (v (tw-spacing-value (nth rest 1))))
          (if
            (nil? v)
            nil
            (case
              dir
              "x"
              (str "left:" v ";right:" v)
              "y"
              (str "top:" v ";bottom:" v)
              :else nil)))
        (and (= head "z") (= (len rest) 1))
        (if
          (= (first rest) "auto")
          "z-index:auto"
          (let
            ((n (parse-int (first rest) nil)))
            (if (nil? n) nil (str "z-index:" n))))
        (and (or (= head "w") (= head "h")) (= (len rest) 1))
        (let
          ((prop (if (= head "w") "width" "height")) (val (first rest)))
          (cond
            (= val "full")
            (str prop ":100%")
            (= val "screen")
            (str prop (if (= head "w") ":100vw" ":100vh"))
            (= val "auto")
            (str prop ":auto")
            (= val "min")
            (str prop ":min-content")
            (= val "max")
            (str prop ":max-content")
            (= val "fit")
            (str prop ":fit-content")
            (contains? val "/")
            (let
              ((frac (split val "/")))
              (if
                (= (len frac) 2)
                (let
                  ((num (parse-int (first frac) nil))
                    (den (parse-int (nth frac 1) nil)))
                  (if
                    (or (nil? num) (nil? den))
                    nil
                    (str prop ":" (* (/ num den) 100) "%")))
                nil))
            :else (let
              ((n (parse-int val nil)))
              (if (nil? n) nil (str prop ":" (* n 0.25) "rem")))))
        (and (= head "max") (>= (len rest) 2) (= (first rest) "w"))
        (let
          ((val-name (join "-" (slice rest 1)))
            (val (get tw-max-widths val-name)))
          (if (nil? val) nil (str "max-width:" val)))
        (and (= head "max") (>= (len rest) 2) (= (first rest) "h"))
        (let
          ((val (first (slice rest 1))))
          (cond
            (= val "full")
            "max-height:100%"
            (= val "screen")
            "max-height:100vh"
            (= val "none")
            "max-height:none"
            :else (let
              ((n (parse-int val nil)))
              (if (nil? n) nil (str "max-height:" (* n 0.25) "rem")))))
        (and (= head "min") (>= (len rest) 2) (= (first rest) "w"))
        (let
          ((val-name (join "-" (slice rest 1)))
            (val (get tw-min-widths val-name)))
          (if (nil? val) nil (str "min-width:" val)))
        (and (= head "min") (>= (len rest) 2) (= (first rest) "h"))
        (let
          ((val (first (slice rest 1))))
          (cond
            (= val "0")
            "min-height:0px"
            (= val "full")
            "min-height:100%"
            (= val "screen")
            "min-height:100vh"
            :else nil))
        (and (= head "overflow") (= (len rest) 1))
        (str "overflow:" (first rest))
        (and (= head "overflow") (= (len rest) 2))
        (str "overflow-" (first rest) ":" (nth rest 1))
        (and (= head "aspect") (= (len rest) 1))
        (case
          (first rest)
          "auto"
          "aspect-ratio:auto"
          "square"
          "aspect-ratio:1 / 1"
          "video"
          "aspect-ratio:16 / 9"
          :else nil)
        (and (= head "object") (= (len rest) 1))
        (str "object-fit:" (first rest))
        (and (= (len parts) 1) (= head "visible"))
        "visibility:visible"
        (and (= (len parts) 1) (= head "invisible"))
        "visibility:hidden"
        (and (= (len parts) 1) (= head "collapse"))
        "visibility:collapse"
        (and (= (len parts) 1) (= head "container"))
        "width:100%;max-width:100%"
        (and (= (len parts) 1) (= head "isolate"))
        "isolation:isolate"
        :else nil))))
