module BulletTrain
  module SuperScaffolding
    module Scaffolders
      class OauthProviderScaffolder < Scaffolder
        def run
          unless argv.count >= 5
            puts ""
            puts "🚅  usage: bin/super-scaffold oauth-provider <omniauth_gem> <gems_provider_name> <our_provider_name> <PROVIDER_API_KEY_ENV_VAR_NAME> <PROVIDER_API_SECRET_ENV_VAR_NAME> [options]"
            puts ""
            puts "E.g. what we actually did to start Shopify off:"
            puts "  bin/super-scaffold oauth-provider omniauth-shopify-oauth2 shopify Oauth::ShopifyAccount SHOPIFY_API_KEY SHOPIFY_API_SECRET_KEY --icon=ti-shopping-cart"
            puts "  (Please note here that the SHOPIFY_API_KEY and SHOPIFY_API_SECRET_KEY strings are not the actual values, just the names we give to the environment variables.)"
            puts ""
            puts "Options:"
            puts ""
            puts "  --icon={ti-*}: Specify an icon."
            puts ""
            puts "For a list of readily available provider strategies, see https://github.com/omniauth/omniauth/wiki/List-of-Strategies ."
            puts ""
            exit
          end

          _, omniauth_gem, gems_provider_name, our_provider_name, api_key, api_secret = *ARGV

          if omniauth_gem == "omniauth-stripe-connect"
            puts "Stripe is already available for use and does not need any scaffolding to be done.".green
            puts "Just add your `STRIPE_CLIENT_ID` and `STRIPE_SECRET_KEY` values to application.yml"
            puts "and you should be able to use Stripe as an OAuth provider out of the box."
            exit
          end

          unless (match = our_provider_name.match(/Oauth::(.*)Account/))
            puts "\n🚨 Your provider name must match the pattern of `Oauth::{Name}Account`, e.g. `Oauth::StripeAccount`\n".red
            return
          end

          options = {
            omniauth_gem: omniauth_gem,
            gems_provider_name: gems_provider_name,
            our_provider_name: match[1],
            api_key: api_key,
            api_secret: api_secret
          }

          unless File.exist?(oauth_transform_string("./app/models/oauth/stripe_account.rb", options)) &&
              File.exist?(oauth_transform_string("./app/models/integrations/stripe_installation.rb", options)) &&
              File.exist?(oauth_transform_string("./app/models/webhooks/incoming/oauth/stripe_account_webhook.rb", options))

            oauth_model_data = {
              "Oauth::StripeAccount": "rails generate model Oauth::StripeAccount uid:string data:jsonb user:references",
              "Integrations::StripeInstallation": "rails generate model Integrations::StripeInstallation team:references oauth_stripe_account:references name:string",
              "Webhooks::Incoming::Oauth::StripeAccountWebhook": "rails generate model Webhooks::Incoming::Oauth::StripeAccountWebhook data:jsonb processed_at:datetime verified_at:datetime oauth_stripe_account:references"
            }

            # Transform class names and model migrations.
            transformed_data = {}
            oauth_model_data.each do |key, value|
              transformed_data[oauth_transform_string(key.to_s, options)] = oauth_transform_string(value, options)
            end

            if @options["skip-migration-generation"]
              undefined_models = transformed_data.keys.each { |key| key.safe_constantize.nil? }
              if undefined_models.any?
                puts ""
                puts "We could not find the following models:".red
                undefined_models.each { |model| puts "  #{model}".red }
                puts ""
                puts "🚨 Before doing the actual Super Scaffolding, you'll need to generate the models like so:".red
                puts ""
                transformed_data.each { |_, migration| puts "  #{migration}".red }
                puts ""
                puts "However, don't do the `rake db:migrate` until after you re-run Super Scaffolding, as it will need to update some settings in those migrations.".red
                puts ""
                return
              end
            else
              transformed_data.each do |klass, generation_command|
                puts "Generating #{klass} model with '#{generation_command}'".green
                generation_thread = Thread.new { `#{generation_command}` }
                generation_thread.join # Wait for the process to finish.
              end
            end
          end

          icon_name = nil
          if @options["icon"].present?
            icon_name = @options["icon"]
          else
            puts "OK, great! Let's do this! By default providers will appear with a dollar symbol,"
            puts "but after you hit enter I'll open a page where you can view other icon options."
            puts "When you find one you like, hover your mouse over it and then come back here and"
            puts "and enter the name of the icon you want to use."
            if TerminalCommands.can_open?
              TerminalCommands.open_file_or_link("http://light.pinsupreme.com/icon_fonts_themefy.html")
            else
              puts "Sorry! We can't open these URLs automatically on your platform, but you can visit them manually:"
              puts ""
              puts "  http://light.pinsupreme.com/icon_fonts_themefy.html"
            end
            puts ""
            puts "Did you find an icon you wanted to use? Enter the name here or hit enter to just"
            puts "use the dollar symbol:"
            icon_name = $stdin.gets.chomp
            puts ""
            unless icon_name.length > 0 || icon_name.downcase == "y"
              icon_name = "icon-puzzle"
            end
          end

          options[:icon] = icon_name

          empty_transformer = Scaffolding::Transformer.new("", "")

          [

            # User OAuth.
            "./app/models/oauth/stripe_account.rb",
            "./app/models/concerns/oauth/stripe_accounts/base.rb",
            "./app/models/webhooks/incoming/oauth/stripe_account_webhook.rb",
            "./app/models/concerns/webhooks/incoming/oauth/stripe_account_webhooks/base.rb",
            "./app/controllers/account/oauth/stripe_accounts_controller.rb",
            "./app/controllers/webhooks/incoming/oauth/stripe_account_webhooks_controller.rb",
            "./app/views/account/oauth/stripe_accounts",
            "./test/models/oauth/stripe_account_test.rb",
            "./test/factories/oauth/stripe_accounts.rb",
            "./config/locales/en/oauth/stripe_accounts.en.yml",
            "./app/views/devise/shared/oauth/_stripe.html.erb",

            # Team Integration.
            "./app/models/integrations/stripe_installation.rb",
            "./app/models/concerns/integrations/stripe_installations/base.rb",
            # './app/serializers/api/v1/integrations/stripe_installation_serializer.rb',
            "./app/controllers/account/integrations/stripe_installations_controller.rb",
            "./app/views/account/integrations/stripe_installations",
            "./test/models/integrations/stripe_installation_test.rb",
            "./test/factories/integrations/stripe_installations.rb",
            "./config/locales/en/integrations/stripe_installations.en.yml",

            # Webhook.
            "./app/models/webhooks/incoming/oauth/stripe_account_webhook.rb",
            "./app/controllers/webhooks/incoming/oauth/stripe_account_webhooks_controller.rb"

          ].each do |name|
            if File.directory?(empty_transformer.resolve_template_path(name))
              oauth_scaffold_directory(name, options)
            else
              oauth_scaffold_file(name, options)
            end
          end

          oauth_scaffold_add_line_to_file("./app/views/devise/shared/_oauth.html.erb", "<%= render 'devise/shared/oauth/stripe', verb: verb if stripe_enabled? %>", "<%# 🚅 super scaffolding will insert new oauth providers above this line. %>", options, prepend: true)
          oauth_scaffold_add_line_to_file("./app/views/account/users/_oauth.html.erb", "<%= render 'account/oauth/stripe_accounts/index', context: @user, stripe_accounts: @user.oauth_stripe_accounts if stripe_enabled? %>", "<% # 🚅 super scaffolding will insert new oauth providers above this line. %>", options, prepend: true)
          oauth_scaffold_add_line_to_file("./config/initializers/devise.rb", "config.omniauth :stripe_connect, ENV['STRIPE_CLIENT_ID'], ENV['STRIPE_SECRET_KEY'], {\n    ## specify options for your oauth provider here, e.g.:\n    # scope: 'read_products,read_orders,write_content',\n  }\n", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
          oauth_scaffold_add_line_to_file("./app/controllers/account/oauth/omniauth_callbacks_controller.rb", "def stripe_connect\n    callback(\"Stripe\", team_id_from_env)\n  end\n", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
          oauth_scaffold_add_line_to_file("./app/models/team.rb", "has_many :integrations_stripe_installations, class_name: 'Integrations::StripeInstallation', dependent: :destroy if stripe_enabled?", "# 🚅 add oauth providers above.", options, prepend: true)
          oauth_scaffold_add_line_to_file("./app/models/user.rb", "has_many :oauth_stripe_accounts, class_name: 'Oauth::StripeAccount' if stripe_enabled?", "# 🚅 add oauth providers above.", options, prepend: true)
          oauth_scaffold_add_line_to_file("./config/locales/en/oauth.en.yml", "stripe_connect: Stripe", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
          oauth_scaffold_add_line_to_file("./app/views/account/shared/_menu.html.erb", "<%= render 'account/integrations/stripe_installations/menu_item' if stripe_enabled? %>", "<%# 🚅 super scaffolding will insert new oauth providers above this line. %>", options, prepend: true)
          oauth_scaffold_add_line_to_file("./config/routes.rb", "resources :stripe_account_webhooks if stripe_enabled?", "# 🚅 super scaffolding will insert new oauth provider webhooks above this line.", options, prepend: true)
          oauth_scaffold_add_line_to_file("./config/routes.rb", "resources :stripe_accounts if stripe_enabled?", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
          oauth_scaffold_add_line_to_file("./config/routes.rb", "resources :stripe_installations if stripe_enabled?", "# 🚅 super scaffolding will insert new integration installations above this line.", options, prepend: true)
          oauth_scaffold_add_line_to_file("./Gemfile", "gem 'omniauth-stripe-connect'", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
          oauth_scaffold_add_line_to_file("./lib/bullet_train_oauth_scaffolder_support.rb", "def stripe_enabled?\n  ENV['STRIPE_CLIENT_ID'].present? && ENV['STRIPE_SECRET_KEY'].present?\nend\n", "# 🚅 super scaffolding will insert new oauth providers above this line.", options, prepend: true)
          oauth_scaffold_add_line_to_file("./lib/bullet_train_oauth_scaffolder_support.rb", "stripe_enabled?,", "# 🚅 super scaffolding will insert new oauth provider checks above this line.", options, prepend: true)
          oauth_scaffold_add_line_to_file("./app/models/ability.rb", "if stripe_enabled?\n        can [:read, :create, :destroy], Oauth::StripeAccount, user_id: user.id\n        can :manage, Integrations::StripeInstallation, team_id: user.team_ids\n        can :destroy, Integrations::StripeInstallation, oauth_stripe_account: {user_id: user.id}\n      end\n", "# 🚅 super scaffolding will insert any new oauth providers above.", options, prepend: true)

          # find the database migration that defines this relationship.
          migration_file_name = `grep "create_table #{oauth_transform_string(":oauth_stripe_accounts", options)}" db/migrate/*`.split(":").first
          empty_transformer.replace_in_file(migration_file_name, "null: false", "null: true")

          migration_file_name = `grep "create_table #{oauth_transform_string(":integrations_stripe_installations", options)}" db/migrate/*`.split(":").first
          empty_transformer.replace_in_file(migration_file_name,
            oauth_transform_string("t.references :oauth_stripe_account, null: false, foreign_key: true", options),
            oauth_transform_string('t.references :oauth_stripe_account, null: false, foreign_key: true, index: {name: "index_stripe_installations_on_oauth_stripe_account_id"}', options))

          migration_file_name = `grep "create_table #{oauth_transform_string(":webhooks_incoming_oauth_stripe_account_webhooks", options)}" db/migrate/*`.split(":").first
          empty_transformer.replace_in_file(migration_file_name, "null: false", "null: true")
          empty_transformer.replace_in_file(migration_file_name, "foreign_key: true", oauth_transform_string('foreign_key: true, index: {name: "index_stripe_webhooks_on_oauth_stripe_account_id"}', options))

          puts ""
          puts "🎉"
          puts ""
          puts "You'll probably need to `bundle install`.".green
          puts ""
          puts "You'll need to configure API keys for this provider in `config/application.yml`, like so:"
          puts ""
          puts oauth_transform_string("  STRIPE_CLIENT_ID: ...", options)
          puts oauth_transform_string("  STRIPE_SECRET_KEY: ...", options)
          puts ""
          puts "If the OAuth provider asks you for some whitelisted callback URLs, the URL structure for those is as so:"
          puts ""
          path = "users/auth/stripe_connect/callback"
          puts oauth_transform_string("  https://yourdomain.co/#{path}", options)
          puts oauth_transform_string("  https://yourtunnel.ngrok.io/#{path}", options)
          puts oauth_transform_string("  http://localhost:3000/#{path}", options)
          puts ""
          puts "If you're able to specify an endpoint to receive webhooks from this provider, use this URL:"
          puts ""
          path = "webhooks/incoming/oauth/stripe_account_webhooks"
          puts oauth_transform_string("  https://yourdomain.co/#{path}", options)
          puts oauth_transform_string("  https://yourtunnel.ngrok.io/#{path}", options)
          puts oauth_transform_string("  http://localhost:3000/#{path}", options)
          puts ""
          puts ""
          puts "If you'd like to edit how your Bullet Train application refers to this provider, just edit the locale file at `config/locales/en/oauth.en.yml`."
          puts ""
          puts "And finally, if you need to specify any custom authorizations or options for your OAuth integration with this provider, you can configure those in `config/initializers/devise.rb`."
          puts ""
        end
      end
    end
  end
end
