# DOORKEEPING

# UPDATE USER.RB
inject_into_file 'app/models/user.rb', before: 'end' do <<~EOF
	  enum role: %i[:new_user, :admin, :manager]
	  enum status: %i[:awaiting_approval, :active, :disabled, :blocked]

	  validates :email, format: URI::MailTo::EMAIL_REGEXP
	  
	  def self.authenticate(email, password)
	    user = User.find_for_authentication(email: email)
	    user&.valid_password?(password) ? user : nil
	  end
	EOF
end


# CREATE DOORKEEPER MIGRATION FILE
run "touch db/migrate/20240722051359_create_doorkeeper_tables.rb"

# REPLACE CONTENT OF MIGRATION FILE
inject_into_file 'db/migrate/20240722051359_create_doorkeeper_tables.rb' do <<~EOF
	class CreateDoorkeeperTables < ActiveRecord::Migration[6.1]
	  def change
	    create_table :oauth_applications do |t|
	      t.string  :name,    null: false
	      t.string  :uid,     null: false
	      t.string  :secret,  null: false

	      t.text    :redirect_uri
	      t.string  :scopes,       null: false, default: ''
	      t.boolean :confidential, null: false, default: true
	      t.timestamps             null: false
	    end

	    add_index :oauth_applications, :uid, unique: true

	    create_table :oauth_access_tokens do |t|
	      t.references :resource_owner, index: true

	      t.references :application,    null: false

	      t.string :token, null: false

	      t.string   :refresh_token
	      t.integer  :expires_in
	      t.datetime :revoked_at
	      t.datetime :created_at, null: false
	      t.string   :scopes

	     
	      t.string   :previous_refresh_token, null: false, default: ""
	    end

	    add_index :oauth_access_tokens, :token, unique: true
	    add_index :oauth_access_tokens, :refresh_token, unique: true
	    add_foreign_key(
	      :oauth_access_tokens,
	      :oauth_applications,
	      column: :application_id
	    )
	  end
	end
	EOF
end

# MIGRATE 
run "rails db:migrate"

# DELETE INITIALIZER/DOORKEEPER.RB
run "rm config/initializers/doorkeeper.rb"

# RECREATE INITIALIZER/DOORKEEPER.RB
run "touch config/initializers/doorkeeper.rb"

# UPDATE CONTENT OF INITIALIZER/DOORKEEPER.RB
inject_into_file 'config/initializers/doorkeeper.rb' do <<~EOF
	Doorkeeper.configure do
	  orm :active_record

	  resource_owner_from_credentials do
	    User.authenticate(params[:email], params[:password])
	  end

	  grant_flows %w[password]

	  use_refresh_token

	  allow_blank_redirect_uri true

	  skip_authorization do 
	    true
	  end
	end
	EOF
end

# UPDATE CONFIG/ROUTES.RB
inject_into_file 'config/routes.rb', before: 'end' do <<~EOF
	draw :api
	EOF
end

# CREATE ROUTES FOLDER CONFIG/ROUTES/
run "mkdir config/routes"

# CREATE FILE IN CONFIG/ROUTES/API.RB
run "touch config/routes/api.rb"

# UPDATE CONTENT
inject_into_file 'config/routes/api.rb' do <<~EOF
	namespace :api do 
	  namespace :v1 do 
	    scope :users, module: :users do
	      post '/', to: 'registrations#create', as: :user_registration
	    end

	    #resources :locations
	    #resources :productions
	  end
	end

	scope :api do 
	  scope :v1 do 
	    use_doorkeeper do 
	      skip_controllers :authorizations, :applications, :authorized_applications
	    end
	  end
	end
	EOF
end

# CREATE DOOKEEPER APPLICATIONS VIA SEED.RB
inject_into_file 'db/seeds.rb' do <<~EOF
	if Doorkeeper::Application.count.zero?
		Doorkeeper::Application.create(name: "Microservice", redirect_uri: "", scopes: "")
		Doorkeeper::Application.create(name: "Web client", redirect_uri: "", scopes: "")
		Doorkeeper::Application.create(name: "IOS client", redirect_uri: "", scopes: "")
		Doorkeeper::Application.create(name: "Android client", redirect_uri: "", scopes: "")
		Doorkeeper::Application.create(name: "JS client", redirect_uri: "", scopes: "")
	end
	EOF
end


# SEED
run "rails db:seed"

# CREATE USERS FOLDER CONTROLLERS/API/V1/USERS
run "mkdir app/controllers/api"
run "mkdir app/controllers/api/v1"
run "mkdir app/controllers/api/v1/users"

# CREATE REGISTRATIONS_CONTROLLER.RB
run "touch app/controllers/api/v1/users/registrations_controller.rb"

# UPDATE CONTENT
inject_into_file 'app/controllers/api/v1/users/registrations_controller.rb' do <<~EOF
	module Api
	  module V1
	    module Users
	      class RegistrationsController < ApiController
	        skip_before_action :doorkeeper_authorize!, only: %i[create]

	        include DoorkeeperRegisterable

	        def create
	          client_app = Doorkeeper::Application.find_by(uid: params[:client_id])
	          unless client_app
	            return render json: { error: I18n.t('doorkeeper.errors.messages.invalid_client') },
	                          status: :unauthorized
	          end

	          allowed_params = user_params.except(:client_id)
	          user = User.new(allowed_params)

	          if user.save
	            render json: render_user(user, client_app), status: :ok
	          else
	            render json: { errors: user.errors }, status: :unprocessable_entity
	          end
	        end

	        private

	        def user_params
	          params.permit(:email, :password, :client_id)
	        end
	      end
	    end
	  end
	end
	EOF
end

# CREATE DOORKEEPER_REGISTERABLE.RB UNDER CONCERNS FOLDER
run "touch app/controllers/concerns/doorkeeper_registerable.rb"

# UPDATE CONTENT
inject_into_file 'app/controllers/concerns/doorkeeper_registerable.rb' do <<~EOF
	module DoorkeeperRegisterable
	  extend ActiveSupport::Concern

	  def generate_refresh_token
	    loop do
	      # generate a random token string and return it
	      # unless there is already another token with the same string
	      token = SecureRandom.hex(32)
	      break token unless Doorkeeper::AccessToken.exists?(refresh_token: token)
	    end
	  end

	  def render_user(user, client_app, token_type = 'Bearer')
	    access_token = Doorkeeper::AccessToken.create(resource_owner_id: user.id,
	                                                  application_id: client_app.id,
	                                                  refresh_token: generate_refresh_token,
	                                                  expires_in: Doorkeeper.configuration.access_token_expires_in.to_i,
	                                                  scopes: '')

	    {
	      id: user.id,
	      email: user.email,
	      role: user.role,
	      access_token: access_token.token,
	      token_type: token_type,
	      expires_in: access_token.expires_in,
	      refresh_token: access_token.refresh_token,
	      created_at: access_token.created_at.to_time.to_i
	    }
	  end
	end
	EOF
end


# CREATE CONFIG/INITIALIZER/CORS.RB
run "touch config/initializers/cors.rb"

# UPDATE CONTENT
inject_into_file 'config/initializers/cors.rb' do <<~EOF
	Rails.application.config.middleware.insert_before 0, Rack::Cors do
	  allow do
	    origins '*'
	    resource '/api/v1/*',
	             headers: :any, methods: %i[get post patch put delete]
	  end
	end
	EOF
end


# CREATE ADMIN SECTION CONTROLLER
run "rails g controller AdminSection"


# UPDATE CONTENT OF CONTROLLERS/ADMIN_SECTION _CONTROLLER.RB
inject_into_file "app/controllers/admin_section_controller.rb", before: 'end' do <<~EOF
	def index  
	end

	def list_users
	  @users = User.all
	end 

	def apps_info
	  @applications = Doorkeeper::Application.all
	end
	EOF
end


#CREATE VIEWS
run "touch app/views/admin_section/index.html.erb"
run "touch app/views/admin_section/list_users.html.erb"
run "touch app/views/admin_section/apps_info.html.erb"

# UPDATE CONFIG/ROUTES.RB
inject_into_file 'config/routes.rb', before: 'end' do <<~EOF
	get 'admin_section/index'
	get 'admin_section/list_users'
	get 'admin_section/apps_info'
	EOF
end

# UPDATE CONTENT OF VIEWS/ADMIN_SECTION/APPS_INFO.HTML.ERB
inject_into_file "app/views/admin_section/apps_info.html.erb" do <<~EOF
	<h3>Applications</h3>

	<table>
	  <thead>
	    <tr>
	      <th>Name</th>
	      <th>Client ID</th>
	      <th>Client Secret</th>
	      <th></th>
	      <th></th>
	 
	      <th colspan="3"></th>
	    </tr>
	  </thead>

	  <tbody>
	    <% @applications.each do |application| %>
	      <tr>
	        <td><%= application.name %></td>
	        <td><%= application.uid %></td>
	        <td><%= application.secret %></td>
	        <td><%#= application. %></td>
	        <td><%#= application. %></td>
	  
	      </tr>
	    <% end %>
	  </tbody>
	</table>
	EOF
end

# UPDATE CONTENT OF VIEWS/ADMIN_SECTION/LIST_USERS.HTML.ERB
inject_into_file "app/views/admin_section/list_users.html.erb" do <<~EOF
	
<table>
  <thead>
        <tr>
          <th>#</th>
          <th>Name</th>
          <th>Email</th>
          <th>Confirmed</th>
          <th>User <br> Role</th>
          <th>User <br> Status</th>
          <th>Sign In <br> Count</th>

          <th>Current <br> Sign In</th>

          <th>Last <br> Sign In</th>

          <th>Current <br> IP</th>
          <th>Last IP</th>
          <th>Created <br> On</th>

          <th>Options</th>
        </tr>
      </thead>

      <tbody id ="all">
        <% @users.each.with_index(1) do |user, index| %>


          <tr>

            <td><%= index%></td>
            <td><%= user.name %></td>
            <td><%= user.email %></td> 
            <td><% if user.confirmed_at? %>Confirmed<%end%></td>    
            <td><%= user.role %></td> 
            <td><%= user.status %></td>       
            <td><%= user.sign_in_count %></td>

            <td><%if !user.current_sign_in_at.nil?%>
              <%= user.current_sign_in_at.strftime('%m-%d-%Y')%><br> <%= user.current_sign_in_at.strftime("at %I:%M %p")%>
              <%end%>
            </td>

            <td><%if !user.last_sign_in_at.nil?%>
              <%= user.last_sign_in_at.strftime('%m-%d-%Y') %><br><%= user.last_sign_in_at.strftime("at %I:%M %p") %>
              <%end%>
            </td>

            <td><%= user.current_sign_in_ip %></td>
            <td><%= user.last_sign_in_ip %></td>
            <td><%= user.created_at.strftime('%m-%d-%Y') %><br><%= user.created_at.strftime("at %I:%M %p") %></td>
            <td>
                   
            </td>
          </tr>

        <% end %>
      </tbody>
    </table>


	EOF
end