import { expect } from 'chai';

import { ReversibleOverridesMap } from '../../overrides/maps';

import { overrides } from './overrides';

const CELL_MAGIC_EXISTS = `%%MAGIC
some text
`;

const CELL_MAGIC_WITH_DOCSTRINGS = `%%MAGIC
text
"""a docstring"""
'''a less common docstring'''
'single quotes'
"double quotes"
text
`;

const ESCAPED_TEXT_WITH_DOCSTRINGS = `text
\\"\\"\\"a docstring\\"\\"\\"
'''a less common docstring'''
'single quotes'
"double quotes"
text
`;

const NO_CELL_MAGIC = `%MAGIC
some text
%%MAGIC
some text
`;

const LINE_MAGIC_WITH_SPACE = `%MAGIC line = dd`;

describe('Default IPython overrides', () => {
  describe('IPython cell magics', () => {
    let cell_magics_map = new ReversibleOverridesMap(
      overrides.filter(override => override.scope == 'cell')
    );
    it('overrides cell magics', () => {
      let override = cell_magics_map.override_for(CELL_MAGIC_EXISTS)!;
      expect(override).to.equal(
        'get_ipython().run_cell_magic("MAGIC", "", """some text\n""")'
      );

      let reverse = cell_magics_map.reverse.override_for(override);
      expect(reverse).to.equal(CELL_MAGIC_EXISTS);
    });

    it('works for empty cells', () => {
      // those are not correct syntax, but will happen when users are in the process of writing
      const cell_magic_with_args = '%%MAGIC\n';
      let override = cell_magics_map.override_for(cell_magic_with_args)!;
      expect(override).to.equal(
        'get_ipython().run_cell_magic("MAGIC", "", """""")'
      );

      let reverse = cell_magics_map.reverse.override_for(override);
      expect(reverse).to.equal(cell_magic_with_args);
    });

    it('escapes arguments in the first line', () => {
      const cell_magic_with_args = '%%MAGIC "arg in quotes"\ntext';
      let override = cell_magics_map.override_for(cell_magic_with_args)!;
      expect(override).to.equal(
        'get_ipython().run_cell_magic("MAGIC", " \\"arg in quotes\\"", """text""")'
      );

      let reverse = cell_magics_map.reverse.override_for(override);
      expect(reverse).to.equal(cell_magic_with_args);
    });

    it('escapes docstrings properly', () => {
      let override = cell_magics_map.override_for(CELL_MAGIC_WITH_DOCSTRINGS)!;
      expect(override).to.equal(
        'get_ipython().run_cell_magic("MAGIC", "", """' +
          ESCAPED_TEXT_WITH_DOCSTRINGS +
          '""")'
      );

      let reverse = cell_magics_map.reverse.override_for(override);
      expect(reverse).to.equal(CELL_MAGIC_WITH_DOCSTRINGS);
    });

    it('does not override cell-magic-like constructs', () => {
      let override = cell_magics_map.override_for(NO_CELL_MAGIC);
      expect(override).to.equal(null);

      override = cell_magics_map.override_for(LINE_MAGIC_WITH_SPACE);
      expect(override).to.equal(null);
    });
  });

  describe('IPython line magics', () => {
    let line_magics_map = new ReversibleOverridesMap(
      overrides.filter(override => override.scope == 'line')
    );
    it('overrides line magics', () => {
      let override = line_magics_map.override_for(LINE_MAGIC_WITH_SPACE)!;
      expect(override).to.equal(
        'get_ipython().run_line_magic("MAGIC", " line = dd")'
      );

      let reverse = line_magics_map.reverse.override_for(override);
      expect(reverse).to.equal(LINE_MAGIC_WITH_SPACE);
    });

    it('overrides x =%ls and x = %ls', () => {
      // this is a corner-case as described in
      // https://github.com/jupyter-lsp/jupyterlab-lsp/issues/281#issuecomment-645286076
      let override = line_magics_map.override_for('x =%ls');
      expect(override).to.equal('x =get_ipython().run_line_magic("ls", "")');

      override = line_magics_map.override_for('x = %ls');
      expect(override).to.equal('x = get_ipython().run_line_magic("ls", "")');
    });

    it('does not override line-magic-like constructs', () => {
      let override = line_magics_map.override_for('list("%")');
      expect(override).to.equal(null);

      override = line_magics_map.override_for('list(" %test")');
      expect(override).to.equal(null);
    });

    it('does not override modulo operators', () => {
      let override = line_magics_map.override_for('3 % 2');
      expect(override).to.equal(null);

      override = line_magics_map.override_for('3%2');
      expect(override).to.equal(null);
    });

    it('escapes arguments', () => {
      let line_magic_with_args = '%MAGIC "arg"';
      let override = line_magics_map.override_for(line_magic_with_args)!;
      expect(override).to.equal(
        'get_ipython().run_line_magic("MAGIC", " \\"arg\\"")'
      );

      let reverse = line_magics_map.reverse.override_for(override);
      expect(reverse).to.equal(line_magic_with_args);

      line_magic_with_args = '%MAGIC "arg\\"';
      override = line_magics_map.override_for(line_magic_with_args)!;
      expect(override).to.equal(
        'get_ipython().run_line_magic("MAGIC", " \\"arg\\\\\\"")'
      );

      reverse = line_magics_map.reverse.override_for(override);
      expect(reverse).to.equal(line_magic_with_args);
    });

    it('overrides shell commands', () => {
      let override = line_magics_map.override_for('!ls -o')!;
      expect(override).to.equal('get_ipython().getoutput("ls -o")')!;

      let reverse = line_magics_map.reverse.override_for(override);
      expect(reverse).to.equal('!ls -o');
    });

    it('overrides shell commands assignments: x =!ls and x = !ls', () => {
      let override = line_magics_map.override_for('x =!ls');
      expect(override).to.equal('x =get_ipython().getoutput("ls")');

      override = line_magics_map.override_for('x = !ls');
      expect(override).to.equal('x = get_ipython().getoutput("ls")');
    });

    it('does not override shell-like constructs', () => {
      let override = line_magics_map.override_for('"!ls"');
      expect(override).to.equal(null);
    });

    it('does not override != comparisons', () => {
      let override = line_magics_map.override_for('1 != None');
      expect(override).to.equal(null);
    });
  });

  describe('IPython help line magics', () => {
    let line_magics_map = new ReversibleOverridesMap(
      overrides.filter(override => override.scope == 'line')
    );

    it('overrides pinfo', () => {
      let override = line_magics_map.override_for('?int')!;
      expect(override).to.equal("get_ipython().run_line_magic('pinfo', 'int')");

      let reverse = line_magics_map.reverse.override_for(override);
      expect(reverse).to.equal('?int');

      override = line_magics_map.override_for('int?')!;
      expect(override).to.equal(
        "get_ipython().run_line_magic('pinfo',  'int')"
      );

      reverse = line_magics_map.reverse.override_for(override);
      expect(reverse).to.equal('int?');
    });

    it('overrides pinfo2', () => {
      let override = line_magics_map.override_for('??int')!;
      expect(override).to.equal(
        "get_ipython().run_line_magic('pinfo2', 'int')"
      );

      let reverse = line_magics_map.reverse.override_for(override);
      expect(reverse).to.equal('??int');

      override = line_magics_map.override_for('int??')!;
      expect(override).to.equal(
        "get_ipython().run_line_magic('pinfo2',  'int')"
      );

      reverse = line_magics_map.reverse.override_for(override);
      expect(reverse).to.equal('int??');

      override = line_magics_map.override_for('some_func??')!;
      expect(override).to.equal(
        "get_ipython().run_line_magic('pinfo2',  'some_func')"
      );
      reverse = line_magics_map.reverse.override_for(override);
      expect(reverse).to.equal('some_func??');
    });

    it('does not override standalone question marks', () => {
      let override = line_magics_map.override_for("'q?'");
      expect(override).to.equal(null);

      override = line_magics_map.override_for('#?');
      expect(override).to.equal(null);

      override = line_magics_map.override_for("?q'");
      expect(override).to.equal(null);

      override = line_magics_map.override_for("?#'");
      expect(override).to.equal(null);
    });
  });
});
