#!/usr/bin/env ruby
# frozen_string_literal: true

require "pathname"
require "set"

ValidationFailed = Class.new(StandardError)

class LengthInvalid < ValidationFailed
  def initialize(pwd) = super("#{pwd} length is not 8")
end

class NoStraightFound < ValidationFailed
  def initialize(pwd) = super("#{pwd} doesn't include an increasing straight")
end

class ForbiddenLetter < ValidationFailed
  def initialize(pwd) = super("#{pwd} has forbidden letters 'i', 'o', or 'l'")
end

class NotEnoughPairs < ValidationFailed
  def initialize(pwd) = super("#{pwd} doesn't include at least two different pairs")
end

def validate(input)
  raise LengthInvalid, input unless input.size == 8

  result = input
    .each_char
    .chain([nil])
    .each_cons(3)
    .each_with_object(straight: false, pairs: Set.new) do |(a, b, c), res|
      raise ForbiddenLetter, input unless ([a, b, c] & %w[i o l]).empty?
      res[:straight] ||= (b == a.succ && c == b.succ)
      res[:pairs].add(a) if (a == b && b != c)
    end

  raise NoStraightFound, input unless result[:straight]
  raise NotEnoughPairs, input unless result[:pairs].size >= 2

  input
end

def next_valid_password(input)
  validate(input)
rescue LengthInvalid
  nil
rescue ForbiddenLetter => e
  idx = input.index(/[iol]/)
  input = input[0..idx].succ + input[(idx+1)..]
  retry
rescue ValidationFailed
  input = input.succ
  retry
end

def main(file_paths)
  file_paths.each do |file_path|
    puts "File: #{file_path}"

    file_path.each_line do |line|
      line = line.chomp
      solution1 = next_valid_password(line)
      puts "solution 1: #{line} -> #{solution1}"
      solution2 = next_valid_password(solution1.succ)
      puts "solution 2: #{solution1} -> #{solution2}"
    end
  end
end

main(ARGV.map { |x| Pathname(x) })