# frozen_string_literal: true

require "spec_helper"
require_relative "../../lib/whodunit/generator"

RSpec.describe Whodunit::Generator do
  let(:test_dir) { "test-rails-app" }
  let(:config_dir) { "#{test_dir}/config" }
  let(:initializer_file) { "#{test_dir}/config/initializers/whodunit.rb" }
  let(:application_record_file) { "#{test_dir}/app/models/application_record.rb" }

  before do
    # Clean up any existing test directory
    FileUtils.rm_rf(test_dir)
  end

  after do
    # Clean up test directory after each test
    FileUtils.rm_rf(test_dir)
  end

  describe ".install_initializer" do
    context "in a Rails application" do
      before do
        # Create mock Rails app structure
        FileUtils.mkdir_p(config_dir)
        File.write("#{config_dir}/application.rb", "# Mock Rails application")
        Dir.chdir(test_dir)
      end

      after do
        Dir.chdir("..")
      end

      it "creates the initializer file" do
        # Capture output to avoid cluttering test output
        expect { described_class.install_initializer }.to output(%r{Generated config/initializers/whodunit\.rb}).to_stdout

        expect(File.exist?("config/initializers/whodunit.rb")).to be true
      end

      it "creates the initializers directory if it doesn't exist" do
        expect { described_class.install_initializer }.to output(%r{Creating config/initializers}).to_stdout

        expect(Dir.exist?("config/initializers")).to be true
      end

      it "generates the correct file header and structure" do
        described_class.install_initializer

        content = File.read("config/initializers/whodunit.rb")

        expect(content).to include("# Whodunit.configure do |config|")
        expect(content).to include("# end")
        expect(content).to include("# frozen_string_literal: true")
        expect(content).to include("# This file was generated by `whodunit install` command.")
      end

      it "includes all basic configuration options" do
        described_class.install_initializer

        content = File.read("config/initializers/whodunit.rb")

        expect(content).to include("#   config.user_class = 'Account'             # Default: 'User'")
        expect(content).to include("#   config.creator_column = :created_by_id    # Default: :creator_id")
        expect(content).to include("#   config.updater_column = :updated_by_id    # Default: :updater_id")
        expect(content).to include("#   config.deleter_column = :deleted_by_id    # Default: :deleter_id")
        expect(content).to include("#   config.soft_delete_column = :discarded_at # Default: nil")
      end

      it "includes data type configuration options" do
        described_class.install_initializer

        content = File.read("config/initializers/whodunit.rb")

        expect(content).to include("#   config.column_data_type = :integer       # Default: :bigint")
        expect(content).to include("#   config.creator_column_type = :string     # Default: nil")
        expect(content).to include("#   config.updater_column_type = :uuid       # Default: nil")
        expect(content).to include("#   config.deleter_column_type = :integer    # Default: nil")
      end

      context "when initializer already exists" do
        before do
          FileUtils.mkdir_p("config/initializers")
          File.write("config/initializers/whodunit.rb", "# Existing content")
        end

        it "prompts for overwrite confirmation" do
          # Mock user input to decline overwrite
          allow($stdin).to receive(:gets).and_return("n\n")

          expect { described_class.install_initializer }.to output(/already exists/).to_stdout
                                                                                    .and raise_error(SystemExit)
        end
      end
    end

    context "outside a Rails application" do
      before do
        Dir.chdir(test_dir) if Dir.exist?(test_dir)
      end

      after do
        Dir.chdir("..") if Dir.exist?(test_dir)
      end

      it "shows an error message" do
        # Create directory but no Rails app
        FileUtils.mkdir_p(test_dir)
        Dir.chdir(test_dir)

        expect { described_class.install_initializer }.to output(/doesn't appear to be a Rails application/).to_stdout
                                                                                                            .and raise_error(SystemExit)
      end
    end
  end

  describe ".help_message" do
    it "returns helpful usage information" do
      help = described_class.help_message

      expect(help).to include("Whodunit - Lightweight creator/updater/deleter tracking")
      expect(help).to include("whodunit install")
      expect(help).to include("whodunit help")
      expect(help).to include("Generate config/initializers/whodunit.rb")
      expect(help).to include("https://github.com/kanutocd/whodunit")
    end
  end

  describe "ApplicationRecord integration" do
    let(:application_record_content) do
      <<~RUBY
        # frozen_string_literal: true

        class ApplicationRecord < ActiveRecord::Base
          primary_abstract_class
        end
      RUBY
    end

    before do
      FileUtils.rm_rf(test_dir)
      FileUtils.mkdir_p(config_dir)
      File.write("#{config_dir}/application.rb", "# Mock Rails application")
      FileUtils.mkdir_p("#{test_dir}/app/models")
      File.write(application_record_file, application_record_content)
      Dir.chdir(test_dir)
    end

    after do
      Dir.chdir("..")
      FileUtils.rm_rf(test_dir)
    end

    context "when user accepts integration" do
      before { allow($stdin).to receive(:gets).and_return("y\n") }

      it "adds Whodunit::Stampable to ApplicationRecord" do
        expect { described_class.install_initializer }.to output(
          /Added Whodunit::Stampable to ApplicationRecord/
        ).to_stdout

        content = File.read("app/models/application_record.rb")
        expect(content).to include("include Whodunit::Stampable")
      end

      it "adds the include after the class definition" do
        described_class.install_initializer

        content = File.read("app/models/application_record.rb")
        lines = content.split("\n")

        class_line_index = lines.index { |line| line.include?("class ApplicationRecord") }
        include_line_index = lines.index { |line| line.include?("include Whodunit::Stampable") }

        expect(include_line_index).to eq(class_line_index + 1)
      end

      it "shows success message about automatic stamping" do
        expect { described_class.install_initializer }.to output(
          /All your models will now automatically include stamping!/
        ).to_stdout
      end
    end

    context "when user declines integration" do
      before { allow($stdin).to receive(:gets).and_return("n\n") }

      it "does not modify ApplicationRecord" do
        original_content = File.read("app/models/application_record.rb")
        described_class.install_initializer
        content = File.read("app/models/application_record.rb")
        expect(content).to eq(original_content)
      end

      it "does not show integration success message" do
        expect { described_class.install_initializer }.not_to output(
          /Added Whodunit::Stampable to ApplicationRecord/
        ).to_stdout
      end
    end

    context "when ApplicationRecord already includes Whodunit::Stampable" do
      let(:application_record_content) do
        <<~RUBY
          # frozen_string_literal: true

          class ApplicationRecord < ActiveRecord::Base
            include Whodunit::Stampable
            primary_abstract_class
          end
        RUBY
      end

      it "skips integration and shows already included message" do
        expect { described_class.install_initializer }.to output(
          /Whodunit::Stampable already included in ApplicationRecord/
        ).to_stdout
      end

      it "does not prompt user for integration" do
        expect { described_class.install_initializer }.not_to output(
          /Do you want to include Whodunit::Stampable in ApplicationRecord/
        ).to_stdout
      end
    end

    context "when ApplicationRecord has non-standard formatting" do
      let(:application_record_content) do
        <<~RUBY
          class ApplicationRecord<ActiveRecord::Base
            primary_abstract_class
          end
        RUBY
      end

      before { allow($stdin).to receive(:gets).and_return("y\n") }

      it "uses fallback pattern matching" do
        expect { described_class.install_initializer }.to output(
          /Added Whodunit::Stampable to ApplicationRecord/
        ).to_stdout

        content = File.read("app/models/application_record.rb")
        expect(content).to include("include Whodunit::Stampable")
      end
    end

    context "when ApplicationRecord cannot be parsed" do
      let(:application_record_content) { "# Malformed or empty file" }

      before { allow($stdin).to receive(:gets).and_return("y\n") }

      it "shows manual integration message" do
        expect { described_class.install_initializer }.to output(
          /Could not automatically modify ApplicationRecord/
        ).to_stdout
      end

      it "provides manual instructions" do
        expect { described_class.install_initializer }.to output(
          /Please manually add: include Whodunit::Stampable/
        ).to_stdout
      end
    end

    context "when ApplicationRecord file does not exist" do
      before { FileUtils.rm_f("app/models/application_record.rb") }

      it "shows file not found message" do
        expect { described_class.install_initializer }.to output(
          %r{ApplicationRecord not found at app/models/application_record\.rb}
        ).to_stdout
      end

      it "does not prompt for integration" do
        expect { described_class.install_initializer }.not_to output(
          /Do you want to include Whodunit::Stampable/
        ).to_stdout
      end
    end
  end
end
