class Rails::Generators::Db::System::ChangeGenerator

:nodoc:

def self.default_generator_root

def self.default_generator_root
  path = File.expand_path(File.join(base_name, "app"), base_root)
  path if File.exist?(path)
end

def all_database_gems

def all_database_gems
  Database.all.map { |database| database.gem }
end

def all_database_gems_regex

def all_database_gems_regex
  all_database_gem_names = all_database_gems.map(&:first)
  /(\b#{all_database_gem_names.join('\b|\b')}\b)/
end

def all_docker_bases

def all_docker_bases
  Database.all.map { |database| docker_base_packages(database.base_package) }.uniq
end

def all_docker_bases_regex

def all_docker_bases_regex
  /(\b#{all_docker_bases.join('\b|\b')}\b)/
end

def all_docker_builds

def all_docker_builds
  Database.all.map { |database| docker_build_packages(database.build_package) }.uniq
end

def all_docker_builds_regex

def all_docker_builds_regex
  /(\b#{all_docker_builds.join('\b|\b')}\b)/
end

def database

def database
  @database ||= Database.build(options[:database])
end

def devcontainer?

def devcontainer?
  return @devcontainer if defined?(@devcontainer)
  @devcontainer = File.exist?(File.expand_path(".devcontainer", destination_root))
end

def devcontainer_json

def devcontainer_json
  return unless File.exist?(devcontainer_json_path)
  @devcontainer_json ||= JSON.parse(File.read(devcontainer_json_path))
end

def devcontainer_json_path

def devcontainer_json_path
  File.expand_path(".devcontainer/devcontainer.json", destination_root)
end

def docker_base_packages(database_package)

def docker_base_packages(database_package)
  if database_package
    [database_package].concat(BASE_PACKAGES).sort
  else
    BASE_PACKAGES
  end.join("\s")
end

def docker_build_packages(database_package)

def docker_build_packages(database_package)
  if database_package
    [database_package].concat(BUILD_PACKAGES).sort
  else
    BUILD_PACKAGES
  end.join("\s")
end

def edit_compose_yaml

def edit_compose_yaml
  compose_yaml_path = File.expand_path(".devcontainer/compose.yaml", destination_root)
  return unless File.exist?(compose_yaml_path)
  compose_config = YAML.load_file(compose_yaml_path)
  Database.all.each do |database|
    compose_config["services"].delete(database.name)
    compose_config["volumes"]&.delete(database.volume)
    compose_config["services"]["rails-app"]["depends_on"]&.delete(database.name)
  end
  if database.service
    compose_config["services"][database.name] = database.service
    compose_config["volumes"] = { database.volume => nil }.merge(compose_config["volumes"] || {})
    compose_config["services"]["rails-app"]["depends_on"] = [
      database.name,
      compose_config["services"]["rails-app"]["depends_on"]
    ].flatten.compact
  end
  compose_config.delete("volumes") unless compose_config["volumes"]&.any?
  compose_config["services"]["rails-app"].delete("depends_on") unless compose_config["services"]["rails-app"]["depends_on"]&.any?
  File.write(compose_yaml_path, compose_config.to_yaml)
end

def edit_database_config

def edit_database_config
  template("config/databases/#{options[:database]}.yml", "config/database.yml")
end

def edit_devcontainer_files

def edit_devcontainer_files
  return unless devcontainer?
  edit_devcontainer_json
  edit_compose_yaml
end

def edit_devcontainer_json

def edit_devcontainer_json
  return unless devcontainer_json
  update_devcontainer_db_host
  update_devcontainer_db_feature
end

def edit_dockerfile

def edit_dockerfile
  dockerfile_path = File.expand_path("Dockerfile", destination_root)
  return unless File.exist?(dockerfile_path)
  gsub_file("Dockerfile", all_docker_bases_regex, docker_base_packages(database.base_package))
  gsub_file("Dockerfile", all_docker_builds_regex, docker_build_packages(database.build_package))
end

def edit_gemfile

def edit_gemfile
  name, version = database.gem
  gsub_file("Gemfile", all_database_gems_regex, name)
  gsub_file("Gemfile", gem_entry_regex_for(name), gem_entry_for(name, *version))
end

def gem_entry_for(*gem_name_and_version)

def gem_entry_for(*gem_name_and_version)
  gem_name_and_version.map! { |segment| "\"#{segment}\"" }
  "gem #{gem_name_and_version.join(", ")}"
end

def gem_entry_regex_for(gem_name)

def gem_entry_regex_for(gem_name)
  /^gem.*\b#{gem_name}\b.*/
end

def initialize(*)

def initialize(*)
  super
  unless Database::DATABASES.include?(options[:to])
    raise Error, "Invalid value for --to option. Supported preconfigurations are: #{Database::DATABASES.join(", ")}."
  end
  opt = options.dup
  opt[:database] ||= opt[:to]
  self.options = opt.freeze
end

def update_devcontainer_db_feature

def update_devcontainer_db_feature
  features = devcontainer_json["features"]
  db_feature = database.feature
  Database.all.each do |database|
    features.delete(database.feature_name)
  end
  features.merge!(db_feature) if db_feature
  new_json = JSON.pretty_generate(features, indent: "  ", object_nl: "\n  ")
  gsub_file(".devcontainer/devcontainer.json", /("features"\s*:\s*)(.|\n)*?(^\s{2}})/, "\\1#{new_json}")
end

def update_devcontainer_db_host

def update_devcontainer_db_host
  container_env = devcontainer_json["containerEnv"]
  db_name = database.name
  if container_env["DB_HOST"]
    if database.service
      container_env["DB_HOST"] = db_name
    else
      container_env.delete("DB_HOST")
    end
  else
    if database.service
      container_env["DB_HOST"] = db_name
    end
  end
  new_json = JSON.pretty_generate(container_env, indent: "  ", object_nl: "\n  ")
  gsub_file(".devcontainer/devcontainer.json", /("containerEnv"\s*:\s*)(.|\n)*?(^\s{2}})/, "\\1#{new_json}")
end