require "option_parser"
require "colorize"
require "path"
require "file_utils"

mode = :month
operation = :copy
verbose = false
dry_run = true

src_dir : Path = Path[Dir.current].expand
dst_dir : Path = Path[Dir.current].expand

puts "Move files to directories based on their names leading timestamp, e.g. <1234567890_*>."

parser = OptionParser.new do |p|
  p.banner = "Usage: mvtotsdir [options] <source directory> <target directory>"

  p.on("-h", "--help", "Show this help") { puts p; exit }
  p.on("-y", "--year", "Group by year") { mode = :year }
  p.on("-m", "--month", "Group by month (default)") { mode = :month }
  p.on("-d", "--day", "Group by day") { mode = :day }
  p.on("-y", "--yes", "Write to disk") { dry_run = false }
  p.on("-c", "--copy", "Copy files (default)") { operation = :copy }
  p.on("-r", "--rename", "Move files") { operation = :rename }
  p.on("-v", "--verbose", "Verbose") { verbose = true }

  p.unknown_args do |args|
    case args.size
    when 0
      # defaults already set
    when 1
      src_dir = Path[args[0]].expand
      dst_dir = src_dir
    when 2
      src_dir = Path[args[0]].expand
      dst_dir = Path[args[1]].expand
    else
      puts p
      exit
    end
  end
end

parser.parse

puts "-- #{Time.local.colorize.bold}
-- source: #{src_dir.colorize.bold}
-- destination: #{dst_dir.colorize.bold}
-- mode: #{mode.colorize.bold}
-- operation: #{operation.colorize.bold}
-- dry run: #{dry_run.to_s.colorize.bold}
-- verbose: #{verbose.to_s.colorize.bold}"

unless File::Info.readable?(src_dir)
  puts "#{src_dir.colorize.bold} is not readable."
  exit 1
end

unless File::Info.writable?(dst_dir)
  puts "#{dst_dir.colorize.bold} is not writable."
  exit 1
end

# helper to parse timestamp
def parse_timestamp(filename : String) : Int64?
  match = filename.match(/^\d{10,13}_/)
  if !match
    return nil
  end

  ts = match[0].chomp("_").to_i64
  ts >= 1_000_000_000_000 ? ts // 1_000 : ts
end

# counters
processed = 0
skipped = 0

Dir.each_child(src_dir) do |f|
  path = src_dir / f

  secs = parse_timestamp(f)
  unless secs
    puts "#{path} #{"skipped".colorize.yellow} (no timestamp found)" if verbose
    skipped += 1
    next
  end

  t = Time.unix(secs)
  year, month, day = t.year, t.month, t.day

  new_dir = case mode
            when :year  then dst_dir / year.to_s
            when :month then dst_dir / year.to_s / month.to_s
            when :day   then dst_dir / year.to_s / month.to_s / day.to_s
            else
              raise "Unknown mode: #{mode}"
            end

  new_path = new_dir / f

  if verbose
    puts "#{path} -> #{new_path}"
  else
    print "."
  end

  unless dry_run
    begin
      FileUtils.mkdir_p(new_dir)
      case operation
      when :copy   then FileUtils.cp(path, new_path)
      when :rename then FileUtils.mv(path, new_path)
      else
        raise "Unknown operation: #{operation}"
      end
    rescue ex : IO::Error
      puts "\nFailed to process #{path}: #{ex.message}".colorize(:red)
    end
  end

  processed += 1
end

puts "\nDone. #{processed} processed, #{skipped} skipped."
