1# frozen_string_literal: true
2
3require "value_semantics/monkey_patched"
4require_relative "../admin_action"
5require_relative "../form_to_h"
6
7class AdminAction
8 class AddInvites < AdminAction
9 class Command
10 using FormToH
11
12 def self.for(target_customer, reply:)
13 EMPromise.resolve(
14 new(customer_id: target_customer.customer_id)
15 ).then { |x|
16 reply.call(x.form).then(&x.method(:create))
17 }
18 end
19
20 def initialize(**bag)
21 @bag = bag
22 end
23
24 def form
25 FormTemplate.render("admin_add_invites")
26 end
27
28 def create(result)
29 AdminAction::AddInvites.for(
30 **@bag,
31 **result.form.to_h
32 .reject { |_k, v| v == "nil" }.transform_keys(&:to_sym)
33 )
34 end
35 end
36
37 CodesTaken = Struct.new(:codes) do
38 def to_s
39 "One of these tokens already exists: #{codes.join(', ')}"
40 end
41 end
42
43 CodesClaimed = Struct.new(:codes) do
44 def to_s
45 "One of these tokens is already claimed: #{codes.join(', ')}"
46 end
47 end
48
49 class MustHaveCodes
50 def to_s
51 "Action must have list of codes to reverse"
52 end
53 end
54
55 def customer_id
56 @attributes[:customer_id]
57 end
58
59 def to_add
60 @attributes[:to_add].to_i
61 end
62
63 def check_forward
64 EMPromise.resolve(nil)
65 .then { check_noop }
66 .then {
67 next nil if chosen_invites.empty?
68
69 InvitesRepo.new.any_existing?(chosen_invites).then { |taken|
70 EMPromise.reject(CodesTaken.new(chosen_invites)) if taken
71 }
72 }
73 end
74
75 def forward
76 if chosen_invites.empty?
77 InvitesRepo.new.create_n_codes(customer_id, to_add).then { |codes|
78 with(invites: codes.join("\n"))
79 }
80 else
81 InvitesRepo.new.create_codes(customer_id, chosen_invites).then { self }
82 end
83 end
84
85 def check_reverse
86 return EMPromise.reject(MustHaveCodes.new) if chosen_invites.empty?
87
88 InvitesRepo.new.any_claimed?(chosen_invites).then { |claimed|
89 EMPromise.reject(CodesClaimed.new(chosen_invites)) if claimed
90 }
91 end
92
93 def reverse
94 InvitesRepo.new.delete_codes(chosen_invites).then { self }
95 end
96
97 def to_s
98 "add_invites(#{customer_id}): #{chosen_invites.join(', ')}"
99 end
100
101 protected
102
103 def check_noop
104 EMPromise.reject(NoOp.new) if to_add.zero?
105 end
106
107 def check_too_many
108 max = split_invites.length
109 EMPromise.reject(TooMany.new(to_add, max)) if to_add > max
110 end
111
112 def chosen_invites
113 @attributes[:invites]&.split("\n") || []
114 end
115 end
116end