#!/usr/bin/env ruby
# frozen_string_literal: true

require "bundler"
require "thor"
require "json"
require "active_support/inflector"
require "decidim/core/version"
require "decidim/generators"

module Decidim
  module Generators
    class ComponentGenerator < Thor
      include Thor::Actions

      attr_reader :component_name,
                  :component_module_name,
                  :component_resource_name,
                  :component_folder,
                  :component_description,
                  :core_version,
                  :npm_package_version,
                  :required_ruby_version,
                  :security_email,
                  :edge_git_branch

      source_root File.expand_path("component_templates", __dir__)

      desc "component COMPONENT_NAME", "Generate a decidim component"

      method_options destination_folder: :string, external: :boolean

      def component(component_name)
        @component_name = component_name
        @component_module_name = component_name.camelize
        @component_folder = options[:destination_folder] || "decidim-module-#{component_name}"
        @core_version = Decidim::Core.version
        @npm_package_version = "^#{semver_friendly_version}"
        @edge_git_branch = Decidim::Generators.edge_git_branch
        @component_description = ask "Write a description for the new component:"
        @required_ruby_version = RUBY_VERSION.length == 5 ? RUBY_VERSION[0..2] : RUBY_VERSION
        @security_email = ask "Provide a public email in case of security concern:"
        format_email!

        template "decidim-component.gemspec.erb", "#{component_folder}/decidim-#{component_name}.gemspec"
        template "Gemfile.erb", "#{component_folder}/Gemfile" if options[:external]
        template "Rakefile", "#{component_folder}/Rakefile"
        template "LICENSE-AGPLv3.txt", "#{component_folder}/LICENSE-AGPLv3.txt"
        template "README.md.erb", "#{component_folder}/README.md"
        template "gitignore", "#{component_folder}/.gitignore"
        template "github/ci.yml.erb", "#{component_folder}/.github/workflows/ci_#{component_name}.yml"
        template "package.json.erb", "#{component_folder}/package.json"
        copy_file ".ruby-version", "#{component_folder}/.ruby-version"
        copy_file ".node-version", "#{component_folder}/.node-version"
        copy_file ".rubocop.yml", "#{component_folder}/.rubocop.yml"
        copy_file ".eslintrc.json", "#{component_folder}/.eslintrc.json"
        copy_file ".stylelintrc.json", "#{component_folder}/.stylelintrc.json"

        app_folder = "#{component_folder}/app"
        template "app/packs/js/entrypoint.js", "#{app_folder}/packs/entrypoints/decidim_#{component_name}.js"
        template "app/packs/images/decidim/component/icon.svg", "#{app_folder}/packs/images/decidim/#{component_name}/icon.svg"
        template "app/packs/stylesheets/decidim/default.scss", "#{app_folder}/packs/stylesheets/decidim/#{component_name}/#{component_name}.scss"
        template "app/controllers/decidim/component/application_controller.rb.erb", "#{app_folder}/controllers/decidim/#{component_name}/application_controller.rb"
        template "app/controllers/decidim/component/admin/application_controller.rb.erb", "#{app_folder}/controllers/decidim/#{component_name}/admin/application_controller.rb"
        template "app/helpers/decidim/component/application_helper.rb.erb", "#{app_folder}/helpers/decidim/#{component_name}/application_helper.rb"
        template "app/models/decidim/component/application_record.rb.erb", "#{app_folder}/models/decidim/#{component_name}/application_record.rb"
        template "app/permissions/decidim/component/admin/permissions.rb.erb", "#{app_folder}/permissions/decidim/#{component_name}/admin/permissions.rb"

        bin_folder = "#{component_folder}/bin"
        template "bin/rails.erb", "#{bin_folder}/rails"
        chmod "#{bin_folder}/rails", "+x"

        config_folder = "#{component_folder}/config"
        template "config/assets.rb.erb", "#{config_folder}/assets.rb"
        template "config/locales/en.yml.erb", "#{config_folder}/locales/en.yml"
        template "config/i18n-tasks.yml.erb", "#{config_folder}/i18n-tasks.yml"

        lib_folder = "#{component_folder}/lib"
        template "lib/decidim/component.rb.erb", "#{lib_folder}/decidim/#{component_name}.rb"
        template "lib/decidim/component/engine.rb.erb", "#{lib_folder}/decidim/#{component_name}/engine.rb"
        template "lib/decidim/component/admin.rb.erb", "#{lib_folder}/decidim/#{component_name}/admin.rb"
        template "lib/decidim/component/admin_engine.rb.erb", "#{lib_folder}/decidim/#{component_name}/admin_engine.rb"
        template "lib/decidim/component/component.rb.erb", "#{lib_folder}/decidim/#{component_name}/component.rb"
        template "lib/decidim/component/version.rb.erb", "#{lib_folder}/decidim/#{component_name}/version.rb"
        template "lib/decidim/component/test/factories.rb.erb", "#{lib_folder}/decidim/#{component_name}/test/factories.rb"

        spec_folder = "#{component_folder}/spec"
        template "spec/spec_helper.rb.erb", "#{spec_folder}/spec_helper.rb"
        template "spec/factories.rb.erb", "#{spec_folder}/factories.rb"
        template "spec/permissions/admin/permissions_spec.rb.erb", "#{spec_folder}/permissions/admin/permissions_spec.rb"
        template "spec/lib/version_spec.rb.erb", "#{spec_folder}/lib/decidim/#{component_name}/version_spec.rb"

        if options[:external]
          inside(component_folder) do
            Bundler.with_original_env { run "bundle install" }
            Bundler.with_original_env { run "bundle lock --add-platform x86_64-linux" }
            Bundler.with_original_env { run "bundle lock --add-platform ruby" }
          end
        end
      end

      private

      def format_email!
        return unless @security_email&.include?("@")

        split = @security_email.split("@")
        email = split.first
        domain = split.last.gsub(".", " [dot] ")
        @security_email = "#{email} [at] #{domain}"
      end

      def semver_friendly_version
        Decidim::Generators.version.gsub(/\.pre/, "-pre").gsub(/\.dev/, "-dev").gsub(/.rc(\d*)/, "-rc\\1")
      end
    end
  end
end
