# HANA TUI - Implementation Plan

## Overview

A single-binary AOT-compiled C# TUI for managing SAP HANA schemas. Built on **.NET 10**
with **Spectre.Console** for rich UI, running against the existing `hdbsql`/`hdbuserstore`
tools already installed on the system.

---

## Project Setup

Update `hanatui.csproj`:
- `<PublishAot>true</PublishAot>`
- `<AllowUnsafeBlocks>true</AllowUnsafeBlocks>`
- `<InvariantGlobalization>true</InvariantGlobalization>`
- `<StripSymbols>true</StripSymbols>`
- NuGet: `Spectre.Console` (core only, no `.Cli`)

Build command:
```bash
dotnet publish -r linux-x64 -c Release -o bin/publish
```
Output: `bin/publish/hanatui` (~15-20 MB single native binary, no runtime dependency)

---

## File Structure

```
hanatui/
├── hanatui.csproj
├── PLAN.md
├── Program.cs
└── src/
    ├── Hana/
    │   ├── HdbClientLocator.cs      # Finds hdbclient path (/usr/sap/hdbclient etc.)
    │   ├── HdbCliRunner.cs          # Spawns hdbsql/hdbuserstore, streams output
    │   ├── HdbUserstoreKey.cs       # Model: key name, host, port, tenant, user
    │   ├── SchemaService.cs         # Lists schemas, builds/runs SQL queries
    │   └── SqlQueryBuilder.cs       # Builds EXPORT/IMPORT/DROP/BACKUP SQL strings
    ├── System/
    │   ├── SystemStats.cs           # Reads /proc/stat + /proc/meminfo, computes deltas
    │   └── CpuSample.cs             # Struct for CPU snapshot (used for delta calc)
    └── Tui/
        ├── KeySelectionScreen.cs    # Startup: arrow-key key picker
        ├── MainMenuScreen.cs        # Main menu with current key shown in header
        ├── OperationForms.cs        # Per-op guided input forms (Export/Import/etc.)
        ├── TaskRunnerScreen.cs      # Live split-panel: stats left, log right
        └── Components/
            ├── StatsPanel.cs        # CPU bars per core + total, RAM bar, Swap bar
            └── LogPanel.cs          # Scrolling timestamped log lines
```

---

## Screen Flow

```
[Launch]
  └─> KeySelectionScreen
        hdbuserstore list → parse keys → arrow-key picker (or manual entry)
        └─> MainMenuScreen  (shows active key + host in header)
              └─> OperationForms  (per operation)
                    1. Fetch schemas (spinner while loading)
                    2. Schema picker (arrow-key selectable list)
                    3. Additional inputs (path, threads, flags)
                    4. Confirmation summary before running
                    └─> TaskRunnerScreen
                          ┌──────────────────┬───────────────────┐
                          │ SYSTEM STATS     │ OPERATION LOG     │
                          │ CPU bars         │ streaming lines   │
                          │   Total [████░░] │ with timestamps   │
                          │   Core0 [███░░░] │                   │
                          │   Core1 [█████░] │                   │
                          │   ...            │                   │
                          │ RAM bar          │                   │
                          │   [██████░░] 58% │                   │
                          │   12.4 / 21.3 GB │                   │
                          │ Swap bar         │                   │
                          │   [██░░░░░░] 18% │                   │
                          │ Elapsed: 4m 32s  │                   │
                          └──────────────────┴───────────────────┘
                          [q]        → "Press Q again to abort"
                          [q again]  → SIGTERM → wait 5s → SIGKILL

                          On complete/abort:
                          - Stats stop refreshing, log stays visible
                          - "Returning in 10s... [any key to stay]"
                          - If key pressed → cancel countdown, stay on result screen
                          - Enter/Esc → back to main menu
```

---

## Operations

| # | Operation          | Key inputs                                      | Notes                        |
|---|--------------------|-------------------------------------------------|------------------------------|
| 1 | Export Schema      | Schema (picker), target path, threads, compress | Shells pigz if available     |
| 2 | Import Schema      | Source schema name, source path, threads, replace | Decompresses .tar.gz if needed |
| 3 | Import & Rename    | Source name, new name, source path, threads, replace | Adds RENAME clause        |
| 4 | Copy Schema        | Schema (picker), target name, temp path, threads, replace | Export then Import-Rename |
| 5 | Drop Schema        | Schema (picker), type YES to confirm            | Destructive — extra confirm  |
| 6 | Rename DB          | Schema (picker), new company name               | Updates CINF + OADM tables   |
| 7 | Backup Tenant      | Target path, threads, compress                  | BACKUP DATA USING FILE       |

---

## System Stats Details

| Metric       | Source           | Method                                        |
|--------------|------------------|-----------------------------------------------|
| CPU % total  | `/proc/stat`     | Delta between two reads 500ms apart           |
| CPU % core N | `/proc/stat`     | Per-core lines (`cpu0`, `cpu1`, ...)          |
| RAM total    | `/proc/meminfo`  | `MemTotal`                                    |
| RAM used     | `/proc/meminfo`  | `MemTotal - MemAvailable`                     |
| Swap total   | `/proc/meminfo`  | `SwapTotal`                                   |
| Swap used    | `/proc/meminfo`  | `SwapTotal - SwapFree`                        |

- Refresh interval: 800ms via `System.Threading.Timer`
- Bar width adapts to terminal width
- Bar format: `[████████░░] 82%`

---

## Abort Behavior

1. First `q` press during running task:
   - Shows warning in log panel: `[WARN] Press Q again to abort the operation`
2. Second `q` press within ~3 seconds:
   - Sends SIGTERM to `hdbsql` child process
   - Waits up to 5 seconds for graceful exit
   - If still running after 5s: sends SIGKILL
   - Logs each step to the log panel

---

## Post-Operation Return Behavior

- After success or abort: stats panel freezes, log remains
- Footer shows: `Returning to menu in 10s... [any key to stay]`
- If any key pressed: countdown cancels, footer changes to `[Enter/Esc] Return to menu`
- User reads the result at their own pace

---

## AOT Compatibility

| Concern                        | Mitigation                                          |
|-------------------------------|------------------------------------------------------|
| No runtime reflection          | All code uses concrete types, no Activator           |
| Spectre.Console prompts        | SelectionPrompt<T>, TextPrompt<T> — AOT-safe        |
| Spectre.Console Live/Layout    | AnsiConsole.Live() — AOT-compatible                 |
| Process spawning               | System.Diagnostics.Process — fully AOT-safe         |
| /proc file reading             | File.ReadAllText — no issues                        |
| Compression                    | Process (pigz/tar) — no native .NET compression     |
| No JsonSerializer              | Not needed                                          |
| No dynamic/Activator           | Enforced throughout                                 |

---

## Key Discovery (hdbuserstore)

Command: `hdbuserstore list`

Sample output:
```
DATA FILE       : /home/user/.hdb/hostname/SSFS_HDB.DAT
KEY FILE        : /home/user/.hdb/hostname/SSFS_HDB.KEY

KEY CRONKEY
  ENV : hostname:30015@NDB
  USER: SYSTEM

KEY DEVKEY
  ENV : devhost:30015@DEV
  USER: SYSTEM
```

Parse: lines starting with `KEY ` → key name. Following `ENV :` and `USER:` lines → details.

---

## SQL Queries Used

```sql
-- List schemas
SELECT SCHEMA_NAME FROM SCHEMAS
WHERE SCHEMA_OWNER = 'SYSTEM'
AND SCHEMA_NAME NOT IN ('_SYS_SECURITY', 'IFSERV', 'B1if', 'SYSTEM', 'RSP');

-- Export
EXPORT "SCHEMA"."*" AS BINARY INTO '/path' WITH REPLACE THREADS N NO DEPENDENCIES;

-- Import
IMPORT "SCHEMA"."*" AS BINARY FROM '/path' WITH IGNORE EXISTING THREADS N;

-- Import-Rename
IMPORT "SCHEMA"."*" AS BINARY FROM '/path'
WITH IGNORE EXISTING RENAME SCHEMA "OLD" TO "NEW" THREADS N;

-- Drop
DROP SCHEMA "SCHEMA" CASCADE;

-- Rename company
UPDATE "SCHEMA".CINF SET "CompnyName" = 'NAME';
UPDATE "SCHEMA".OADM SET "CompnyName" = 'NAME', "PrintHeadr" = 'NAME';

-- Backup tenant
BACKUP DATA USING FILE ('/path/prefix');

-- Get tenant name
SELECT DATABASE_NAME FROM SYS.M_DATABASES;
```
