import chalkPipe from 'chalk-pipe';
import inquirer from 'inquirer';
import tcpPortUsed from 'tcp-port-used';
import URL from 'url';
import Util from "../util/Util";
import path from 'path';
import Lang, {STR} from "../util/Lang";

import('colors');

const dbDefaults = {
  mysql2: {
    host: 'localhost',
    port: '3306',
    username: 'root',
    password: '',
    database: ''
  },
  pg: {
    host: 'localhost',
    port: '5432',
    username: 'postgres',
    password: '',
    database: ''
  },
  mssql: {
    host: 'localhost',
    port: '1433',
    username: 'sa',
    password: '',
    database: ''
  },
  sqlite3: {
    host: 'localhost',
    port: '1433',
    username: 'sa',
    password: '',
    database: ''
  },
};


class TryMgr {

  /**
   *
   * Does the below :
   * - Get database input and make a DB URL from it.
   * - Create new folder and 'cd' to that folder.
   * - Return true/success
   * - Else failure
   *
   * @param args
   * @returns {Promise<string|string|boolean|*>}
   */
  public static async getProjectInput(args): Promise<any> {
    let dbUrl;

    if (args._[0] !== 't' && args._[0] !== 'try') {
      if ( args._[1] === 'rest') {
        dbUrl = `sqlite3://sqlite3?d=${path.join(__dirname, 'sakila.db')}`;
      } else if (args._[1] === 'gql') {
        dbUrl = `sqlite3://sqlite3?d=${path.join(__dirname, 'sakila.db')}&api=graphql`;
      }
    } else {
      /* Construct database URL from prompt */
      const dbTypes = Object.keys(dbDefaults);
      args.url = []

      const finalAnswers = await inquirer
        .prompt([
          {
            name: 'type',
            type: 'list',
            message: Lang.getString(STR.DB_TYPE),// '🔥 Choose SQL Database type\t:',
            choices: dbTypes.map(t => ({
              name: t,
              value: t,
              short: t.green.bold
            })),
            default: 'mysql2',
            transformer(color) {
              return chalkPipe(color)(color.green.bold);
            }
          },
          {
            name: 'host',
            type: 'input',
            message: Lang.getString(STR.DB_HOST),// '👉 Enter database host name\t:',
            default(ans) {
              return dbDefaults[ans.type].host
            },
            transformer(color) {
              return chalkPipe(color)(color.green.bold);
            },
            when({type}) {
              return type !== 'sqlite3'
            }
          },
          {
            name: 'port',
            type: 'number',
            message: Lang.getString(STR.DB_PORT),// '👉 Enter database port number\t:',
            default(ans) {
              return dbDefaults[ans.type].port
            },
            transformer(color) {
              try {
                return color.green.bold;
              } catch (e) {
                return color
              }
            },
            validate(port, answers) {
              const done = this.async();
              if (answers.host === 'host.docker.internal' || answers.host === 'docker.for.win.localhost') {
                done(null, true)
              } else {
                TryMgr.isPortOpen(answers.host, port).then(isOpen => {
                  if (isOpen) {
                    done(null, true)
                  } else {
                    // done('Port is not open')
                    console.log(`\n\n😩 ${answers.host}:${port} is not open please start the database if you haven't\n`.red.bold)
                    process.exit(0);
                  }
                }).catch(done)
              }
            },
            when({type}) {
              return type !== 'sqlite3'
            }
          },
          {
            name: 'username',
            type: 'input',
            message: Lang.getString(STR.DB_USER), // '👉 Enter database username\t:',
            default(ans) {
              return dbDefaults[ans.type].username
            },
            transformer(color) {
              return chalkPipe(color)(color.green.bold);
            },
            when({type}) {
              return type !== 'sqlite3'
            }
          },
          {
            name: 'password',
            type: 'input',
            mask: true,
            message: Lang.getString(STR.DB_PASSWORD), // '🙈 Enter database password\t:',
            transformer(color) {
              return new Array(color.length).fill('*'.green.bold).join('')
            },
            when({type}) {
              return type !== 'sqlite3'
            }
          },
          {
            name: 'database',
            type: 'input',
            message: Lang.getString(STR.DB_SCHEMA), // '👉 Enter database/schema name\t:',
            transformer(color) {
              return chalkPipe(color)(color.green.bold);
            },
            when({type}) {
              return type !== 'sqlite3'
            }
          },
          {
            name: 'apiType',
            type: 'list',
            message: Lang.getString(STR.DB_API), // '🚀 Enter API type to generate\t:',
            choices: ['REST APIs', 'GRAPHQL APIs', 'gRPC APIs'].map(t => ({
              name: t,
              value: t,
              short: t.green.bold
            })),
            transformer(color) {
              return chalkPipe(color)(color.green.bold);
            },
            when({type}) {
              return type !== 'sqlite3'
            }
          },
        ])

      // console.log(finalAnswers);

      if (finalAnswers.type === 'sqlite3') {
        console.log('Please use desktop app to create Sqlite project'.green.bold)
        process.exit(0);
      }

      /* if not valid retry getting right input */
      /*    if (!finalAnswers.database) {
            console.log('\n\tWarning! Database name can NOT be empty. Retry.\n '.red.bold);
            this.getNewProjectInput(args);
          }*/
      //


      /* prepare the args */
      // const url = `${finalAnswers.type}://${finalAnswers.host}:${finalAnswers.port}?u=${finalAnswers.username}&p=${finalAnswers.password}&d=${finalAnswers.database}&api=${apiTypeMapping[finalAnswers.projectType]}`;
      // args._[0] = finalAnswers.projectType === 'REST APIs' ? 'gar' : 'gag';
      switch (finalAnswers.apiType) {
        case 'REST APIs':
          finalAnswers.apiType = 'rest';
          break;
        case 'GRAPHQL APIs':
          finalAnswers.apiType = 'graphql';
          break;
        case 'gRPC APIs':
          finalAnswers.apiType = 'grpc';
          break;
        default:
          finalAnswers.apiType = 'rest';
          break;
      }
      dbUrl = `${finalAnswers.type}://${finalAnswers.host}:${finalAnswers.port}?u=${finalAnswers.username}&p=${finalAnswers.password}&d=${finalAnswers.database}&api=${finalAnswers.apiType}`
    }
    // await Util.runCmd(`cd ${__dirname};echo "const {XcTry} = require('xc-instant'); process.env.NC_DB_URL='${finalAnswers.dbUrl}'; XcTry().then(() => console.log('App started'));" | node`);
    await Util.runCmd(`echo "const {XcTry} = require('xc-instant'); XcTry('${dbUrl}').then(() => console.log('App started'));" | NODE_PATH=${path.join(__dirname, '..', 'node_modules')} node`);

  }


  public static async testConnection({url}) {

    for (const u of url) {
      const parsedUrlData = URL.parse(u, true);
      const queryParams = parsedUrlData.query;
      const client = parsedUrlData.protocol.slice(0, -1);
      const config = {
        client,
        connection: {
          database: client === 'pg' ? 'postgres' : (client === 'mssql' ? undefined : null),
          host: parsedUrlData.hostname,
          password: queryParams.p,
          port: +parsedUrlData.port,
          user: queryParams.u,
        }
      };

      try {
        const knex = require('knex')(config)
        await knex.raw("SELECT 1+1 as data");
      } catch (e) {
        console.log(`\n😩 Test connection failed for : ${url}\n`.red.bold)
        return false;
      }
    }
    return true;
  }


  public static async isPortOpen(host, port) {
    try {
      return await tcpPortUsed.check(+port, host)
    } catch (e) {
      console.log(e)
      console.log(`\n😩 ${host}:${port} is not reachable please check\n`.red.bold)
      return true;
    }
  }

}

export default TryMgr;
/**
 * @copyright Copyright (c) 2021, Xgene Cloud Ltd
 *
 * @author Naveen MR <oof1lab@gmail.com>
 * @author Pranav C Balan <pranavxc@gmail.com>
 *
 * @license GNU AGPL version 3 or any later version
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */
