// Copyright IBM Corp. 2013,2019. All Rights Reserved.
// Node module: loopback-connector-mysql
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

'use strict';
require('./init.js');
const assert = require('assert');
const should = require('should');
const DataSource = require('loopback-datasource-juggler').DataSource;
const mysqlConnector = require('../');
const url = require('url');

let db, DummyModel, odb, config;

describe('connections', function() {
  before(function() {
    require('./init.js');

    config = global.getConfig();

    odb = global.getDataSource({collation: 'utf8_general_ci',
      createDatabase: true});
    db = odb;
  });

  it('should pass with valid settings', function(done) {
    const db = new DataSource(mysqlConnector, config);
    db.ping(done);
  });

  it('ignores all other settings when url is present', function(done) {
    const formatedUrl = generateURL(config);
    const dbConfig = {
      url: formatedUrl,
      host: 'invalid-hostname',
      port: 80,
      database: 'invalid-database',
      username: 'invalid-username',
      password: 'invalid-password',
    };

    const db = new DataSource(mysqlConnector, dbConfig);
    db.ping(done);
  });

  it('should use utf8 charset', function(done) {
    const test_set = /utf8/;
    const test_collo = /utf8_general_ci/;
    const test_set_str = 'utf8';
    const test_set_collo = 'utf8_general_ci';
    charsetTest(test_set, test_collo, test_set_str, test_set_collo, done);
  });

  it('should disconnect first db', function(done) {
    db.disconnect(function() {
      odb = global.getDataSource();
      done();
    });
  });

  it('should disconnect then connect and ORM should work', function() {
    const ds = new DataSource(mysqlConnector, config);
    const Student = ds.define('Student', {
      name: {type: String, length: 255},
      age: {type: Number},
    }, {
      forceId: false,
    });

    return ds.connect()
      .then(function(err) {
        should.not.exist(err);
        return ds.automigrate(['Student']);
      })
      .then(function(err) {
        should.not.exist(err);
        should(ds.connected).be.True();
        return ds.disconnect();
      })
      .then(function(err) {
        should.not.exist(err);
        should(ds.connected).be.False();
        return ds.connect();
      })
      .then(function(err) {
        should.not.exist(err);
        should(ds.connected).be.True();
        return Student.count();
      })
      .then(function(count) {
        should(count).be.a.Number();
        return ds.disconnect();
      });
  });

  it('should use latin1 charset', function(done) {
    const test_set = /latin1/;
    const test_collo = /latin1_general_ci/;
    const test_set_str = 'latin1';
    const test_set_collo = 'latin1_general_ci';
    charsetTest(test_set, test_collo, test_set_str, test_set_collo, done);
  });

  it('should drop db and disconnect all', function(done) {
    db.connector.execute('DROP DATABASE IF EXISTS ' + db.settings.database, function(err) {
      db.disconnect(function() {
        done();
      });
    });
  });

  describe('lazyConnect', function() {
    it('should skip connect phase (lazyConnect = true)', function(done) {
      const dbConfig = {
        host: '127.0.0.1',
        port: 4,
        lazyConnect: true,
      };
      const ds = new DataSource(mysqlConnector, dbConfig);

      const errTimeout = setTimeout(function() {
        done();
      }, 2000);
      ds.on('error', function(err) {
        clearTimeout(errTimeout);
        done(err);
      });
    });

    it('should report connection error (lazyConnect = false)', function(done) {
      const dbConfig = {
        host: '127.0.0.1',
        port: 4,
        lazyConnect: false,
      };
      const ds = new DataSource(mysqlConnector, dbConfig);

      ds.on('error', function(err) {
        err.message.should.containEql('ECONNREFUSED');
        done();
      });
    });
  });
});

function charsetTest(test_set, test_collo, test_set_str, test_set_collo, done) {
  query('DROP DATABASE IF EXISTS ' + odb.settings.database, function(err) {
    assert.ok(!err);
    odb.disconnect(function() {
      db = global.getDataSource({collation: test_set_collo,
        createDatabase: true});
      DummyModel = db.define('DummyModel', {string: String});
      db.automigrate(function() {
        const q = 'SELECT DEFAULT_COLLATION_NAME' +
          ' FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ' +
          db.driver.escape(db.settings.database) + ' LIMIT 1';
        db.connector.execute(q, function(err, r) {
          assert.ok(!err);
          should(r[0].DEFAULT_COLLATION_NAME).match(test_collo);
          db.connector.execute('SHOW VARIABLES LIKE "character_set%"', function(err, r) {
            assert.ok(!err);
            let hit_all = 0;
            for (const result in r) {
              hit_all += matchResult(r[result], 'character_set_connection', test_set);
              hit_all += matchResult(r[result], 'character_set_database', test_set);
              hit_all += matchResult(r[result], 'character_set_results', test_set);
              hit_all += matchResult(r[result], 'character_set_client', test_set);
            }
            assert.equal(hit_all, 4);
          });
          db.connector.execute('SHOW VARIABLES LIKE "collation%"', function(err, r) {
            assert.ok(!err);
            let hit_all = 0;
            for (const result in r) {
              hit_all += matchResult(r[result], 'collation_connection', test_set);
              hit_all += matchResult(r[result], 'collation_database', test_set);
            }
            assert.equal(hit_all, 2);
            done();
          });
        });
      });
    });
  });
}

function matchResult(result, variable_name, match) {
  if (result.Variable_name === variable_name) {
    assert.ok(result.Value.match(match));
    return 1;
  }
  return 0;
}

function query(sql, cb) {
  odb.connector.execute(sql, cb);
}

function generateURL(config) {
  const urlObj = {
    protocol: 'mysql',
    auth: config.username || '',
    hostname: config.host,
    pathname: config.database,
    slashes: true,
  };
  if (config.password) {
    urlObj.auth += ':' + config.password;
  }
  const formatedUrl = url.format(urlObj);
  return formatedUrl;
}