/*!
  Copyright 2013 Lovell Fuller and others.
  SPDX-License-Identifier: Apache-2.0
*/

const { after, before, describe, it } = require('node:test');
const assert = require('node:assert');
const fs = require('node:fs');
const semver = require('semver');
const libvips = require('../../lib/libvips');

const originalPlatform = process.platform;

const setPlatform = (platform) => {
  Object.defineProperty(process, 'platform', { value: platform });
};

const restorePlatform = () => {
  setPlatform(originalPlatform);
};

describe('libvips binaries', () => {
  describe('Windows platform', () => {
    before(() => { setPlatform('win32'); });
    after(restorePlatform);

    it('pkgConfigPath returns empty string', () => {
      assert.strictEqual('', libvips.pkgConfigPath());
    });
    it('globalLibvipsVersion returns empty string', () => {
      assert.strictEqual('', libvips.globalLibvipsVersion());
    });
    it('globalLibvipsVersion is always false', () => {
      assert.strictEqual(false, libvips.useGlobalLibvips());
    });
  });

  describe('non-Windows platforms', () => {
    before(() => { setPlatform('linux'); });
    after(restorePlatform);

    it('pkgConfigPath returns a string', () => {
      const pkgConfigPath = libvips.pkgConfigPath();
      assert.strictEqual('string', typeof pkgConfigPath);
    });
    it('globalLibvipsVersion returns a string', () => {
      const globalLibvipsVersion = libvips.globalLibvipsVersion();
      assert.strictEqual('string', typeof globalLibvipsVersion);
    });
    it('globalLibvipsVersion returns a boolean', () => {
      const useGlobalLibvips = libvips.useGlobalLibvips();
      assert.strictEqual('boolean', typeof useGlobalLibvips);
    });
  });

  describe('platform agnostic', () => {
    it('minimumLibvipsVersion returns a valid semver', () => {
      const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
      assert.strictEqual('string', typeof minimumLibvipsVersion);
      assert.notStrictEqual(null, semver.valid(minimumLibvipsVersion));
    });
    it('useGlobalLibvips can be ignored via an env var', () => {
      process.env.SHARP_IGNORE_GLOBAL_LIBVIPS = 1;

      const useGlobalLibvips = libvips.useGlobalLibvips();
      assert.strictEqual(false, useGlobalLibvips);

      delete process.env.SHARP_IGNORE_GLOBAL_LIBVIPS;
    });
    it('useGlobalLibvips can be forced via an env var', () => {
      process.env.SHARP_FORCE_GLOBAL_LIBVIPS = 1;

      const useGlobalLibvips = libvips.useGlobalLibvips();
      assert.strictEqual(true, useGlobalLibvips);

      let logged = false;
      const logger = (message) => {
        assert.strictEqual(message, 'Detected SHARP_FORCE_GLOBAL_LIBVIPS, skipping search for globally-installed libvips');
        logged = true;
      };
      const useGlobalLibvipsWithLogger = libvips.useGlobalLibvips(logger);
      assert.strictEqual(true, useGlobalLibvipsWithLogger);
      assert.strictEqual(true, logged);

      delete process.env.SHARP_FORCE_GLOBAL_LIBVIPS;
    });
  });

  describe('Build time platform detection', () => {
    it('Can override platform with npm_config_platform and npm_config_libc', function () {
      process.env.npm_config_platform = 'testplatform';
      process.env.npm_config_libc = 'testlibc';
      const platformArch = libvips.buildPlatformArch();
      if (platformArch === 'wasm32') {
        return this.skip();
      }
      const [platform] = platformArch.split('-');
      assert.strictEqual(platform, 'testplatformtestlibc');
      delete process.env.npm_config_platform;
      delete process.env.npm_config_libc;
    });
    it('Can override arch with npm_config_arch', function () {
      process.env.npm_config_arch = 'test';
      const platformArch = libvips.buildPlatformArch();
      if (platformArch === 'wasm32') {
        return this.skip();
      }
      const [, arch] = platformArch.split('-');
      assert.strictEqual(arch, 'test');
      delete process.env.npm_config_arch;
    });
  });

  describe('Build time directories', () => {
    it('sharp-libvips include', () => {
      const dir = libvips.buildSharpLibvipsIncludeDir();
      if (dir) {
        assert.strictEqual(fs.statSync(dir).isDirectory(), true);
      }
    });
    it('sharp-libvips cplusplus', () => {
      const dir = libvips.buildSharpLibvipsCPlusPlusDir();
      if (dir) {
        assert.strictEqual(fs.statSync(dir).isDirectory(), true);
      }
    });
    it('sharp-libvips lib', () => {
      const dir = libvips.buildSharpLibvipsLibDir();
      if (dir) {
        assert.strictEqual(fs.statSync(dir).isDirectory(), true);
      }
    });
  });

  describe('Runtime detection', () => {
    it('platform', () => {
      const [platform] = libvips.runtimePlatformArch().split('-');
      assert.strict(['darwin', 'freebsd', 'linux', 'linuxmusl', 'win32'].includes(platform));
    });
    it('arch', () => {
      const [, arch] = libvips.runtimePlatformArch().split('-');
      assert.strict(['arm', 'arm64', 'ia32', 'x64', 'ppc64'].includes(arch));
    });
    it('isUnsupportedNodeRuntime', () => {
      assert.strictEqual(libvips.isUnsupportedNodeRuntime(), undefined);
    });
  });

  describe('logger', () => {
    const consoleLog = console.log;
    const consoleError = console.error;

    after(() => {
      console.log = consoleLog;
      console.error = consoleError;
    });

    it('logs an info message', (_t, done) => {
      console.log = (msg) => {
        assert.strictEqual(msg, 'sharp: progress');
        done();
      };
      libvips.log('progress');
    });

    it('logs an error message', (_t, done) => {
      console.error = (msg) => {
        assert.strictEqual(msg, 'sharp: Installation error: problem');
        done();
      };
      libvips.log(new Error('problem'));
    });
  });

  describe('yarn locator hash', () => {
    it('known platform', () => {
      const cc = process.env.CC;
      delete process.env.CC;
      process.env.npm_config_platform = 'linux';
      process.env.npm_config_arch = 's390x';
      process.env.npm_config_libc = '';
      const locatorHash = libvips.yarnLocator();
      assert.strictEqual(locatorHash, '8cdba194cb');
      delete process.env.npm_config_platform;
      delete process.env.npm_config_arch;
      delete process.env.npm_config_libc;
      process.env.CC = cc;
    });
    it('unknown platform', () => {
      process.env.npm_config_platform = 'unknown-platform';
      const locatorHash = libvips.yarnLocator();
      assert.strictEqual(locatorHash, '');
      delete process.env.npm_config_platform;
    });
  });
});
