{ pkgs, lib }:

with lib;

let
  compartments = pipe (fileContents ./input.txt) [
    (splitString "\n")
  ];

  splitAtMiddle = s: {
    c1 = substring 0 ((stringLength s) / 2) s;
    c2 = substring ((stringLength s) / 2) (stringLength s) s;
  };
  charsToSet = s: foldl (set: c: set // { ${c} = true; }) { } (stringToCharacters s);
  getCommonChar = { c1, c2 }:
    findSingle
      (c: c2.${c} or false)
      "Error: no common chars"
      "Error: multiple chars"
      (attrNames c1);
  mapRange = min: max: f: i: if min <= i && i < max then f i else i;
  transformRange = min: max: offset: i: mapRange min max (x: x + offset) i;

  charValue = c: pipe c [
    lib.strings.charToInt
    (transformRange 65 91 (-(65 - 27)))
    (transformRange 97 123 (-(97 - 1)))
  ];

  answer1 = pipe compartments [
    (map splitAtMiddle)
    (map (mapAttrs (_: charsToSet)))
    (map getCommonChar)
    (map charValue)
    (foldr add 0)
    toString
  ];

  chunksOf = n: l: if length l <= n
                     then [l]
                     else [(take n l)] ++ (chunksOf n (drop n l));
  toA123 = l: {
    a1 = elemAt l 0;
    a2 = elemAt l 1;
    a3 = elemAt l 2;
  };
  getCommonChar2 = { a1, a2, a3 }:
    findSingle
      (c: a2.${c} or false && a3.${c} or false)
      "Error: no common chars"
      "Error: multiple chars"
      (attrNames a1);

  answer2 = pipe compartments [
    (chunksOf 3)
    (map toA123)
    (map (mapAttrs (_: charsToSet)))
    (map getCommonChar2)
    (map charValue)
    (foldr add 0)
    toString
  ];

in pkgs.writeText "answers" ''
  Task1:
    ${answer1}

  Task2:
    ${answer2}
''