import os from 'os';
import clear from 'clear';

// Import helpers
import showChooser from '../helpers/showChooser';
import print from '../helpers/print';
import exec from '../helpers/exec';
import config from '../helpers/config';
import prompt from '../helpers/prompt';

// Import shared types
import Deployment from '../types/Deployment';

// Get home directory
const homeDir = os.homedir();

/**
 * Connect/tunnel into a db
 * @author Gabe Abrams
 * @param deployment the currently selected deployment
 */
const connectToDatabase = async (deployment: Deployment) => {
  // Get dbName
  const { dbName } = config;

  // Make sure the deployment has a db
  if (!dbName) {
    clear();
    print.title('No Database!');
    console.log('');
    console.log('This deployment does not have a database.');
    console.log('');
    print.enterToContinue();
    return;
  }

  // Destructure
  const {
    name,
    app,
    profile,
  } = deployment;

  // Kill apps
  clear();
  print.title('Connect to database');
  console.log('\nEnter sudo to cleanup existing sessions:');
  exec(
    'sudo lsof -ti tcp:27018 | xargs kill',
    true,
  );
  clear();
  print.title('Connect to database');
  console.log('');

  // Run the connection script
  const stackName = `CacclDeploy-${app}`;
  // > Allow for alternate stack names
  const bastionInstanceId = exec(
    `aws cloudformation describe-stacks --stack-name ${stackName} --query "Stacks[].Outputs[?ExportName=='${stackName}-bastion-host-id'].OutputValue" --profile ${profile} --output text`,
    false,
  ).trim();
  console.log(`Bastion Instance Id: ${bastionInstanceId}`);
  // > Endpoint/hostname of the db
  const dbEndpoint = exec(
    `aws cloudformation describe-stacks --stack-name ${stackName} --query "Stacks[].Outputs[?ExportName=='${stackName}-db-cluster-endpoint'].OutputValue" --profile ${profile} --output text`,
    false,
  ).trim();
  console.log(`DB Endpoint: ${dbEndpoint}`);
  // > Arn of the secretsmanager secret where the db master password is stored
  const dbPasswordSecret = exec(
    `aws cloudformation describe-stacks --stack-name ${stackName} --query "Stacks[].Outputs[?ExportName=='${stackName}-db-password-secret-arn'].OutputValue" --profile ${profile} --output text`,
    false,
  ).trim();
  console.log(`DB Password Secret: ${dbPasswordSecret}`);
  // > Get DB password
  const dbPassword = exec(
    `aws secretsmanager get-secret-value --secret-id ${dbPasswordSecret} --query 'SecretString' --profile ${profile} --output text`,
    false,
  ).trim();
  console.log(`DB Password: ${dbPassword}`);
  // > Arn of the secretsmanager secret where the db availability zone is stored
  const bastionAz = exec(
    `aws cloudformation describe-stacks --stack-name ${stackName} --query "Stacks[].Outputs[?ExportName=='${stackName}-bastion-host-az'].OutputValue" --profile ${profile} --output text`,
    false,
  ).trim();
  console.log(`Bastion AZ: ${bastionAz}`);
  // > Arn of the secretsmanager secret where the db IP is stored
  const bastionIP = exec(
    `aws cloudformation describe-stacks --stack-name ${stackName} --query "Stacks[].Outputs[?ExportName=='${stackName}-bastion-host-ip'].OutputValue" --profile ${profile} --output text`,
    false,
  ).trim();
  console.log(`Bastion IP: ${bastionIP}`);
  // > Add public key to AWS for 60s
  exec(
    `aws ec2-instance-connect send-ssh-public-key --instance-id ${bastionInstanceId} --instance-os-user ec2-user --availability-zone ${bastionAz} --ssh-public-key file://${homeDir}/.ssh/id_rsa.pub --profile ${profile}`,
    false,
  );
  console.log('Public key added to AWS');

  // Start tunneling
  console.log('Tunneling into AWS...');
  exec(
    `ssh -C -o StrictHostKeyChecking=no -f -L 27018:${dbEndpoint} ec2-user@${bastionIP} sleep 330`,
    true,
  );

  // Finished
  console.log('\nFinished! Scroll up for errors or more details.');

  // Choose an operation
  const { tag } = showChooser({
    question: 'Operation:',
    options: [
      {
        description: 'Robo 3T Setup Instructions',
        tag: 'R',
      },
      {
        description: 'Studio 3T Setup Instructions',
        tag: 'S',
      },
      {
        description: 'Export Collection',
        tag: 'E',
      },
      {
        description: 'Insert Items in Collection',
        tag: 'I',
      },
      {
        description: 'Upsert Items in Collection',
        tag: 'U',
      },
      {
        description: 'Back to Main Menu',
        tag: 'B',
      },
    ],
    title: 'Choose an operation:',
  });

  // Robo 3T
  if (tag === 'R') {
    clear();
    print.title('Robo 3T Setup Instructions');
    console.log(
      [
        '1. Open Robo 3T, create a new connection',
        '2. Fill out the "Connection" tab:',
        `    Name = ${name}`,
        `    Address = localhost`,
        '    Port = 27018',
        '3. Fill out the "Authentication" tab:',
        '    Check the "Perform authentication" box',
        `    Database = ${dbName}`,
        `    Username = root`,
        `    Password = ${dbPassword}`,
        '4. Fill out the "SSL" tab:',
        '    Check the "Use SSL protocol" box',
        '    Authentication Method = Self-signed certificate',
        '    Check the "Advanced Options" box',
        '    Invalid Hostnames = Allowed',
        '5. Click "Test"',
        '6. If the test is successful, click "Save"',
      ].join('\n'),
    );
    console.log('');
    print.enterToContinue();
  }

  // Studio 3T
  if (tag === 'S') {
    clear();
    print.title('Studio 3T Setup Instructions');
    console.log(
      [
        '1. Open Studio 3T, create a new connection',
        '2. Choose "Manually configure my connection settings", then click "Next"',
        `3. Set the "Connection name" to ${name}`,
        '4. Fill out the "Server" tab:',
        '    Connection Type = Standalone',
        '    Address = localhost',
        '    Port = 27018',
        '5. Fill out the "Authentication" tab:',
        '    Authentication Mode = Legacy',
        `    Username = root`,
        `    Password = ${dbPassword}`,
        `    Authentication DB = ${dbName}`,
        '6. Fill out the "SSL" tab:',
        '    Check the "Use SSL protocol to connect" box',
        '    Check the "Allow invalid hostnames" box',
        '5. Click "Test Connection"',
        '6. If the test is successful, click "Save"',
      ].join('\n'),
    );
    console.log('');
    await print.enterToContinue();
  }

  // Export Collection
  if (tag === 'E') {
    clear();
    print.title('Export Collection');
    
    // Get the collection name
    const collectionName = prompt('Collection Name: ');
    const outFilename = `${collectionName}-${new Date().toISOString()}.json`;
    console.log(`\nExporting collection to ${outFilename}\n`);
    exec(
      `mongoexport --collection=${collectionName} --host="localhost:27018" --db=${dbName} --out="${outFilename}" --username=root --password=${dbPassword} --ssl --sslAllowInvalidHostnames --sslAllowInvalidCertificates`,
      true,
    );
    console.log(`\nExported to ${outFilename}\n`);
    await print.enterToContinue();
  }

  // Insert Items in Collection
  if (tag === 'I') {
    clear();
    print.title('Insert Items in Collection');
    
    // Get the collection name
    const collectionName = prompt('Collection Name: ');
    console.log('\nCreate a JSON file (one entry per line), drop the file here, then press enter.');
    const jsonPath = prompt('Path: ').trim();

    console.log(`\nInserting items into collection "${collectionName}"\n`);
    exec(
      `mongoimport --collection=${collectionName} --host="localhost:27018" --db=${dbName} --username=root --password=${dbPassword} --ssl --sslAllowInvalidHostnames --sslAllowInvalidCertificates --file="${jsonPath}"`,
      true,
    );
    print.title('Inserted Successfully');
    await print.enterToContinue();
  }

  // Upsert Items in Collection
  if (tag === 'U') {
    clear();
    print.title('Upsert Items in Collection');

    // Get the collection name
    const collectionName = prompt('Collection Name: ');
    console.log('\nCreate a JSON file (one entry per line), drop the file here, then press enter.');
    const jsonPath = prompt('Path: ').trim();

    console.log(`\nUpserting items into collection "${collectionName}"\n`);
    exec(
      `mongoimport --upsert --collection=${collectionName} --host="localhost:27018" --db=${dbName} --username=root --password=${dbPassword} --ssl --sslAllowInvalidHostnames --sslAllowInvalidCertificates --file="${jsonPath}"`,
      true,
    );
    print.title('Upsert Successful');
    await print.enterToContinue();
  }
};

export default connectToDatabase;
