import { Etcd3 } from 'etcd3';
import log4js from 'log4js';
import dotenv from 'dotenv';

dotenv.config();

const ETCD_HOST = process.env.ETCD_HOST || 'localhost';
const ETCD_PORT = process.env.ETCD_PORT || 2379;
const LOG_LEVEL = process.env.LOG_LEVEL || 'info';

const logger = log4js.getLogger('Etcd');
logger.level = LOG_LEVEL;

class EtcdServer {
  constructor() {
    this.client = null;
    this.watchers = new Map();
    this.hosts = [`${ETCD_HOST}:${ETCD_PORT}`];
    logger.trace(`EtcdServer constructor: hosts set to ${JSON.stringify(this.hosts)}`);
  }

  async connect() {
    if (!this.client) {
      logger.info('Connecting to Etcd...');
      logger.trace(`Creating Etcd client with hosts ${JSON.stringify(this.hosts)}`);
      this.client = new Etcd3({
        hosts: this.hosts,
      });

      // Test connection
      try {
        await this.client.get('test-connection').string();
        logger.trace('Etcd client connected successfully.');
      } catch (error) {
        if (error.code === 'NOT_FOUND') {
          logger.trace('Etcd client connected successfully (test key not found as expected).');
        } else {
          throw error;
        }
      }
    } else {
      logger.trace('Etcd client already exists, skipping connection.');
    }
    return this.client;
  }

  async getClient() {
    logger.trace('Checking if Etcd client exists.');
    if (!this.client) {
      logger.trace('No client found, calling connect().');
      await this.connect();
    }
    logger.trace('Returning Etcd client.');
    return this.client;
  }

  // Hash-like functionality using etcd
  async setKey(key, value) {
    const client = await this.getClient();
    const stringValue = typeof value === 'string' ? value : JSON.stringify(value);

    await client.put(key).value(stringValue);
    logger.trace(`Set key: ${key}, value: ${stringValue}`);
    return true;
  }

  async getKey(key) {
    const client = await this.getClient();

    try {
      const value = await client.get(key).string();
      logger.trace(`Retrieved key: ${key}, value: ${value}`);

      // Try to parse as JSON, fallback to string
      try {
        return JSON.parse(value);
      } catch {
        return value;
      }
    } catch (error) {
      if (error.code === 'NOT_FOUND') {
        logger.trace(`Key not found: ${key}`);
        return null;
      }
      throw error;
    }
  }

  async disconnect() {
    logger.info('Disconnecting from Etcd...');

    // Stop all watchers
    for (const [key, watcher] of this.watchers) {
      logger.trace(`Stopping watcher: ${key}`);
      watcher.removeAllListeners();
      await watcher.close();
    }
    this.watchers.clear();

    if (this.client) {
      await this.client.close();
      this.client = null;
      logger.info('Disconnected from Etcd');
    }
  }
}

const etcdServer = new EtcdServer();

export { EtcdServer, etcdServer };
