require "rspec"
require "json"
require "dry-types"
require "dry-validation"

module Types
  include Dry::Types.module

  RegionCode = Types::Strict::String.constrained(
    format: /\A(au|nz|uk|ie)\z/,
  )
end

class OpenSettlement < Dry::Types::Value
  attribute :region_code, Types::RegionCode
  attribute :period_ending_on, Types::Strict::Date
end

class ArgSchemas
  def self.[](struct)
    Dry::Validation.Form do
      struct.schema.each { |attr, type| key(attr).required(type) }
    end
  end
end

PayloadSchema = Dry::Validation.Form do
  key(:command).required(inclusion?: %w(OpenSettlement))
  key(:args).required(:hash?)

  rule(open_settlement_args: [:command, :args]) do |command, args|
    command.eql?('OpenSettlement').then(args.schema(ArgSchemas[OpenSettlement]))
  end
end

class CommandsController
  attr_reader :payload

  def initialize(payload)
    @payload = payload
  end

  def call
    PayloadSchema.call(payload).messages
  end
end

RSpec.describe "command enqueuing" do
  it "errors with an empty payload" do
    payload = {}
    controller = CommandsController.new(payload)
    errors = controller.call

    expect(errors).to eq(
      command: ["is missing", "must be one of: OpenSettlement"],
      args: ["is missing"],
    )
  end

  it "errors when command is missing from payload" do
    payload = {
      args: {},
    }
    controller = CommandsController.new(payload)
    errors = controller.call

    expect(errors).to eq(
      command: ["is missing", "must be one of: OpenSettlement"],
      args: ["must be filled"]
    )
  end

  it "errors with an invalid set of args" do
    payload = {
      command: "OpenSettlement",
      args: {
        region_code: "au",
      },
    }
    controller = CommandsController.new(payload)
    errors = controller.call

    expect(errors).to eq(
      args: {
        period_ending_on: ["is missing"],
      },
    )
  end

  it "validates correct params" do
    payload = {
      command: "OpenSettlement",
      args: {
        region_code: "au",
        period_ending_on: "2016-03-31",
      }
    }
    controller = CommandsController.new(payload)
    errors = controller.call

    expect(errors).to be_empty
  end
end