# frozen_string_literal: true

require 'aws-sdk-sns'
require 'luna_park/extensions/injector'
require_relative 'event'
require_relative 'errors'

module CycloneLariat
  class Client
    include LunaPark::Extensions::Injector

    dependency(:aws_sns_client_class)  { Aws::SNS::Client }
    dependency(:aws_credentials_class) { Aws::Credentials }

    DEFAULT_VERSION  = 1
    DEFAULT_INSTANCE = :prod
    SNS_SUFFIX       = :fanout

    def initialize(key:, secret_key:, region:, version: nil, publisher: nil, instance: nil)
      @key = key
      @secret_key = secret_key
      @region = region
      @version = version
      @publisher = publisher
      @instance = instance
    end

    def event(type, data: {}, version: self.version, uuid: SecureRandom.uuid)
      Event.wrap(
        uuid: uuid,
        type: type,
        sent_at: Time.now.iso8601,
        version: version,
        publisher: publisher,
        data: data
      )
    end

    def publish(msg, to: nil)
      topic = to || [instance, msg.kind, SNS_SUFFIX, publisher, msg.type].join('-')

      aws_client.publish(
        topic_arn: topic_arn(topic),
        message: msg.to_json
      )
    end

    def publish_event(type, data: {}, version: self.version, uuid: SecureRandom.uuid, to: nil)
      publish event(type, data: data, version: version, uuid: uuid), to: to
    end

    class << self
      def version(version = nil)
        version.nil? ? @version || DEFAULT_VERSION : @version = version
      end

      def instance(instance = nil)
        instance.nil? ? @instance || DEFAULT_INSTANCE : @instance = instance
      end

      def publisher(publisher = nil)
        publisher.nil? ? @publisher || (raise 'You should define publisher') : @publisher = publisher
      end
    end

    def version
      @version ||= self.class.version
    end

    def publisher
      @publisher ||= self.class.publisher
    end

    def instance
      @instance ||= self.class.instance
    end

    private

    attr_reader :key, :secret_key, :region

    def topic_arn(topic_name)
      list  = aws_client.list_topics.topics
      topic = list.find { |t| t.topic_arn.match?(topic_name) }
      raise Errors::TopicNotFound.new(expected_topic: topic_name, existed_topics: list.map(&:topic_arn)) if topic.nil?

      topic.topic_arn
    end

    def aws_client
      @aws_client ||= aws_sns_client_class.new(credentials: aws_credentials, region: region)
    end

    def aws_credentials
      @aws_credentials ||= aws_credentials_class.new(key, secret_key)
    end
  end
end