import Aoc
import Data.Char ( ord, chr )
import Data.List ( group, nub )
import Data.Bifunctor ( bimap )

toBase26' :: Char -> Int
toBase26' = (\n->n-97) . ord

fromBase26' :: Int -> Char
fromBase26' = chr . (97+)

toBase26 :: [Char] -> [Int]
toBase26 = reverse . map toBase26'

fromBase26 :: [Int] -> [Char]
fromBase26 = reverse . map fromBase26'

next :: [Int] -> [Int]
next (25:rest) = 0:next rest
next (x:rest)
    | x `elem` [7,10,13] = x+2:rest -- skip i,o,l
    | otherwise = x+1:rest

lengthy :: [a] -> Bool
lengthy = (>1) . length

getAnswer :: [[Int]] -> [Char]
getAnswer = fromBase26 . head

part1 :: String -> (String, [[Int]]) 
part1 = (\ps -> (getAnswer ps, tail ps)) . filter isSecure . iterate next . toBase26
    where
        hasStraight = any lengthy . filter ((==1) . head) . group . map (uncurry (-)) . adjacents
        hasNoIOL = not . any (`elem` [8,14,11])
        hasPair = lengthy . nub . map head . filter lengthy . group
        isSecure ns = hasStraight ns && hasNoIOL ns && hasPair ns

part2 :: p -> [[Int]] -> String
part2 _ = getAnswer

main :: IO ()
main = aocMain' single part1 part2