import consola from 'consola';
import { connect, JetStreamClient, KV, KvOptions, NatsConnection } from 'nats';
import { config } from '../config';

/**
 * Singleton NATS client.
 * Call connect() once during app startup (after the DB loader).
 * All other modules obtain the connection via getKV().
 */
class NatsClient {
    private connection: NatsConnection | null = null;
    private js: JetStreamClient | null = null;
    private kvBuckets: Map<string, KV> = new Map();

    /**
     * Establishes the NATS connection and JetStream context.
     * Must be called once before any KV operations.
     */
    public async connect(): Promise<void> {
        this.connection = await connect({ servers: config.nats_url });
        this.js = this.connection.jetstream();
        consola.success(`NATS connected to ${config.nats_url}`);
    }

    /**
     * Returns a KV bucket by name, creating it if it doesn't exist yet.
     * Results are cached — repeated calls with the same name return the same instance.
     */
    public async getKV(bucketName: string, options?: Partial<KvOptions>): Promise<KV> {
        if (this.kvBuckets.has(bucketName)) {
            return this.kvBuckets.get(bucketName);
        }
        if (!this.js) {
            throw new Error('NATS not connected. Call NatsClient.connect() first.');
        }
        const kv = await this.js.views.kv(bucketName, options);
        this.kvBuckets.set(bucketName, kv);
        return kv;
    }

    /**
     * Gracefully closes the NATS connection.
     * Call during app shutdown if needed.
     */
    public async disconnect(): Promise<void> {
        if (this.connection) {
            await this.connection.drain();
            this.connection = null;
            this.js = null;
            this.kvBuckets.clear();
            consola.info('NATS disconnected.');
        }
    }

    public isConnected(): boolean {
        return this.connection !== null;
    }
}

export default new NatsClient();
