require "./spec_helper"
require "./reset_migrations_spec/**"

describe Marten::CLI::Manage::Command::ResetMigrations do
  with_installed_apps(
    Marten::CLI::Manage::Command::ResetMigrationsSpec::FooApp,
    Marten::CLI::Manage::Command::ResetMigrationsSpec::BarApp,
    Marten::CLI::Manage::Command::ResetMigrationsSpec::EmptyApp
  )

  before_all do
    FileUtils.rm_rf(Marten::CLI::Manage::Command::ResetMigrationsSpec.expected_migration_filepath)
    Marten::DB::Management::Migrations::Recorder.new(Marten::DB::Connection.default).setup
    Marten::DB::Management::Migrations::Recorder.new(Marten::DB::Connection.get(:other)).setup
  end

  after_all do
    FileUtils.rm_rf(Marten::CLI::Manage::Command::ResetMigrationsSpec.expected_migration_filepath)

    introspector = Marten::DB::Management::Introspector.for(Marten::DB::Connection.default)
    Marten::DB::Management::SchemaEditor.run_for(Marten::DB::Connection.default) do |schema_editor|
      if introspector.table_names.includes?("reset_migrations_spec_foo_app_tags")
        schema_editor.delete_table("reset_migrations_spec_foo_app_tags")
      end

      if introspector.table_names.includes?("reset_migrations_spec_bar_app_tags")
        schema_editor.delete_table("reset_migrations_spec_bar_app_tags")
      end
    end

    # Reset local migration app configs to avoid them to be used elsewhere.
    Migration::ResetMigrationsSpec::FooApp::V202108092226111.reset_app_config
    Migration::ResetMigrationsSpec::FooApp::V202108092226112.reset_app_config
    Migration::ResetMigrationsSpec::BarApp::V202108092226111.reset_app_config
    Migration::ResetMigrationsSpec::BarApp::V202108092226112.reset_app_config
  end

  describe "#run" do
    it "prints an error if no app label is not specified" do
      stdout = IO::Memory.new
      stderr = IO::Memory.new

      command = Marten::CLI::Manage::Command::ResetMigrations.new(
        options: [] of String,
        stdout: stdout,
        stderr: stderr
      )

      command.handle

      stderr.rewind.gets_to_end.includes?("You must specify an app label").should be_true
    end

    it "prints an error if the specified app label is not associated with any existing app" do
      stdout = IO::Memory.new
      stderr = IO::Memory.new

      command = Marten::CLI::Manage::Command::ResetMigrations.new(
        options: ["unknown_app"],
        stdout: stdout,
        stderr: stderr
      )

      command.handle

      stderr.rewind.gets_to_end.includes?(
        "Label 'unknown_app' is not associated with any installed apps"
      ).should be_true
    end

    it "prints the expected messages if no changes are detected because the app is empty" do
      stdout = IO::Memory.new
      stderr = IO::Memory.new

      command = Marten::CLI::Manage::Command::ResetMigrations.new(
        options: ["reset_migrations_spec_empty_app"],
        stdout: stdout,
        stderr: stderr
      )

      command.handle

      stdout.rewind.gets_to_end.strip.should eq "No changes detected"
    end

    it "generates a replacement migration for a given app" do
      stdout = IO::Memory.new
      stderr = IO::Memory.new

      command = Marten::CLI::Manage::Command::ResetMigrations.new(
        options: ["reset_migrations_spec_foo_app"],
        stdout: stdout,
        stderr: stderr
      )

      time = Marten::CLI::Manage::Command::ResetMigrationsSpec::EXPECTED_MIGRATION_TIME
      Timecop.freeze(time) do
        command.handle

        File.exists?(Marten::CLI::Manage::Command::ResetMigrationsSpec.expected_migration_filepath).should be_true
        generated_migration = File.read(Marten::CLI::Manage::Command::ResetMigrationsSpec.expected_migration_filepath)

        generated_migration.split.map(&.strip).should eq(
          (
            <<-MIGRATION
            # Generated by Marten #{Marten::VERSION} on #{time.to_local}

            class Migration::ResetMigrationsSpecFooApp::V#{Time.local.to_s("%Y%m%d%H%M%S")}1 < Marten::Migration
              replaces :reset_migrations_spec_foo_app, "202108092226111_auto"
              replaces :reset_migrations_spec_foo_app, "202108092226112_auto"

              def plan
                create_table :reset_migrations_spec_foo_app_tag do
                  column :id, :big_int, primary_key: true, auto: true
                  column :label, :string, max_size: 255, unique: true
                  column :active, :bool, default: true
                end
              end
            end

            MIGRATION
          ).split.map(&.strip)
        )
      end
    end

    it "outputs the expected message when generating a replacement migration for a given app" do
      stdout = IO::Memory.new
      stderr = IO::Memory.new

      command = Marten::CLI::Manage::Command::ResetMigrations.new(
        options: ["reset_migrations_spec_foo_app"],
        stdout: stdout,
        stderr: stderr
      )

      time = Marten::CLI::Manage::Command::ResetMigrationsSpec::EXPECTED_MIGRATION_TIME
      Timecop.freeze(time) do
        command.handle
      end

      output = stdout.rewind.gets_to_end
      output.includes?("Generating migrations for app 'reset_migrations_spec_foo_app':").should be_true
      output.includes?("○ Create reset_migrations_spec_foo_app_tag table").should be_true
    end
  end
end

module Marten::CLI::Manage::Command::ResetMigrationsSpec
  EXPECTED_MIGRATION_TIME = Time.utc(2022, 2, 15, 10, 20, 30)

  def self.expected_migration_filepath
    Timecop.freeze(EXPECTED_MIGRATION_TIME) do
      File.join(
        __DIR__,
        "reset_migrations_spec/foo_app/migrations/",
        "#{Time.local.to_s("%Y%m%d%H%M%S")}1_create_reset_migrations_spec_foo_app_tag_table.cr"
      )
    end
  end
end
