# frozen_string_literal: true

require 'optparse'
require 'etc'

COLUMNS = 3
MARGIN = 3

TYPES = {
  'fifo' => 'p',
  'characterSpecial' => 'c',
  'directory' => 'd',
  'blockSpecial' => 'b',
  'file' => '-',
  'link' => 'l',
  'socket' => 's'
}.freeze

PERMISSIONS = {
  '0' => '---',
  '1' => '--x',
  '2' => '-w-',
  '3' => '-wx',
  '4' => 'r--',
  '5' => 'r-x',
  '6' => 'rw-',
  '7' => 'rwx'
}.freeze

def run
  params = ARGV.getopts('a', 'r', 'l')
  filenames = params['a'] ? Dir.glob('*', File::FNM_DOTMATCH) : Dir.glob('*')
  filenames = filenames.reverse if params['r']
  params['l'] ? output_file_datails(filenames) : output(filenames)
end

def output(filenames)
  rows = filenames.size.ceildiv(COLUMNS)
  pads = pad_created(filenames, rows)
  (0...rows).each do |row|
    COLUMNS.times do |column|
      index = row + column * rows
      print filenames[index].ljust(pads[column] + COLUMNS) if filenames[index]
    end
    puts
  end
end

def pad_created(filenames, rows)
  filenames.each_slice(rows).map { _1.map(&:length).max }
end

def output_file_datails(filenames)
  puts total_file_blocks(filenames)
  filenames.each do |file|
    file_stat = File::Stat.new(file)
    print TYPES[file_stat.ftype]
    print file_mode(file_stat)
    print owner_info(file_stat, filenames)
    print time_stamp(file_stat)
    print symbolic(file)
    puts
  end
end

def owner_info(file_stat, filenames)
  [
    file_stat.nlink.to_s.prepend(' '),
    Etc.getpwuid(file_stat.uid).name,
    Etc.getgrgid(file_stat.gid).name,
    file_stat.size.to_s.rjust(max_filename_length(filenames))
  ].join(' ')
end

def symbolic(filenames)
  if File.lstat(filenames).symlink?
    " #{filenames} -> #{File.readlink(filenames)}"
  else
    " #{filenames}"
  end
end

def file_mode(file_stat)
  file_count = file_stat.mode.to_s(8).slice(-3, 3)
  file_permission = file_count.split('').map do |file|
    PERMISSIONS[file]
  end
  file_permission.join('')
end

def max_filename_length(filenames)
  filenames.map { |file| File.size(file) }.max.to_s.length + MARGIN
end

def time_stamp(file_stat)
  file_stat.mtime.strftime('%_m月 %_d %H:%M')
end

def total_file_blocks(filenames)
  blocks = filenames.map do |file|
    File::Stat.new(file).blocks
  end
  "total #{blocks.sum}"
end

run