/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 */

import {parse} from '@babel/parser';
import {transformFromAstSync} from '@babel/core';
import {default as generate} from '@babel/generator';
const flipperRequires = require('../plugin-flipper-requires');

const babelOptions = {
  ast: true,
  plugins: [flipperRequires],
  filename: 'index.js',
};

const babelOptionsPlugin = {
  ...babelOptions,
  root: 'plugins/randomPlugin/',
  filename: 'plugins/randomPlugin/index.js',
};

test('transform react requires to global object', () => {
  const src = 'require("react")';
  const ast = parse(src);
  const transformed = transformFromAstSync(ast, src, babelOptionsPlugin)!.ast;
  const {code} = generate(transformed!);
  expect(code).toBe('global.React;');
});

test('transform react-dom requires to global object', () => {
  const src = 'require("react-dom")';
  const ast = parse(src);
  const transformed = transformFromAstSync(ast, src, babelOptionsPlugin)!.ast;
  const {code} = generate(transformed!);
  expect(code).toBe('global.ReactDOM;');
});

test('transform react-dom/client requires to global object', () => {
  const src = 'require("react-dom/client")';
  const ast = parse(src);
  const transformed = transformFromAstSync(ast, src, babelOptionsPlugin)!.ast;
  const {code} = generate(transformed!);
  expect(code).toBe('global.ReactDOMClient;');
});

test('transform flipper requires to global object', () => {
  const src = 'require("flipper")';
  const ast = parse(src);
  const transformed = transformFromAstSync(ast, src, babelOptionsPlugin)!.ast;
  const {code} = generate(transformed!);
  expect(code).toBe('global.Flipper;');
});

test('do NOT transform flipper requires outside of plugins folder', () => {
  const src = 'require("flipper");';
  const ast = parse(src);
  const transformed = transformFromAstSync(ast, src, babelOptions)!.ast;
  const {code} = generate(transformed!);
  expect(code).toBe('require("flipper");');
});

test('transform React identifier to global.React', () => {
  const src = 'React;';
  const ast = parse(src);
  const transformed = transformFromAstSync(ast, src, babelOptionsPlugin)!.ast;
  const {code} = generate(transformed!);
  expect(code).toBe('global.React;');
});

test('do NOT transform React identifier to global.React outside of plugins folder', () => {
  const src = 'React;';
  const ast = parse(src);
  const transformed = transformFromAstSync(ast, src, babelOptions)!.ast;
  const {code} = generate(transformed!);
  expect(code).toBe('React;');
});

test('do NOT transform local React namespace import to global.React', () => {
  const src = `import * as React from 'react';`;
  const ast = parse(src, {sourceType: 'module'});
  const transformed = transformFromAstSync(ast, src, babelOptions)!.ast;
  const {code} = generate(transformed!);
  expect(code).toMatchInlineSnapshot(`"import * as React from 'react';"`);
});

test('throw error when requiring outside the plugin', () => {
  const src = 'require("../test.js")';
  const ast = parse(src);
  expect(() => {
    transformFromAstSync(ast, src, babelOptionsPlugin);
  }).toThrow();
});

test('allow requiring from parent folder as long as we stay in plugin folder', () => {
  const src = 'require("../test.js")';
  const ast = parse(src);
  const transformed = transformFromAstSync(ast, src, {
    ...babelOptions,
    root: '/path/to/plugin',
    filename: '/path/to/plugin/subfolder/index.js',
  })!.ast;
  const {code} = generate(transformed!);
  expect(code).toBe('require("../test.js");');
});
