require 'rails_helper'
require 'spec_helper'
require 'rspec/expectations'
# @!domain RSpec::Matchers

FULL_VALID_PROPERTIES = {
  ldap: 'fire',
  client_time: 1_570_063_334.427_744,
  user_agent: "some chrome thing",
  client_ip: "127.0.0.1",
  page_url: "admin-panel.internal.justin.tv/twitch/user/1234568",
  session_id: "b9acf2bc-6934-b3b6-df2d-d6b854e37824"
}

def random_string(length:, upper: true, lower: true, number: false)
  l = []
  l.push('a'..'z') if upper
  l.push('A'..'Z') if lower

  l.push(0..9) if number
  l = l.map(&:to_a).flatten
  (0...length).map { l[rand(l.length)] }.join
end

def snake_to_camel(string = "")
  string.split('_').map(&:capitalize).join
end

def trackers
  TrackedEvent
    .create(**FULL_VALID_PROPERTIES)
    .public_methods(false)
    .select { |m| m =~ /admin_panel_/ }
end

describe TrackedEvent do

  describe ".create" do
    let(:valid_object) do
      test_scaffold = Struct.new(:ldap, :client_time, :user_agent, :ip, :url, :session_id)
      test_scaffold.new(*FULL_VALID_PROPERTIES.values)
    end
    let(:partial_valid_properties) { FULL_VALID_PROPERTIES.except(:client_time) }
    let(:extra_invalid_properties) { FULL_VALID_PROPERTIES.merge({ test_prop: 'pls ignore'}) }
    let(:partial_invalid_properties) { FULL_VALID_PROPERTIES.except(:client_ip) }

    context "an event structure with all valid properties" do
      let(:created) { TrackedEvent.create(**FULL_VALID_PROPERTIES) }

      it "returns a new TrackedEvent Scaffold" do
        expect(created.members).to eql(valid_object.members)
        expect(created.entries).to eql(valid_object.entries)
        expect(created.to_h).to eql(valid_object.to_h)
      end
    end
    context "an event structure with required valid properties" do
      let(:created) { TrackedEvent.create(**partial_valid_properties) }
      it "returns a new TrackedEvent Scaffold" do
        expect(created.client_time).not_to be_nil
        expect(created.members).to eql(valid_object.members)
        expect(created.to_h.except(:client_time)).to eql(valid_object.to_h.except(:client_time))
      end
    end
    context "an event structure with invalid properties" do
      it "raises an error" do
        expect { TrackedEvent.create(**extra_invalid_properties) }.to(
          raise_error(ArgumentError, /unknown keyword/)
        )
      end
    end
    context "an event structure with missing properties" do
      it "raises an error" do
        expect { TrackedEvent.create(**partial_invalid_properties) }.to(
          raise_error(ArgumentError, /missing keyword/)
        )
      end
    end
  end

  describe "Scaffold" do

    let(:builder) { TrackedEvent.create(**FULL_VALID_PROPERTIES) }

    context "creates a valid data struct" do
      trackers.each do |tracker|
        describe "for tracking method" do
          it tracker.to_s do
            args = {}
            params = builder.method(tracker).parameters.select { |pair| pair[0] == :keyreq }
            params.each do |_type, param|
              rl = random_string(length: 2, upper: false, lower: false, number: true).to_i
              args[param.to_sym] = random_string(length: rl, upper: true, lower: true, number: true)
            end
            args = [tracker, **args].delete_if(&:empty?)
            expect(builder.send(*args)).to be_instance_of(TrackedEvent::Scaffold.const_get(snake_to_camel(tracker.to_s)))
          end
        end
      end
    end
  end
end
