#!/usr/bin/env ruby

require 'rubygems'
require 'optparse'
require 'io/console'
require 'csv'
require 'open3'
require_relative '../lib/muzzy'

Version = Muzzy::VERSION

## check environments
kakasi_path = File.absolute_path(File.expand_path('..', __FILE__) + "/../vendor/bin/kakasi")
unless File.exists?(kakasi_path)
  $stderr.puts "cannot find kakasi. please check kakasi installation is successed."
  $stderr.puts "expect path: #{kakasi_path}"
  $stderr.puts "If you had not executed `muzzy_setup` yet, please run `muzzy_setup` first."
  exit 1
end
mysql_cmd = `which mysql`.to_s.chomp
if mysql_cmd == ''
  $stderr.puts "cannot find mysql client"
  exit 1
end
mysqlimport_cmd = `which mysqlimport`.to_s.chomp
if mysqlimport_cmd == ''
  $stderr.puts "cannot find mysqlimport command"
  exit 1
end


options = {
  file: '',
  verbose: false,
  mysql_config: {
    user: 'root',
    host: 'localhost',
    use_password: false,
    database: 'muzzy',
  },
  mysqlimport_config: {
    delete: false
  },
}

def check_file(file)
  if File.directory?(file)
    $stderr.puts "muzzy: #{file}: is directory"
    exit Errno::ENOENT::Errno
  end
  unless File.exists?(file)
    $stderr.puts "muzzy: #{file}: No such file or directory"
    exit Errno::ENOENT::Errno
  end
end

## parse arguments
ARGV.options do |opt|
  opt.banner = "Usage: muzzy [filepath] [options]"
  begin
    opt.on('-u', "--user [USER]", String, 'mysql user') {|user|
      options[:mysql_config][:user] = user
    }
    opt.on('-h [HOST]', 'mysql host') {|v| options[:mysql_config][:host] = v }
    opt.on('-p', 'using mysql password') {|v|
      options[:mysql_config][:use_password] = v
    }
    opt.on('-V', '--verbose', 'verbose option') {
      options[:verbose] = true
    }
    opt.on('-r', 'remove data and insert it') {
      options[:mysqlimport_config][:delete] = true
    }

    if ARGV[0].to_s.length > 0 && ARGV[0].to_s[0] != '-'
      options[:file] = ARGV[0]
    elsif ARGV.last.to_s.length > 0 && ARGV.last.to_s[0] != '-'
      options[:file] = ARGV.last
    else
      opt.on('-f', '--file [FILEPATH]', 'path to target file') {|v| options[:file] = v }
    end
    if options[:file].to_s != ''
      check_file(options[:file])
    end

    opt.on('-v', '--version') {
      $stdout.puts opt.ver
      exit
    }

    opt.parse!

    if options[:file].nil? || options[:file] == ''
      $stdout.puts opt.help
      exit 1
    end
  rescue => e
    $stderr.puts("[ERROR] #{e.message}")
    exit 1
  end
end

if options[:mysql_config][:database] !~ /\A(\w)+\z/
  $stderr.puts "illegal database name"
  exit 1
end

filetype_detector = Muzzy::FiletypeDetector.new(options[:file])
if filetype_detector.unknown?
  $stderr.puts "illegal file"
  exit 1
end

first_row = filetype_detector.first_row
second_row = filetype_detector.second_row
fields_terminated_by = filetype_detector.tsv? ? "\t" : ','

# detect first_row is header or data row
first_row_is_header = Muzzy::HeaderDetector.detect([first_row, second_row])
if first_row_is_header == nil
  # cannot judge header is header or not
  puts "first row is"
  puts first_row.join(',')
  puts "first row is HEADER? [y/n]"
  loop do
    y_or_n = $stdin.gets.to_s.chomp
    if y_or_n.match(/\A[Yy]\z/)
      first_row_is_header = true
      break
    end
    if y_or_n.match(/\A[Nn]\z/)
      first_row_is_header = false
      break
    end
    puts "plase enter y or n"
  end
end

# convert header row to compatible with database table columns
if first_row_is_header
  first_row = first_row.map do |str|
    std_out = Open3.capture2('echo', str)[0]
    Open3.capture2(kakasi_path, '-Ja', '-Ha', '-Ka', '-Ea', '-i', 'utf8', '-o', 'utf8', stdin_data: std_out)[0]
  end.map do |x|
    # kakasi returns ko^do if 'コード' given so replace it to _
    # space changes to _
    x.chomp.strip.gsub(/[\^]/, '_').gsub(/\s/, '_')
  end
end

# TODO ヘッダが空白含んでたりするやつとかをなんとかする
col_data_types = []
Cell = Struct.new(:type, :name)
if first_row_is_header
  if first_row
    col_data_types = first_row.map.with_index do |str, i|
      if str.to_s.match(/\A[\d,]+\z/)
        # number
        Cell.new('integer', first_row[i])
      else
        Cell.new('text', first_row[i])
      end
    end
  else
    col_data_types = first_row.map.with_index do |str, i|
      colname = first_row[i].gsub(/[,-]/, '')
      if str.to_s.match(/_id/i) && str.to_s.match(/\A[\w]+\z/i)
        # number
        Cell.new('integer', colname)
      else
        Cell.new('text', colname)
      end
    end
  end
else
  # TODO not create table option

  # first row is data(not header)
  col_data_types = first_row.map.with_index do |str, i|
    if str.to_s.match(/\A[\d,]+\z/)
      # number
      Cell.new('integer', "col#{i}")
    else
      Cell.new('text', "col#{i}")
    end
  end
end

config = {
  filepath: options[:file],
  cmd_path: mysql_cmd,
  mysqlimport_path: mysqlimport_cmd,
  user: options[:mysql_config][:user],
  host: options[:mysql_config][:host],
  # password: options[:mysql_config][:password],
  use_password: options[:mysql_config][:use_password],
  database_name: options[:mysql_config][:database],
}

db_adapter = Muzzy::DatabaseAdapter::MysqlAdapter.new(
  config,
  verbose: options[:verbose]
)
unless db_adapter.confirm_database
  unless db_adapter.create_database
    # database create failed
    exit 1
  end
end

filename = File.basename(options[:file])
table_name = filename.match(/\A(\w+)(\.\w+)?\z/)[1]

# confirm table
unless db_adapter.confirm_table(table_name)
  # cannot confirm table so create table
  unless db_adapter.create_table(table_name, col_data_types)
    # error, cannot create table
    exit 1
  end
end

# import
db_adapter.import(table_name, {
  first_row_is_header: first_row_is_header,
  fields_terminated_by: fields_terminated_by,
  delete: options[:mysqlimport_config][:delete]
})

exit 0