# frozen_string_literal: true

require "em_promise"
require "delegate"
require "ruby-bandwidth-iris"
require "lazy_object"

require_relative "db_port"

class PortRepo
	def initialize(output, dry_run: false)
		@output = output
		@dry_run = dry_run
		@step_repo = PortingStepRepo.new(output: output)
	end

	def list
		raise NotImplementedError, "subclass must implement"
	end

	def process
		list.then { |ports|
			EMPromise.all(ports.map(&method(:tick)))
		}
	end

	class Bandwidth < self
		class PortWithBackend < SimpleDelegator
			def backend_sgx
				CONFIG[:sgx]
			end

			def id
				self[:order_id]
			end

			def customer_id
				self[:customer_order_id]
			end

			def tel
				self[:billing_telephone_number]
			end

			def updated_at
				self[:last_modified_date]
			end
		end

		def list
			(BandwidthIris::PortIn.list(
				page: 1,
				size: 50,
				start_date: Date.today - 1,
				end_date: Date.today
			) || []).map(&PortWithBackend.method(:new))
		end
	end

	class Db < self
		def initialize(
			output,
			dry_run: false,
			db: LazyObject.new { DB },
			filters: {}
		)
			super(output, dry_run: dry_run)
			@db = db
			@filters = filters
		end

		def list(**kwargs)
			rows(**@filters.merge(kwargs)).then { |r|
				r.map { |row|
					DbPort.from(**row.transform_keys(&:to_sym))
				}
			}
		end

	protected

		def rows(
			max_results: 50,
			offset: 0,
			start_date: Date.today - 1,
			end_date: Date.today + 1,
			customer_id: nil
		)
			conditions = ["actual_foc_date >= $1", "actual_foc_date <= $2"]
			params = [start_date, end_date]

			if customer_id
				conditions << "customer_id = $#{params.length + 1}"
				params << customer_id
			end

			@db.exec_defer(<<~SQL, params + [max_results, offset])
				SELECT * FROM ports
				WHERE #{conditions.join(' AND ')}
				LIMIT $#{params.length + 1}
				OFFSET $#{params.length + 2}
			SQL
		end
	end

	class Fake < Bandwidth
		FakePort = Struct.new(
			:id,
			:processing_status,
			:actual_foc_date,
			:updated_at,
			:customer_id,
			:tel,
			:backend_sgx
		)

		def list
			minutes = 1.0 / (24 * 60)

			[
				# This should be ignored
				FakePort.new(
					"T01", "SUBMITTED", nil, DateTime.now - 1,
					"ignored", "9998887777", "testroute"
				),
				FakePort.new(
					"T02", "COMPLETE", DateTime.now - (60 * minutes),
					DateTime.now - (55 * minutes), "0001", "2223334444", "testroute"
				)
			]
		end
	end

protected

	def tick(port)
		@step_repo.find(port).then { |step|
			@output.info(port.id, :class, step.class)
			if @dry_run
				@output.info("DRY", :dry, "Not taking action")
			else
				step.perform_next_step
			end
		}
	end
end
