/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
const { rmdir, copyFile } = require("fs");

// https://stackoverflow.com/questions/61934443/read-excel-files-in-cypress
const readXlsx = require("./read-xlsx");
const makeServer = require("./server");
/**
 * @type {Cypress.PluginConfig}
 */
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
  // `on` is used to hook into various events Cypress emits
  // `config` is the resolved Cypress config

  // register utility tasks to read and parse Excel files
  on("task", {
    copyFile() {
      console.log("copyFile", __dirname);
      return new Promise((resolve, reject) => {
        copyFile(
          "./scripts/cypress/fixtures/quickTest/noco_0_91_7.db",
          "./packages/nocodb/noco.db",
          (err) => {
            if (err) {
              console.log(err);
              return reject(err);
            }
            resolve(null);
          }
        );
      });
    },
    deleteFolder(folderName) {
      console.log("deleting folder %s", folderName);

      return new Promise((resolve, reject) => {
        rmdir(folderName, { maxRetries: 10, recursive: true }, (err) => {
          if (err) {
            console.error(err);

            return reject(err);
          }

          resolve(null);
        });
      });
    },
    readXlsx: readXlsx.read,
    readSheetList: readXlsx.sheetList,
    log(message) {
      console.log(`##Cypress>> ${message}`);
      return null;
    },
    queryDb: (query) => {
      return queryTestDb(query, config);
    },
    sqliteExec: (query) => {
      _sqliteExec(query);
      return null;
    },
    sqliteExecReturnValue: (query) => {
      return _sqliteExecReturnValue(query);
    },
    pgExec: (query) => {
      _pgExec(query);
      return null;
    },
    pgExecTest: (query) => {
      return _pgExecTest(query);
    },
  });

  let server, port, close;

  on("before:spec", async (spec) => {
    // we can customize the server based on the spec about to run
    const info = await makeServer();
    // save the server instance information
    server = info.server;
    port = info.port;
    close = info.close;
    console.log("started the server on port %d", port);
  });

  on("after:spec", async (spec) => {
    if (!server) {
      console.log("no server to close");
      return;
    }
    await close();
    console.log("closed the server running on port %d", port);
  });
};

// mysql connection
// https://gist.github.com/fityanos/0a345e9e9de498b6c629f78e6b2835f5

const mysql = require("mysql2");
function queryTestDb(query, config) {
  // creates a new mysql connection using credentials from cypress.json env's
  const connection = mysql.createConnection(config.env.db);
  // start connection to db
  connection.connect();
  // exec query + disconnect to db as a Promise
  return new Promise((resolve, reject) => {
    connection.query(query, (error, results) => {
      if (error) reject(error);
      else {
        connection.end();
        // console.log(results)
        return resolve(results);
      }
    });
  });
}

// sqlite connection
const sqlite3 = require("sqlite3").verbose();
function _sqliteExecReturnValue(query) {
  // open the database
  console.log("Current directory: " + process.cwd());
  let db = new sqlite3.Database(
    "./scripts/cypress/fixtures/sqlite-sakila/sakila.db",
    sqlite3.OPEN_READWRITE,
    (err) => {
      if (err) {
        console.error(err.message);
      } else {
        console.log("Connected to the noco xcdb database.");
      }
    }
  );

  // exec query + disconnect to db as a Promise
  return new Promise((resolve, reject) => {
    db.get(query, [], (err, row) => {
      db.close();
      if (err) {
        reject(err);
      } else {
        return resolve(row);
      }
    });
  });
}

function _sqliteExec(query) {
  // open the database
  console.log("Current directory: " + process.cwd());
  let db = new sqlite3.Database(
    "./scripts/cypress/fixtures/sqlite-sakila/sakila.db",
    sqlite3.OPEN_READWRITE,
    (err) => {
      if (err) {
        console.error(err.message);
      } else {
        console.log("Connected to the noco xcdb database.");
      }
    }
  );

  db.serialize(() => {
    db.run(query);
  });

  db.close((err) => {
    if (err) {
      console.error(err.message);
    } else {
      console.log("Close the database connection.");
    }
  });
}

// pg connection
const { Pool, Client } = require("pg");
const pg_credentials = {
  user: "postgres",
  host: "localhost",
  database: "postgres",
  password: "password",
  port: 5432,
};
function _pgExec(query) {
  // open pg client connection
  const client = new Client(pg_credentials);
  client.connect();

  // query & terminate
  client.query(query, (err, res) => {
    console.log(err, res);
    client.end();
  });
}

function _pgExecTest(query, retryCount = 60) {
  return new Promise((resolve, reject) => {
    // open pg client connection
    const client = new Client(pg_credentials);

    client.connect().catch(() => {
      client.end();
    });

    // query & terminate
    client.query(query, (err, res) => {
      if (err) {
        if (retryCount--)
          return setTimeout(
            () => _pgExecTest(query, retryCount).then(resolve).catch(reject),
            2000
          );
        else reject(err);
      }

      console.log("==== success ===", res);
      resolve(true);
      client.end();
    });
  });
}