1package model
2
3import (
4 "context"
5 "fmt"
6
7 tea "github.com/charmbracelet/bubbletea"
8 "github.com/charmbracelet/lipgloss"
9
10 "github.com/bnema/dumber/internal/application/usecase"
11 "github.com/bnema/dumber/internal/cli/styles"
12 "github.com/bnema/dumber/internal/domain/entity"
13)
14
15// PurgeModel wraps styles.PurgeModel for standalone CLI use.
16type PurgeModel struct {
17 selector styles.PurgeModel
18 purgeUC *usecase.PurgeDataUseCase
19
20 loading bool
21 purging bool
22 done bool
23
24 results *usecase.PurgeOutput
25 info string
26 err error
27
28 theme *styles.Theme
29}
30
31// NewPurgeModel creates a new purge command model.
32func NewPurgeModel(theme *styles.Theme, purgeUC *usecase.PurgeDataUseCase) PurgeModel {
33 return PurgeModel{theme: theme, purgeUC: purgeUC, loading: true}
34}
35
36type purgeTargetsLoadedMsg struct {
37 targets []entity.PurgeTarget
38 err error
39}
40
41type purgeCompleteMsg struct {
42 out *usecase.PurgeOutput
43 err error
44}
45
46// Init implements tea.Model.
47func (m PurgeModel) Init() tea.Cmd {
48 return m.loadTargets()
49}
50
51func (m PurgeModel) loadTargets() tea.Cmd {
52 return func() tea.Msg {
53 targets, err := m.purgeUC.GetPurgeTargets(context.Background())
54 return purgeTargetsLoadedMsg{targets: targets, err: err}
55 }
56}
57
58// Update implements tea.Model.
59func (m PurgeModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
60 switch msg := msg.(type) {
61 case purgeTargetsLoadedMsg:
62 m.loading = false
63 if msg.err != nil {
64 m.err = msg.err
65 m.done = true
66 return m, nil
67 }
68
69 m.selector = styles.NewPurge(m.theme, msg.targets)
70 return m, nil
71
72 case purgeCompleteMsg:
73 m.purging = false
74 m.done = true
75 m.results = msg.out
76 if msg.err != nil {
77 m.err = msg.err
78 }
79 return m, nil
80
81 case tea.KeyMsg:
82 if m.done {
83 return m, tea.Quit
84 }
85 if m.loading || m.purging {
86 return m, nil
87 }
88 }
89
90 if m.loading || m.purging {
91 return m, nil
92 }
93
94 selector, cmd := m.selector.Update(msg)
95 m.selector = selector
96
97 if m.selector.Done() {
98 if m.selector.Canceled {
99 return m, tea.Quit
100 }
101 if m.selector.Confirmed {
102 targetTypes := m.selector.SelectedTypes()
103 if len(targetTypes) == 0 {
104 m.done = true
105 m.info = "Nothing selected"
106 return m, nil
107 }
108 m.purging = true
109 return m, m.performPurge(targetTypes)
110 }
111 }
112
113 return m, cmd
114}
115
116func (m PurgeModel) performPurge(targetTypes []entity.PurgeTargetType) tea.Cmd {
117 return func() tea.Msg {
118 out, err := m.purgeUC.Execute(context.Background(), usecase.PurgeInput{TargetTypes: targetTypes})
119 return purgeCompleteMsg{out: out, err: err}
120 }
121}
122
123// View implements tea.Model.
124func (m PurgeModel) View() string {
125 t := m.theme
126
127 if m.loading {
128 spinner := styles.NewLoading(t, "Scanning purge targets...")
129 return t.Box.Render(spinner.View())
130 }
131
132 if m.purging {
133 spinner := styles.NewLoading(t, "Purging...")
134 return t.Box.Render(spinner.View())
135 }
136
137 if m.done {
138 if m.info != "" {
139 content := lipgloss.JoinVertical(
140 lipgloss.Left,
141 t.Subtle.Render(m.info),
142 "",
143 t.Subtle.Render("Press any key to exit"),
144 )
145 return t.Box.Render(content)
146 }
147
148 if m.err != nil && m.results == nil {
149 content := lipgloss.JoinVertical(
150 lipgloss.Left,
151 t.ErrorStyle.Render("Error: "+m.err.Error()),
152 "",
153 t.Subtle.Render("Press any key to exit"),
154 )
155 return t.Box.Render(content)
156 }
157
158 contentLines := []string{t.Title.Render("Purge complete")}
159 if m.results != nil {
160 for _, r := range m.results.Results {
161 if r.Success {
162 contentLines = append(contentLines, fmt.Sprintf("%s %s", t.SuccessStyle.Render(styles.IconCheck), r.Target.Path))
163 } else {
164 contentLines = append(contentLines, fmt.Sprintf("%s %s: %v", t.ErrorStyle.Render(styles.IconX), r.Target.Path, r.Error))
165 }
166 }
167 contentLines = append(
168 contentLines,
169 "",
170 t.Subtle.Render(
171 fmt.Sprintf("%d succeeded, %d failed", m.results.SuccessCount, m.results.FailureCount),
172 ),
173 )
174 }
175 contentLines = append(contentLines, "", t.Subtle.Render("Press any key to exit"))
176 return t.Box.Render(lipgloss.JoinVertical(lipgloss.Left, contentLines...))
177 }
178
179 return m.selector.View()
180}
181
182var _ tea.Model = (*PurgeModel)(nil)