1# frozen_string_literal: true
 2
 3require "em-http"
 4require "em-synchrony/em-http" # For apost vs post
 5require "json"
 6require "lazy_object"
 7require "value_semantics/monkey_patched"
 8
 9require_relative "sim"
10
11class SIMRepo
12	value_semantics do
13		db Anything(), default: LazyObject.new { DB }
14	end
15
16	KEEPGO_HEADERS = {
17		"Accept" => "application/json",
18		"apiKey" => CONFIG[:keepgo][:api_key],
19		"accessToken" => CONFIG[:keepgo][:access_token]
20	}.freeze
21
22	def all(start_page=1)
23		req("lines").aget(
24			head: KEEPGO_HEADERS, query: { page: start_page, per_page: 1000 }
25		).then { |req|
26			result = JSON.parse(req.response)
27			sims = result.dig("sim_cards", "items")&.map(&SIM.method(:extract))
28
29			next sims if result.dig("sim_cards", "last_page") == start_page
30			next [] if !sims || sims.empty?
31
32			all(start_page + 1).then { |next_page| sims + next_page }
33		}
34	end
35
36	def find(iccid)
37		req("line/#{iccid}/get_details").aget(head: KEEPGO_HEADERS).then { |req|
38			SIM.extract(JSON.parse(req.response)&.dig("sim_card"))
39		}
40	end
41
42	def refill(sim, **kwargs)
43		iccid = sim.is_a?(String) ? sim : sim.iccid
44
45		req("line/#{iccid}/refill").apost(
46			head: KEEPGO_HEADERS.merge("Content-Type" => "application/json"),
47			body: kwargs.to_json
48		).then { |req| JSON.parse(req.response) }
49	end
50
51	def update_nicknames(sims)
52		iccids = PG::TextEncoder::Array.new.encode(sims.map(&:iccid))
53		nicknames = PG::TextEncoder::Array.new.encode(sims.map(&:nickname))
54		db.query_defer(<<~SQL, [iccids, nicknames])
55			UPDATE sims SET nickname = data_table.nickname
56			FROM
57			(SELECT UNNEST($1::text[]) AS iccid, UNNEST($2::text[]) AS nickname) AS data_table
58			WHERE sims.iccid = data_table.iccid
59		SQL
60	end
61
62	def owned_by(customer)
63		customer = customer.customer_id if customer.respond_to?(:customer_id)
64		promise = db.query_defer(<<~SQL, [customer])
65			SELECT iccid, nickname FROM sims WHERE customer_id=$1
66		SQL
67		promise.then { |result|
68			EMPromise.all(result.map { |row|
69				find(row["iccid"]).then { |sim| sim.with(nickname: row["nickname"]) }
70			})
71		}
72	end
73
74	def put_owner(sim, customer, nickname=nil)
75		db.exec(<<~SQL, [customer.customer_id, sim.iccid, nickname])
76			UPDATE sims SET customer_id=$1, nickname=COALESCE($3, nickname) WHERE iccid=$2 AND customer_id IS NULL
77		SQL
78	end
79
80	def available
81		db.query_defer(<<~SQL).then { |r| find(r.first["iccid"]) }
82			SELECT iccid FROM sims WHERE customer_id IS NULL LIMIT 1
83		SQL
84	end
85
86protected
87
88	def req(path)
89		EM::HttpRequest.new(
90			"https://myaccount.keepgo.com/api/v2/#{path}",
91			tls: { verify_peer: true }
92		)
93	end
94end