const mysql = require("mysql2/promise");
const { Client } = require("ssh2");
const { Pool } = require('pg');
const dotenv = require("dotenv");
const path = require("path");

// Helper function to setup SSH tunnel
async function setupSshTunnel(sshConfig) {
  return new Promise((resolve, reject) => {
    const ssh = new Client();

    ssh.on('error', (err) => {
      console.error('SSH connection error:', err);
    });

    ssh.on('end', () => {
      console.log('SSH connection ended normally');
    });

    ssh.on('close', () => {
      console.log('SSH connection closed');
    });

    ssh
      .on("ready", () => {
        ssh.forwardOut(
          "127.0.0.1",
          0,
          sshConfig.prodDbConfig.host,
          sshConfig.prodDbConfig.port,
          async (err, stream) => {
            if (err) reject(err);
            resolve({ ssh, stream });
          }
        );
      })
      .connect(sshConfig.ssh);
  });
}

// Helper function to setup database connections
async function setupConnections(sshConfig) {
  const tunnel = await setupSshTunnel(sshConfig);
  
  // Setup MySQL connection for production
  const prodConnection = await mysql.createConnection({
    ...sshConfig.prodDbConfig,
    stream: tunnel.stream,
  });

  // Setup PostgreSQL connection pool for local
  const localPool = new Pool(sshConfig.localDbConfig);

  // Test the PostgreSQL connection
  try {
    const client = await localPool.connect();
    await client.query('SELECT NOW()');
    client.release();
    console.log('PostgreSQL connection successful');
  } catch (err) {
    console.error('PostgreSQL connection error:', err);
    throw err;
  }

  // Create a wrapper for the PostgreSQL pool to match MySQL interface
  const localConnection = {
    _client: null,
    _transactionActive: false,

    query: async (text, params) => {
      // If we're not in a transaction, use the pool directly
      if (!localConnection._transactionActive) {
        const client = await localPool.connect();
        try {
          const result = await client.query(text, params);
          return [result];
        } finally {
          client.release();
        }
      }
      
      // If we're in a transaction, use the dedicated client
      if (!localConnection._client) {
        throw new Error('No active transaction client');
      }
      const result = await localConnection._client.query(text, params);
      return [result];
    },

    beginTransaction: async () => {
      if (localConnection._transactionActive) {
        throw new Error('Transaction already active');
      }
      localConnection._client = await localPool.connect();
      await localConnection._client.query('BEGIN');
      localConnection._transactionActive = true;
    },

    commit: async () => {
      if (!localConnection._transactionActive) {
        throw new Error('No active transaction to commit');
      }
      await localConnection._client.query('COMMIT');
      localConnection._client.release();
      localConnection._client = null;
      localConnection._transactionActive = false;
    },

    rollback: async () => {
      if (!localConnection._transactionActive) {
        throw new Error('No active transaction to rollback');
      }
      await localConnection._client.query('ROLLBACK');
      localConnection._client.release();
      localConnection._client = null;
      localConnection._transactionActive = false;
    },

    end: async () => {
      if (localConnection._client) {
        localConnection._client.release();
        localConnection._client = null;
      }
      await localPool.end();
    }
  };

  return { prodConnection, localConnection, tunnel };
}

// Helper function to close connections
async function closeConnections(connections) {
  const { ssh, prodConnection, localConnection } = connections;
  
  try {
    if (prodConnection) await prodConnection.end();
    if (localConnection) await localConnection.end();
    
    // Wait a bit for any pending data to be written before closing SSH
    await new Promise(resolve => setTimeout(resolve, 100));
    
    if (ssh) {
      ssh.on('close', () => {
        console.log('SSH connection closed cleanly');
      });
      ssh.end();
    }
  } catch (err) {
    console.error('Error during cleanup:', err);
  }
}

module.exports = {
  setupConnections,
  closeConnections
}; 