import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import ADB from '../..';
import path from 'path';
import { rootDir } from '../../lib/helpers.js';
import { apiLevel, platformVersion, MOCHA_TIMEOUT } from './setup';
import { fs, mkdirp } from 'appium-support';
import temp from 'temp';
import _ from 'lodash';
import { waitForCondition } from 'asyncbox';


chai.should();
chai.use(chaiAsPromised);
const expect = chai.expect;

const DEFAULT_IMES = [
  'com.android.inputmethod.latin/.LatinIME',
  'com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME',
  'io.appium.android.ime/.UnicodeIME',
];
const CONTACT_MANAGER_PATH = path.resolve(rootDir, 'test', 'fixtures', 'ContactManager.apk');
const CONTACT_MANAGER_PKG = 'com.example.android.contactmanager';
const CONTACT_MANAGER_ACTIVITY = 'ContactManager';


describe('adb commands', function () {
  this.timeout(MOCHA_TIMEOUT);

  let adb;
  const androidInstallTimeout = 90000;
  before(async function () {
    adb = await ADB.createADB({ adbExecTimeout: 60000 });
  });
  it('getApiLevel should get correct api level', async function () {
    (await adb.getApiLevel()).should.equal(apiLevel);
  });
  it('getPlatformVersion should get correct platform version', async function () {
    const actualPlatformVersion = await adb.getPlatformVersion();
    parseFloat(platformVersion).should.equal(parseFloat(actualPlatformVersion));
  });
  it('availableIMEs should get list of available IMEs', async function () {
    (await adb.availableIMEs()).should.have.length.above(0);
  });
  it('enabledIMEs should get list of enabled IMEs', async function () {
    (await adb.enabledIMEs()).should.have.length.above(0);
  });
  it('defaultIME should get default IME', async function () {
    const defaultIME = await adb.defaultIME();
    if (defaultIME) {
      DEFAULT_IMES.should.include(defaultIME);
    }
  });
  it('enableIME and disableIME should enable and disable IME', async function () {
    const imes = await adb.availableIMEs();
    if (imes.length < 2) {
      return this.skip();
    }

    const ime = _.last(imes);
    await adb.disableIME(ime);
    (await adb.enabledIMEs()).should.not.include(ime);
    await adb.enableIME(ime);
    (await adb.enabledIMEs()).should.include(ime);
  });
  it('processExists should be able to find ui process', async function () {
    (await adb.processExists('com.android.systemui')).should.be.true;
  });
  it('ping should return true', async function () {
    (await adb.ping()).should.be.true;
  });
  it('getPIDsByName should return pids', async function () {
    (await adb.getPIDsByName('com.android.phone')).should.have.length.above(0);
  });
  it('killProcessesByName should kill process', async function () {
    if (process.env.CI) {
      this.retries(2);
    }

    await adb.install(CONTACT_MANAGER_PATH, {
      timeout: androidInstallTimeout,
      grantPermissions: true,
    });
    await adb.startApp({pkg: CONTACT_MANAGER_PKG, activity: CONTACT_MANAGER_ACTIVITY});
    await adb.killProcessesByName(CONTACT_MANAGER_PKG);
    await waitForCondition(async () => (await adb.getPIDsByName(CONTACT_MANAGER_PKG)).length === 0, {
      waitMs: 5000,
      intervalMs: 500,
    });
  });
  it('killProcessByPID should kill process', async function () {
    await adb.install(CONTACT_MANAGER_PATH, {
      timeout: androidInstallTimeout,
      grantPermissions: true,
    });
    await adb.startApp({pkg: CONTACT_MANAGER_PKG, activity: CONTACT_MANAGER_ACTIVITY});
    let pids = await adb.getPIDsByName(CONTACT_MANAGER_PKG);
    pids.should.have.length.above(0);
    await adb.killProcessByPID(pids[0]);
    await waitForCondition(async () => (await adb.getPIDsByName(CONTACT_MANAGER_PKG)).length === 0, {
      waitMs: 5000,
      intervalMs: 500,
    });
  });
  it('should get device language and country', async function () {
    if (parseInt(apiLevel, 10) >= 23 || process.env.CI) {
      return this.skip();
    }

    ['en', 'fr'].should.contain(await adb.getDeviceSysLanguage());
    ['US', 'EN_US', 'EN', 'FR'].should.contain(await adb.getDeviceSysCountry());
  });
  it('should get device locale', async function () {
    if (parseInt(apiLevel, 10) < 23 || parseInt(apiLevel, 10) > 28) {
      return this.skip();
    }

    await adb.setDeviceSysLocaleViaSettingApp('en', 'US');
    ['us', 'en', 'ca_en', 'en-US'].should.contain(await adb.getDeviceLocale());
  });
  it('should forward the port', async function () {
    await adb.forwardPort(4724, 4724);
  });
  it('should remove forwarded port', async function () {
    await adb.forwardPort(8200, 6790);
    (await adb.adbExec([`forward`, `--list`])).should.contain('tcp:8200');
    await adb.removePortForward(8200);
    (await adb.adbExec([`forward`, `--list`])).should.not.contain('tcp:8200');

  });
  it('should reverse forward the port', async function () {
    await adb.reversePort(4724, 4724);
  });
  it('should remove reverse forwarded port', async function () {
    await adb.reversePort(6790, 8200);
    (await adb.adbExec([`reverse`, `--list`])).should.contain('tcp:6790');
    await adb.removePortReverse(6790);
    (await adb.adbExec([`reverse`, `--list`])).should.not.contain('tcp:6790');

  });
  it('should start logcat from adb', async function () {
    await adb.startLogcat();
    let logs = adb.logcat.getLogs();
    logs.should.have.length.above(0);
    await adb.stopLogcat();
  });
  it('should get model', async function () {
    (await adb.getModel()).should.not.be.null;
  });
  it('should get manufacturer', async function () {
    (await adb.getManufacturer()).should.not.be.null;
  });
  it('should get screen size', async function () {
    (await adb.getScreenSize()).should.not.be.null;
  });
  it('should get screen density', async function () {
    (await adb.getScreenDensity()).should.not.be.null;
  });
  it('should be able to toggle gps location provider', async function () {
    await adb.toggleGPSLocationProvider(true);
    (await adb.getLocationProviders()).should.include('gps');
    await adb.toggleGPSLocationProvider(false);
    (await adb.getLocationProviders()).should.not.include('gps');
  });
  it('should be able to toogle airplane mode', async function () {
    await adb.setAirplaneMode(true);
    (await adb.isAirplaneModeOn()).should.be.true;
    await adb.setAirplaneMode(false);
    (await adb.isAirplaneModeOn()).should.be.false;
  });
  it('should be able to toogle wifi @skip-ci', async function () {
    this.retries(3);

    await adb.setWifiState(true);
    (await adb.isWifiOn()).should.be.true;
    await adb.setWifiState(false);
    (await adb.isWifiOn()).should.be.false;
  });
  it('should be able to turn off animation @skip-ci', async function () {
    await adb.grantPermission('io.appium.settings', 'android.permission.SET_ANIMATION_SCALE');

    await adb.setAnimationState(false);
    (await adb.isAnimationOn()).should.be.false;
  });
  it('should be able to turn on animation @skip-ci', async function () {
    await adb.grantPermission('io.appium.settings', 'android.permission.SET_ANIMATION_SCALE');

    await adb.setAnimationState(true);
    (await adb.isAnimationOn()).should.be.true;
  });
  it('should be able to set device locale via setting app @skip-ci', async function () {
    // Operation not allowed: java.lang.SecurityException: Package io.appium.settings has not requested permission android.permission.CHANGE_CONFIGURATION
    // is shown if the setting apk is not updated.
    await adb.grantPermission('io.appium.settings', 'android.permission.CHANGE_CONFIGURATION');

    await adb.setDeviceSysLocaleViaSettingApp('fr', 'fr');
    (await adb.getDeviceSysLocale()).should.equal('fr-FR');

    await adb.setDeviceSysLocaleViaSettingApp('zh', 'CN', 'Hans');
    (await adb.getDeviceSysLocale()).should.equal('zh-Hans-CN');

    await adb.setDeviceSysLocaleViaSettingApp('en', 'us');
    (await adb.getDeviceSysLocale()).should.equal('en-US');
  });
  describe('app permissions', function () {
    before(async function () {
      let deviceApiLevel = await adb.getApiLevel();
      if (deviceApiLevel < 23) {
        //test should skip if the device API < 23
        return this.skip();
      }
      let isInstalled = await adb.isAppInstalled('io.appium.android.apis');
      if (isInstalled) {
        await adb.uninstallApk('io.appium.android.apis');
      }
    });
    it('should install and grant all permission', async function () {
      let apiDemos = path.resolve(rootDir, 'test',
          'fixtures', 'ApiDemos-debug.apk');
      await adb.install(apiDemos, {timeout: androidInstallTimeout});
      (await adb.isAppInstalled('io.appium.android.apis')).should.be.true;
      await adb.grantAllPermissions('io.appium.android.apis');
      let requestedPermissions = await adb.getReqPermissions('io.appium.android.apis');
      expect(await adb.getGrantedPermissions('io.appium.android.apis')).to.have.members(requestedPermissions);
    });
    it('should revoke permission', async function () {
      await adb.revokePermission('io.appium.android.apis', 'android.permission.RECEIVE_SMS');
      expect(await adb.getGrantedPermissions('io.appium.android.apis')).to.not.have.members(['android.permission.RECEIVE_SMS']);
    });
    it('should grant permission', async function () {
      await adb.grantPermission('io.appium.android.apis', 'android.permission.RECEIVE_SMS');
      expect(await adb.getGrantedPermissions('io.appium.android.apis')).to.include.members(['android.permission.RECEIVE_SMS']);
    });
  });

  describe('push file', function () {
    function getRandomDir () {
      return `/data/local/tmp/test${Math.random()}`;
    }

    let localFile = temp.path({prefix: 'appium', suffix: '.tmp'});
    let tempFile = temp.path({prefix: 'appium', suffix: '.tmp'});
    const stringData = `random string data ${Math.random()}`;
    before(async function () {
      await mkdirp(path.dirname(localFile));
      await mkdirp(path.dirname(tempFile));

      await fs.writeFile(localFile, stringData);
    });
    after(async function () {
      if (await fs.exists(localFile)) {
        await fs.unlink(localFile);
      }
    });
    afterEach(async function () {
      if (await fs.exists(tempFile)) {
        await fs.unlink(tempFile);
      }
    });
    it('should push file to a valid location', async function () {
      let remoteFile = `${getRandomDir()}/remote.txt`;

      await adb.push(localFile, remoteFile);

      // get the file and its contents, to check
      await adb.pull(remoteFile, tempFile);
      let remoteData = await fs.readFile(tempFile);
      remoteData.toString().should.equal(stringData);
    });
    it('should throw error if it cannot write to the remote file', async function () {
      await adb.push(localFile, '/foo/bar/remote.txt').should.be.rejectedWith(/\/foo/);
    });
  });

  describe('bugreport', function () {
    it('should return the report as a raw string', async function () {
      if (process.env.CI) {
        // skip the test on CI, since it takes a lot of time
        return this.skip;
      }
      const BUG_REPORT_TIMEOUT = 2 * 60 * 1000; // 2 minutes
      this.timeout(BUG_REPORT_TIMEOUT);
      (await adb.bugreport()).should.be.a('string');
    });
  });

  describe('features', function () {
    it('should return the features as a list', async function () {
      _.isArray(await adb.listFeatures()).should.be.true;
    });
  });

  describe('isStreamedInstallSupported', function () {
    it('should return boolean value', async function () {
      _.isBoolean(await adb.isStreamedInstallSupported()).should.be.true;
    });
  });

  describe('isIncrementalInstallSupported', function () {
    it('should return boolean value', async function () {
      _.isBoolean(await adb.isIncrementalInstallSupported()).should.be.true;
    });
  });

  describe('addToDeviceIdleWhitelist', function () {
    it('should add package to the whitelist', async function () {
      await adb.install(CONTACT_MANAGER_PATH, {timeout: androidInstallTimeout});
      if (await adb.addToDeviceIdleWhitelist(CONTACT_MANAGER_PKG)) {
        const pkgList = await adb.getDeviceIdleWhitelist();
        pkgList.some((item) => item.includes(CONTACT_MANAGER_PKG)).should.be.true;
      }
    });
  });
});
