package banking import ( "context" "fmt" "io" "math/rand/v2" "time" tea "github.com/charmbracelet/bubbletea" "git.t-juice.club/torjus/oubliette/internal/shell" ) const sessionTimeout = 10 * time.Minute // BankingShell is an 80s-style green-on-black bank terminal TUI. type BankingShell struct{} // NewBankingShell returns a new BankingShell instance. func NewBankingShell() *BankingShell { return &BankingShell{} } func (b *BankingShell) Name() string { return "banking" } func (b *BankingShell) Description() string { return "80s-style banking terminal TUI" } func (b *BankingShell) Handle(ctx context.Context, sess *shell.SessionContext, rw io.ReadWriteCloser) error { ctx, cancel := context.WithTimeout(ctx, sessionTimeout) defer cancel() bankName := configString(sess.ShellConfig, "bank_name", "SECUREBANK") terminalID := configString(sess.ShellConfig, "terminal_id", "") region := configString(sess.ShellConfig, "region", "NORTHEAST") if terminalID == "" { terminalID = fmt.Sprintf("SB-%04d", rand.IntN(10000)) } m := newModel(sess, bankName, terminalID, region) p := tea.NewProgram(m, tea.WithInput(rw), tea.WithOutput(rw), tea.WithAltScreen(), ) done := make(chan error, 1) go func() { _, err := p.Run() done <- err }() select { case err := <-done: return err case <-ctx.Done(): p.Quit() <-done return nil } } // configString reads a string from the shell config map with a default. func configString(cfg map[string]any, key, defaultVal string) string { if cfg == nil { return defaultVal } if v, ok := cfg[key]; ok { if s, ok := v.(string); ok && s != "" { return s } } return defaultVal }