require 'thor'
require 'fileutils'
require 'pg/url'
require 'string/builder'
require 'tty/prompt'
require 'limeta/connection'
require 'limeta/table'

module Limeta
  class CLI < Thor
    using String::Builder

    default_task :convert

    class_option :adapter, type: :string, aliases: '-a', desc: 'Database adapter', enum: %w[psql sqlite3], required: true
    class_option :conn, type: :string, aliases: '-c', desc: 'Connection string', required: false
    class_option :table, type: :string, aliases: '-t', desc: 'Export table name', required: true

    disable_required_check! :help, :version

    desc "[FILES]", "Export PGN metadata to a database"
    def convert(*files)
      files = expand files
      raise ArgumentError.new("No PGN files provided") if files.empty?

      prompt = TTY::Prompt.new

      conn = case options[:adapter]
      when 'psql'
        options[:conn] || PG::URL.prompt
      when 'sqlite3'
        options[:conn] || String.build do |s|
          path = File.expand_path prompt.ask("Path to database file:")
          extensions = %w[.db .sql .sqlite .sqlite3]
          if File.file? path
            if extensions.include? File.extname(path)
              s << path
            else
              raise "Expected #{File.basename path} extension to be one of #{extensions.inspect}"
            end
          elsif File.directory? path
            puts
            if prompt.yes? "Create a database in \e[1m#{path}\e[0m?"
              db = nil
              pass = false
              until pass
                name = prompt.ask "Database name:"
                db = File.join path, "#{name.sub(/\.(sqlite3|sqlite|sql|db|)/, '')}.sqlite3"
                puts
                pass = prompt.yes? "Is \e[1m#{db}\e[0m correct?"
              end
              puts

              if File.exist? db
                puts "\e[31mWARNING\e[0m: Database \e[1m#{db}\e[0m already exists."
                return unless prompt.yes? "Continue anyway?"
              else
                FileUtils.touch db
                puts "Database #{db} created"
              end

              s << db
            else
              return
            end
          else
            if File.directory? File.dirname(path)
              if extensions.include? File.extname(path)
                if prompt.yes? "Create database \e[1m#{path}\e[0m?"
                  FileUtils.touch path
                  puts "Database \e[1m#{path}\e[0m created"
                else
                  return
                end
              else
                raise "Expected #{File.basename path} extension to be one of #{extensions.inspect}"
              end
            else
              raise "Invalid path #{path}"
            end
          end
        end
      end

      conn = case options[:adapter]
      when 'psql' then conn.start_with?("postgres://") ? conn : "postgres://#{conn}"
      when 'sqlite3' then conn
      end

      db = Limeta::Connection.establish conn, adapter: options[:adapter]
      table = Limeta::Table.new options[:table], database: db
      table.populate! files
    end

    map %w[--version -v] => :version
    desc '--version, -v', 'Display installed Limeta version'
    def version() puts Limeta::VERSION end

    no_tasks do
      def expand(files)
        files.map{|f| File.file?(f) ? Dir[f] : Dir["#{f}/**/*.pgn"]}.flatten
      end
    end

    class << self
      def help(shell, subcommand = false)
        list = printable_commands(true, subcommand)
        Thor::Util.thor_classes_in(self).each do |klass|
          list += klass.printable_commands(false)
        end

        list.reject! {|l| l[0].split[1] == 'help'}

        if defined?(@package_name) && @package_name
          shell.say "#{@package_name} commands:"
        else
          shell.say "Commands:"
        end

        shell.print_table(list, indent: 2, truncate: true)
        shell.say
        class_options_help(shell)
      end
    end
  end
end