# frozen_string_literal: true

require "rails_helper"

RSpec.describe ActiveStorageValidations::TotalSizeValidator do
  let(:validator_test_class) { TotalSize::Validator }
  let(:params) { {} }

  describe "ASVAttachable shared behavior" do
    it_behaves_like "ASVAttachable"
  end

  describe "#(custom_)check_validity!" do
    it_behaves_like "checks validator validity"

    context "when used with has_one_attached" do
      subject(:result) { instance.invalid.attach(blob_file_1ko) and instance }

      let(:instance) { validator_test_class::CheckValidityHasManyAttachedOnly.new(params) }

      it "raises an error at model initialization" do
        expect { result.valid? }.to raise_error(ArgumentError, "This validator is only available for has_many_attached relations")
      end
    end
  end

  describe "Validator checks" do
    let(:model) { validator_test_class::Check.new(params) }

    describe ":less_than" do
      let(:file_having_lower_than_less_than_option) { file_1ko }
      let(:file_having_exact_less_than_option) { file_2ko }
      let(:file_having_higher_than_less_than_option) { file_5ko }
      let(:error_name) { "total_file_size_not_less_than" }
      let(:error_options_for_file_having_exact_less_than_option) do
        {
          total_file_size: "2 KB",
          min: nil,
          max: "2 KB"
        }
      end
      let(:error_options_for_file_having_higher_than_less_than_option) do
        {
          total_file_size: "5 KB",
          min: nil,
          max: "2 KB"
        }
      end

      it_behaves_like "comparison less_than option"
    end

    describe ":less_than_or_equal_to" do
      let(:file_having_lower_than_less_than_or_equal_to_option) { file_1ko }
      let(:file_having_exact_less_than_or_equal_to_option) { file_2ko }
      let(:file_having_higher_than_less_than_or_equal_to_option) { file_5ko }
      let(:error_name) { "total_file_size_not_less_than_or_equal_to" }
      let(:error_options_for_file_having_exact_less_than_or_equal_to_option) do
        {
          total_file_size: "2 KB",
          min: nil,
          max: "2 KB"
        }
      end
      let(:error_options_for_file_having_higher_than_less_than_or_equal_to_option) do
        {
          total_file_size: "5 KB",
          min: nil,
          max: "2 KB"
        }
      end

      it_behaves_like "comparison less_than_or_equal_to option"
    end

    describe ":greater_than" do
      let(:file_having_lower_than_greater_than_option) { file_1ko }
      let(:file_having_exact_greater_than_option) { file_7ko }
      let(:file_having_higher_than_greater_than_option) { file_10ko }
      let(:error_name) { "total_file_size_not_greater_than" }
      let(:error_options_for_file_having_lower_than_greater_than_option) do
        {
          total_file_size: "1 KB",
          min: "7 KB",
          max: nil
        }
      end
      let(:error_options_for_file_having_exact_greater_than_option) do
        {
          total_file_size: "7 KB",
          min: "7 KB",
          max: nil
        }
      end

      it_behaves_like "comparison greater_than option"
    end

    describe ":greater_than_or_equal_to" do
      let(:file_having_lower_than_greater_than_or_equal_to_option) { file_1ko }
      let(:file_having_exact_greater_than_or_equal_to_option) { file_7ko }
      let(:file_having_higher_than_greater_than_or_equal_to_option) { file_10ko }
      let(:error_name) { "total_file_size_not_greater_than_or_equal_to" }
      let(:error_options_for_file_having_lower_than_greater_than_or_equal_to_option) do
        {
          total_file_size: "1 KB",
          min: "7 KB",
          max: nil
        }
      end

      it_behaves_like "comparison greater_than_or_equal_to option"
    end

    describe ":between" do
      let(:file_having_lower_than_lower_bound_between_option) { file_1ko }
      let(:file_having_exact_lower_bound_between_option) { file_2ko }
      let(:file_having_between_bounds_between_option) { file_5ko }
      let(:file_having_exact_higher_bound_between_option) { file_7ko }
      let(:file_having_higher_than_higher_bound_between_option) { file_10ko }
      let(:error_name) { "total_file_size_not_between" }
      let(:error_options_for_file_having_lower_than_lower_bound_between_option) do
        {
          total_file_size: "1 KB",
          min: "2 KB",
          max: "7 KB"
        }
      end
      let(:error_options_for_file_having_higher_than_higher_bound_between_option) do
        {
          total_file_size: "10.2 KB",
          min: "2 KB",
          max: "7 KB"
        }
      end

      it_behaves_like "comparison between option"
    end

    describe ":equal_to" do
      let(:file_having_lower_than_equal_to_option) { file_1ko }
      let(:file_having_exact_equal_to_option) { file_5ko }
      let(:file_having_higher_than_equal_to_option) { file_7ko }
      let(:error_name) { "total_file_size_not_equal_to" }
      let(:error_options_for_file_having_lower_than_equal_to_option) do
        {
          total_file_size: "1 KB",
          exact: "5 KB"
        }
      end
      let(:error_options_for_file_having_higher_than_equal_to_option) do
        {
          total_file_size: "7 KB",
          exact: "5 KB"
        }
      end

      it_behaves_like "comparison equal_to option"
    end

    describe "multi-file sum" do
      # validates :equal_to, total_size: { equal_to: 5.kilobytes }
      let(:attribute) { :equal_to }

      context "when the sum of several files equals the bound" do
        subject(:record) { model.public_send(attribute).attach([ file_2ko, file_2ko, file_1ko ]) and model }

        it { is_expected_to_be_valid }
      end

      context "when the sum of several files is lower than the bound" do
        subject(:record) { model.public_send(attribute).attach([ file_1ko, file_1ko ]) and model }

        let(:error_options) do
          {
            total_file_size: "2 KB",
            exact: "5 KB"
          }
        end

        it { is_expected_not_to_be_valid }
        it { is_expected_to_include_error_message("total_file_size_not_equal_to", with_locales: [ "en" ], error_options: error_options) }
        it { is_expected_to_have_error_options(error_options) }
      end

      context "when the sum of several files is higher than the bound" do
        subject(:record) { model.public_send(attribute).attach([ file_2ko, file_5ko ]) and model }

        let(:error_options) do
          {
            total_file_size: "7 KB",
            exact: "5 KB"
          }
        end

        it { is_expected_not_to_be_valid }
        it { is_expected_to_include_error_message("total_file_size_not_equal_to", with_locales: [ "en" ], error_options: error_options) }
        it { is_expected_to_have_error_options(error_options) }
      end
    end
  end

  describe "Rails options" do
    it_behaves_like "works with all rails common validation options"
  end
end
