(ns edition2022.day7
  (:require [clojure.string :as str]))

(def example "$ cd /\n$ ls\ndir a\n14848514 b.txt\n8504156 c.dat\ndir d\n$ cd a\n$ ls\ndir e\n29116 f\n2557 g\n62596 h.lst\n$ cd e\n$ ls\n584 i\n$ cd ..\n$ cd ..\n$ cd d\n$ ls\n4060174 j\n8033020 d.log\n5626152 d.ext\n7214296 k")
(def example-lines (str/split-lines example))

(defn add-file [tree command]
  ;(println command)
  (assoc tree (second command) (bigint (Integer/parseInt (first command)))))

(defn create-dir-tree [lines tree]
  (if (or (empty? lines) (= "$ cd .." (first lines)))
    [(rest lines) tree]
    (let [current-line (first lines)
          other-lines (rest lines)
          command (str/split current-line #" ")
          new-args (cond
                     (= "$ cd /" current-line) [other-lines tree]
                     (= "ls" (second command)) [other-lines tree]
                     (= "dir" (first command)) [other-lines tree]
                     (= "cd" (second command)) (let [other-exec (create-dir-tree other-lines {})]
                                                 [(first other-exec)
                                                  (assoc tree (nth command 2) (second other-exec))])
                     :else [other-lines (add-file tree command)])]
      (recur (first new-args) (second new-args)))))

(defn dir-structure [lines]
  (second (create-dir-tree lines {})))

(defn dir-name [parent child]
  (str parent \. child))

(defn compute-sizes [parent tree]
  (let [files-size (->> (vals tree)
                        (filter #(not (map? %)))
                        (reduce + 0N))
        dirs (->> (seq tree)
                  (filter #(map? (second %))))
        sizes (->> dirs
                   (map #(compute-sizes (dir-name parent (first %)) (second %)))
                   (reduce conj {})
                   )
        parent-size (->> (map first dirs)
                         (map #(dir-name parent %))
                         (select-keys sizes)
                         (vals)
                         (reduce + files-size))]
    (assoc sizes parent parent-size)))

(defn sum-with-cap [lines]
  (->> lines
       (dir-structure)
       (compute-sizes "/")
       (vals)
       (filter #(<= % 100000N))
       (reduce + 0N)
       ))

(defn input-lines []
  (->> (slurp "resources/day7.in")
       (str/split-lines)))

(defn solution-part1 []
  (->> (input-lines)
       (sum-with-cap)))

(defn solution-part2 []
  (let [sizes (->> (input-lines)
                   (dir-structure)
                   (compute-sizes "/"))
        used-space (sizes "/")
        free-space (- 70000000N used-space)]
    (->> sizes
         (vals)
         (sort)
         (drop-while #(< (+ % free-space) 30000000N))
         (first))))

(comment
  example-lines
  (create-dir-tree ["14848514 b.txt" "8504156 c.dat"] {})
  (create-dir-tree ["$ ls" "14848514 b.txt" "8504156 c.dat"] {})
  (create-dir-tree ["$ ls" "14848514 b.txt" "8504156 c.dat" "dir aaaa"] {})
  (create-dir-tree ["$ cd /" "$ ls" "14848514 b.txt" "8504156 c.dat" "dir aa"] {})
  (create-dir-tree ["$ cd /" "$ ls" "14848514 b.txt" "8504156 c.dat" "dir a" "$ cd a" "$ ls" "29116 f"] {})
  (dir-structure example-lines)

  (compute-sizes "/" {"a" 10, "b" 20, "c" {"d" 40}})
  (compute-sizes "/" (dir-structure example-lines))
  (sum-with-cap example-lines)
  (solution-part1)
  (dir-structure (input-lines))
  (compute-sizes "/" (dir-structure (input-lines)))
  all-dirs-from-file
  (count all-dirs-from-file)
  (count (set all-dirs-from-file))
  (count (filter #(str/includes? in %) all-dirs-from-file))
  (count (compute-sizes "/" (dir-structure (input-lines))))
  (solution-part2)
  )