require "rails_helper"

RSpec.describe Banner, type: :model do

  describe "validations and relations" do
    it { should validate_presence_of(:name) }
    it { should validate_presence_of(:banner_key) }
    it { should have_many(:banner_fields).dependent(:destroy) }
    it { should have_many(:banner_pages).dependent(:destroy) }
    it { should have_many(:pages).through(:banner_pages) }
  end

  context "callbacks" do
    describe "create_banner_fields" do
      it "is called on create" do
        banner = Banner.new(name: "Test", banner_key: Banner.keys.first)
        expect(banner).to receive :create_banner_fields
        banner.save!
      end
    end
  end

  context "scopes" do
    describe "active" do
      it "returns a collection of banners that are currently active" do
        banner = create(:banner, active: true)
        expect(Banner.active).to eq [banner]
      end
    end
  end

  context "class methods" do
    describe ".keys" do
      it "returns the list of keys that map to banner configurations in config/banners.yml" do
        expect(Banner.keys).to eq YAML.load_file(Rails.root.join("config", "banners.yml")).keys
      end
    end

    describe ".banner_from_path" do
      context "while on a preview page" do
        it "returns a banner based on a given url path" do
          page   = create(:page, url: "/#{SecureRandom.hex(42)}")
          path   = "/admin/pages/#{page.id}/preview"
          banner = create(:banner, active: true)
          create(:banner_page, banner: banner, page: page)
          expect(Banner.banner_from_path({ path: path })).to eq banner
        end
      end

      context "while at the canonical url" do
        context "and the banner is set to whitelist urls" do
          it "returns a banner based on a given url path" do
            path   = "/#{SecureRandom.base64}"
            banner = create(:banner, active: true, inclusion_method: "whitelist")
            page   = create(:page, url: path)
            create(:banner_page, banner: banner, page: page)
            expect(Banner.banner_from_path({ path: path })).to eq banner
          end
        end

        context "and the banner is set to blacklist urls" do
          it "returns a banner based on a given url path" do
            path   = "/#{SecureRandom.base64}"
            banner = create(:banner, active: true, inclusion_method: "blacklist")
            page   = create(:page, url: path)
            expect(Banner.banner_from_path({ path: path })).to eq banner
            BannerPage.create!(banner: banner, page: page)
            expect(Banner.banner_from_path({ path: path })).to eq nil
          end
        end
      end
    end
  end

  context "instance methods" do
    describe "#create_banner_fields" do
      it "will create default BannerField records" do
        banner = Banner.new(name: "Test", banner_key: Banner.keys.first)
        banner.save!
        banner.banner_fields.destroy_all # reset the banner fields to 0

        expect { banner.create_banner_fields }.to change { banner.banner_fields.count }.from(0).to(banner.banner_config["fields"].count)
      end
    end

    describe "#banner_config" do
      it "returns a hash of configuration data associated with a given banner" do
        banner_key = Banner.keys.sample
        banner = create(:banner, banner_key: banner_key)
        expect(banner.banner_config).to eq YAML.load_file(Rails.root.join("config", "banners.yml"))[banner_key]
      end
    end

    context "publish/unpublish" do
      let(:banner) { create(:banner) }

      describe "#publish" do
        it "sets active to true and clears the cache" do
          banner.update!(active: false)
          expect_clear_cache
          expect { banner.publish }.to change { banner.active }.to(true)
        end
      end

      describe "#unpublish" do
        it "sets active to false and clears the cache" do
          banner.update!(active: true)
          expect_clear_cache
          expect { banner.unpublish }.to change { banner.active }.to(false)
        end
      end
    end

    describe "#clear_cache!" do
      it "clears the Rails cache" do
        expect_clear_cache
        Banner.new.clear_cache!
      end
    end

    describe "#default_field_data" do
      let(:banner) { create(:banner) }
      let(:mock_field_config) { { "type" => "text" } }

      context "with default value specified in the configuration" do
        it "returns the default value" do
          random_value = SecureRandom.base64(42)
          mock_field_config["default_value"] = random_value
          expect(banner.default_field_data(mock_field_config)).to eq random_value
        end
      end

      context "with no explicit default value in the configuration" do
        context "and type is 'text'" do
          it "returns an empty string" do
            expect(banner.default_field_data(mock_field_config)).to eq ""
          end
        end

        context "and type is 'DateTime'" do
          before(:each) { Timecop.freeze }
          after(:each)  { Timecop.return }
          it "return the current time object" do
            mock_field_config["type"] = "DateTime"
            expect(banner.default_field_data(mock_field_config)).to eq ""
          end
        end

        context "and type is 'KungFu'" do
          it "returns an empty string" do
            mock_field_config["type"] = "KungFu"
            expect(banner.default_field_data(mock_field_config)).to eq ""
          end
        end
      end
    end

    describe "#field_values_hash" do
      it "returns a hash of field names/value data" do
        banner = create(:banner)
        Timecop.freeze
        expected_result = banner.banner_config["fields"].map do |field_key, _field_config|
          [field_key, ""]
        end.to_h
        expect(banner.field_values_hash).to eq expected_result
        Timecop.return
      end
    end

  end

  def expect_clear_cache
    expect(Rails.cache).to receive(:clear)
  end

end
