require_relative "souls/version"
require "active_support/core_ext/string/inflections"
require_relative "souls/init"
require_relative "souls/api"
require_relative "souls/worker"
require_relative "souls/cli"
require "date"
require "json"
require "fileutils"
require "net/http"
require "paint"
require "whirly"
require "tty-prompt"

module Souls
  SOULS_METHODS = %w[
    model
    query
    mutation
    type
    resolver
    policy
    rspec_factory
    rspec_model
    rspec_query
    rspec_mutation
    rspec_resolver
    rspec_policy
  ].freeze
  public_constant :SOULS_METHODS
  class Error < StandardError; end
  class << self
    attr_accessor :configuration

    def update_repo(service_name: "api", update_kind: "patch")
      current_dir_name = FileUtils.pwd.to_s.match(%r{/([^/]+)/?$})[1]
      current_ver = get_latest_version_txt(service_name: service_name)
      new_ver = version_detector(current_ver: current_ver, update_kind: update_kind)
      bucket_url = "gs://souls-bucket/boilerplates"
      file_name = "#{service_name}-v#{new_ver}.tgz"
      release_name = "#{service_name}-latest.tgz"

      case current_dir_name
      when "souls"
        system("echo '#{new_ver}' > lib/souls/versions/.souls_#{service_name}_version")
        system("echo '#{new_ver}' > apps/#{service_name}/.souls_#{service_name}_version")
        system("cd apps/ && tar -czf ../#{service_name}.tgz #{service_name}/ && cd ..")
      when "api", "worker", "console", "admin", "media"
        system("echo '#{new_ver}' > lib/souls/versions/.souls_#{service_name}_version")
        system("echo '#{new_ver}' > .souls_#{service_name}_version")
        system("cd .. && tar -czf ../#{service_name}.tgz #{service_name}/ && cd #{service_name}")
      else
        raise(StandardError, "You are at wrong directory!")
      end

      system("gsutil cp #{service_name}.tgz #{bucket_url}/#{service_name.pluralize}/#{file_name}")
      system("gsutil cp #{service_name}.tgz #{bucket_url}/#{service_name.pluralize}/#{release_name}")
      system("gsutil cp .rubocop.yml #{bucket_url}/.rubocop.yml")
      FileUtils.rm("#{service_name}.tgz")
      "#{service_name}-v#{new_ver} Succefully Stored to GCS! "
    end

    def version_detector(current_ver: [0, 0, 1], update_kind: "patch")
      case update_kind
      when "patch"
        "#{current_ver[0]}.#{current_ver[1]}.#{current_ver[2] + 1}"
      when "minor"
        "#{current_ver[0]}.#{current_ver[1] + 1}.0"
      when "major"
        "#{current_ver[0] + 1}.0.0"
      else
        raise(StandardError, "Wrong version!")
      end
    end

    def overwrite_version(new_version: "0.1.1")
      FileUtils.rm("./lib/souls/version.rb")
      file_path = "./lib/souls/version.rb"
      File.open(file_path, "w") do |f|
        f.write(<<~TEXT)
          module Souls
            VERSION = "#{new_version}".freeze
            public_constant :VERSION
          end
        TEXT
      end
      overwrite_gemfile_lock(new_version: new_version)
      true
    rescue StandardError, e
      raise(StandardError, e)
    end

    def overwrite_gemfile_lock(new_version: "0.1.1")
      file_path = "Gemfile.lock"
      new_file_path = "Gemfile.lock.tmp"
      File.open(file_path, "r") do |f|
        File.open(new_file_path, "w") do |new_line|
          f.each_line.with_index do |line, i|
            if i == 3
              new_line.write("    souls (#{new_version})\n")
            else
              new_line.write(line)
            end
          end
        end
      end
      FileUtils.rm(file_path)
      FileUtils.mv(new_file_path, file_path)
    end

    def get_latest_version_txt(service_name: "api")
      case service_name
      when "gem"
        return Souls::VERSION.split(".").map(&:to_i)
      when "api", "worker", "console", "admin", "media"
        file_path = "#{Gem.dir}/gems/souls-#{Souls::VERSION}/lib/souls/versions/.souls_#{service_name}_version"
      else
        raise(StandardError, "You are at wrong directory!")
      end
      File.open(file_path, "r") do |f|
        f.readlines[0].strip.split(".").map(&:to_i)
      end
    end

    def update_service_gemfile(service_name: "api", version: "0.0.1")
      file_dir = "./apps/#{service_name}"
      file_path = "#{file_dir}/Gemfile"
      gemfile_lock = "#{file_dir}/Gemfile.lock"
      tmp_file = "#{file_dir}/tmp/Gemfile"
      File.open(file_path, "r") do |f|
        File.open(tmp_file, "w") do |new_line|
          f.each_line do |line|
            gem = line.gsub("gem ", "").gsub("\"", "").gsub("\n", "").gsub(" ", "").split(",")
            if gem[0] == "souls"
              old_ver = gem[1].split(".")
              old_ver[2] = (old_ver[2].to_i + 1).to_s
              new_line.write("  gem \"souls\", \"#{version}\"\n")
            else
              new_line.write(line)
            end
          end
        end
      end
      FileUtils.rm(file_path)
      FileUtils.rm(gemfile_lock) if File.exist?(gemfile_lock)
      FileUtils.mv(tmp_file, file_path)
      puts(Paint["\nSuccessfully Updated #{service_name} Gemfile!", :green])
    end

    def check_schema(class_name: "user")
      schema_data = get_columns_num(class_name: class_name)
      create_migration_data = get_create_migration_type(class_name: class_name)
      add_migration_data = get_migration_type(class_name: class_name, action: "add")
      remove_migration_data = get_migration_type(class_name: class_name, action: "remove")
      migration_data = create_migration_data + add_migration_data - remove_migration_data
      return "Already Up to date!" if schema_data.size == migration_data.size

      schema_data - migration_data
    end

    def get_columns_num(class_name: "user")
      file_path = "./db/schema.rb"
      class_check_flag = false
      cols = []
      File.open(file_path, "r") do |f|
        f.each_line.with_index do |line, _i|
          class_check_flag = true if line.include?("create_table") && line.include?(class_name)
          if class_check_flag == true && !line.include?("create_table")
            return cols if line.include?("t.index") || line.strip == "end"

            types = Souls::Api::Generate.get_type_and_name(line)
            array = line.include?("array: true")
            cols << { column_name: types[1], type: types[0], array: array }
          end
        end
      end
      cols
    end

    def get_create_migration_type(class_name: "user")
      pluralized_class_name = class_name.pluralize
      file_path = Dir["db/migrate/*_create_#{pluralized_class_name}.rb"][0]

      class_check_flag = false
      response = [
        { column_name: "created_at", type: "datetime", array: false },
        { column_name: "updated_at", type: "datetime", array: false }
      ]
      File.open(file_path) do |f|
        f.each_line do |line|
          class_check_flag = true if line.include?("create_table")
          next unless class_check_flag == true && !line.include?("create_table")
          return response if line.include?("t.timestamps") || line.strip == "end"

          types = Souls::Api::Generate.get_type_and_name(line)
          types.map { |n| n.gsub!(":", "") }
          array = line.include?("array: true")
          response << { column_name: types[1], type: types[0], array: array }
        end
      end
    end

    def get_migration_type(class_name: "user", action: "add")
      pluralized_class_name = class_name.pluralize
      file_paths = Dir["db/migrate/*_#{action}_column_to_#{pluralized_class_name}.rb"]

      new_columns =
        file_paths.map do |file_path|
          get_col_name_and_type(class_name: class_name, file_path: file_path, action: action)
        end
      new_columns.flatten
    end

    def get_last_migration_type(class_name: "user", action: "add")
      pluralized_class_name = class_name.pluralize
      file_paths = Dir["db/migrate/*_#{action}_column_to_#{pluralized_class_name}.rb"]

      file_paths.max
      resoponse = get_col_name_and_type(class_name: class_name, file_path: file_paths.max, action: action)
      resoponse.flatten
    end

    def get_col_name_and_type(
      class_name: "user", file_path: "db/migrate/20210816094410_add_column_to_users.rb", action: "add"
    )
      pluralized_class_name = class_name.pluralize
      response = []
      File.open(file_path) do |line|
        line.each_line do |file_line|
          next unless file_line.include?("#{action}_column")

          array = file_line.include?("array: true")
          types = file_line.split(",").map(&:strip)
          types.map { |n| n.gsub!(":", "") }
          types[0].gsub!("#{action}_column ", "")
          unless types[0].to_s == pluralized_class_name
            raise(StandardError, "Wrong class_name!Please Check your migration file!")
          end

          response << { column_name: types[1], type: types[2], array: array }
        end
      end
      response
    end

    def configure
      self.configuration ||= Configuration.new
      yield(configuration)
    end
  end

  class Configuration
    attr_accessor :app, :strain, :project_id, :github_repo, :worker_endpoint, :fixed_gems

    def initialize
      @app = nil
      @project_id = nil
      @strain = nil
      @github_repo = nil
      @worker_endpoint = nil
      @fixed_gems = nil
    end
  end
end