# NDKNutzapMonitor

The `NDKNutzapMonitor` class monitors a user's nutzap inbox for new nutzaps and processes them automatically. It handles the full lifecycle of nutzaps, from discovery to redemption.

## Features

- Monitors relays for nutzaps sent to a specific user
- Automatically redeems nutzaps when the appropriate private key is available
- Keeps track of nutzap states (initial, processing, redeemed, spent, error)
- Persists states across application sessions using the configured NDK Cache Adapter
- Emits events for tracking monitor activity

## Basic Usage

```typescript
import { NDKNutzapMonitor } from "@nostr-dev-kit/ndk-wallet";
import NDK, { NDKUser, NDKPrivateKeySigner, NDKCashuMintList } from "@nostr-dev-kit/ndk";
import { NDKCashuWallet } from "@nostr-dev-kit/ndk-wallet"; // Example wallet

// Assume ndk: NDK, user: NDKUser, mintList?: NDKCashuMintList are initialized
// Assume myCashuWallet: NDKCashuWallet is initialized
// Assume myPrivateKeySigner: NDKPrivateKeySigner is initialized

// Create a monitor for a user
// The store is now automatically derived from ndk.cacheAdapter if available
const monitor = new NDKNutzapMonitor(ndk, user, {
    mintList,
    // No need to pass 'store' manually if using a compatible cache adapter
});

// Set the wallet to use for redeeming nutzaps
monitor.wallet = myCashuWallet;

// If you have extra private keys that might have been used to receive p2pk-locked keys, you can also add them
// this is almost always unnecessary -- the nutzap monitor will try to do this for you if the private keys are
// properly stored as nostr events per the NIP-60 spec.
await monitor.addPrivkey(myPrivateKeySigner);

// Start monitoring for nutzaps
await monitor.start({});

// Listen for events
monitor.on("redeemed", (events, amount) => {
    console.log(`Redeemed ${events.length} nutzaps for ${amount} sats`);
});
```

## State Management

The monitor uses a state machine to track the status of each nutzap. The possible states (`NdkNutzapStatus`) are defined in `@nostr-dev-kit/ndk`:

- `INITIAL`: First time we see a nutzap
- `PROCESSING`: Currently processing the nutzap
- `REDEEMED`: Successfully redeemed
- `SPENT`: The nutzap has already been spent
- `MISSING_PRIVKEY`: No private key available to redeem the nutzap
- `TEMPORARY_ERROR`: A transient error occurred
- `PERMANENT_ERROR`: A permanent error occurred (will not retry)
- `INVALID_NUTZAP`: The nutzap data is invalid

## State Persistence (via Cache Adapter)

The `NDKNutzapMonitor` now leverages the configured `ndk.cacheAdapter` for persisting nutzap states across application sessions.

If the `ndk.cacheAdapter` implements the optional `getAllNutzapStates` and `setNutzapState` methods (as defined in the `NDKCacheAdapter` interface in `@nostr-dev-kit/ndk`), the monitor will automatically use these methods to load initial states and save updates.

### Cache Adapter Interface Methods

The relevant methods in the `NDKCacheAdapter` interface are:

```typescript
// Defined in @nostr-dev-kit/ndk
interface NDKCacheAdapter {
    // ... other methods

    /**
     * Gets all nutzap states from the cache.
     * @returns A map of event IDs to nutzap states.
     */
    getAllNutzapStates?(): Promise<Map<NDKEventId, NDKNutzapState>>;

    /**
     * Sets the state of a nutzap in the cache.
     * @param id The ID of the nutzap event.
     * @param stateChange The partial state change to apply.
     */
    setNutzapState?(id: NDKEventId, stateChange: Partial<NDKNutzapState>): Promise<void>;
}
```

### State Updates

When updating a nutzap's state, the monitor calls `cacheAdapter.setNutzapState` with only the changed properties (`Partial<NDKNutzapState>`), not the entire state object. This allows the cache adapter implementation to efficiently merge updates.

For example:

```typescript
// Internal call within NDKNutzapMonitor
await ndk.cacheAdapter?.setNutzapState?.(nutzapId, {
    status: NdkNutzapStatus.REDEEMED,
    redeemedAmount: 100,
});
```

### Implementing Persistence

If you are creating a custom cache adapter and want it to support nutzap state persistence, you need to implement the `getAllNutzapStates` and `setNutzapState` methods.

NDK Mobile (`@nostr-dev-kit/ndk-mobile`) provides an implementation using SQLite via its `NDKCacheAdapterSqlite`. If you use this adapter with your NDK instance, nutzap state persistence will work automatically.

```typescript
// Example: Initializing NDK with the SQLite adapter from ndk-mobile
import NDK from "@nostr-dev-kit/ndk";
import { NDKCacheAdapterSqlite } from "@nostr-dev-kit/ndk-mobile";

const cacheAdapter = new NDKCacheAdapterSqlite("my-ndk-cache.db");
await cacheAdapter.initialize(); // Important: Initialize the adapter

const ndk = new NDK({
    cacheAdapter: cacheAdapter,
    // ... other NDK options
});

// Pass the NDK instance to the adapter AFTER NDK is initialized
// This allows the adapter to use the NDK instance if needed (e.g., for deserializing events)
cacheAdapter.ndk = ndk;

// Now, when NDKNutzapMonitor is created with this ndk instance,
// it will use the SQLite adapter for persistence.
```

## Events

The monitor emits several events that you can listen for:

- `seen_in_unknown_mint`: Emitted when a nutzap is seen in a mint not in the user's mint list
- `state_changed`: Emitted when the state of a nutzap changes
- `redeemed`: Emitted when a nutzap is successfully redeemed
- `seen`: Emitted when a new nutzap is seen
- `failed`: Emitted when a nutzap fails to be redeemed

## Managing Private Keys

The monitor needs private keys to redeem nutzaps. Typically, you don't need to do this manually; setting the `monitor.wallet` property should provide the necessary keys if the wallet implementation supports it (like `NDKCashuWallet`).

You can also add extra private keys using:

```typescript
await monitor.addPrivkey(privateKeySigner);
```

You can add multiple private keys, and the monitor will automatically use the appropriate key for each nutzap.

If a nutzap requires a private key that isn't available, it will be marked as `MISSING_PRIVKEY`. If the key is later added, the monitor will automatically attempt to redeem the nutzap.

## Error Handling

The monitor handles various error conditions:

- If a nutzap cannot be redeemed due to a missing private key, it's marked as `MISSING_PRIVKEY`
- If the redemption fails with a transient error, it's marked as `TEMPORARY_ERROR`
- If the redemption fails with a permanent error (e.g., "unknown public key size"), it's marked as `PERMANENT_ERROR`

The monitor will automatically retry nutzaps with temporary errors but not those with permanent errors.
