# frozen_string_literal: true

require "bandwidth"
require "value_semantics/monkey_patched"
require "uri"

class CustomerFwd
	class InfiniteTimeout < StandardError
		attr_reader :fwd

		def initialize(fwd)
			super "Infinite timeout"
			@fwd = fwd
		end
	end

	def self.for(uri:, timeout: nil, voicemail_enabled: :default)
		timeout = Timeout.for(timeout, voicemail_enabled: voicemail_enabled)

		fwd = if uri
			if uri =~ /\Asip:(.*)@sip.cheogram.com\Z/
				uri = "xmpp:#{$1.gsub(/%([0-9A-F]{2})/i) { $1.to_i(16).chr }}"
			end

			URIS.fetch(uri.split(":", 2).first&.to_sym) {
				raise "Unknown forward URI: #{uri}"
			}.new(uri: uri, timeout: timeout)
		end

		fwd && !timeout.zero? ? fwd : None.new(uri: uri, timeout: timeout)
	end

	class Timeout
		def self.for(s, voicemail_enabled: :default)
			return Infinite.new unless voicemail_enabled

			if s.nil? || s.is_a?(Infinite) || s.to_i.negative?
				return new(25) if voicemail_enabled == true # ~5s / ring, 5 rings

				return Infinite.new
			end

			return s if s.is_a?(self)

			new(s)
		end

		def initialize(s)
			@timeout = [s.to_i, 300].min
		end

		def zero?
			@timeout.zero?
		end

		def infinite?
			false
		end

		def to_i
			@timeout
		end

		def to_s
			to_i.to_s
		end

		def as_json(*)
			to_i
		end

		def to_json(*args)
			as_json.to_json(*args)
		end

		class Infinite < Timeout
			def initialize; end

			def zero?
				false
			end

			def infinite?
				1
			end

			def to_i; end

			def as_json(*)
				nil
			end
		end
	end

	value_semantics do
		uri Either(/:/, NilClass)
		def_attr :timeout, Timeout, coerce: Timeout.method(:for)
	end

	def with(new_attrs)
		CustomerFwd.for(to_h.merge(new_attrs))
	end

	def voicemail_enabled?
		!timeout.infinite?
	end

	def create_call(account)
		raise InfiniteTimeout, self if timeout.infinite?

		request = Bandwidth::ApiCreateCallRequest.new.tap { |cc|
			cc.to = to
			cc.call_timeout = timeout.to_i
			yield cc if block_given?
		}
		BANDWIDTH_VOICE.create_call(account, body: request).data.call_id
	end

	def as_json(*)
		{ timeout: timeout, uris: [uri] }
	end

	def to_json(*args)
		as_json.to_json(*args)
	end

	class Tel < CustomerFwd
		def initialize(values)
			super
			raise "Bad tel format: #{uri}" unless uri.match?(/\Atel:\+1\d{10}\Z/)
		end

		def to
			uri.sub(/^tel:/, "")
		end
	end

	class SIP < CustomerFwd
		def to
			uri
		end
	end

	class XMPP < CustomerFwd
		def to
			jid = uri.sub(/^xmpp:/, "")
			"sip:#{ERB::Util.url_encode(jid)}@sip.cheogram.com"
		end
	end

	class None < CustomerFwd
		def create_call(*); end

		def to; end

		def as_json(*)
			[]
		end
	end

	URIS = {
		tel: Tel,
		sip: SIP,
		xmpp: XMPP
	}.freeze
end
