import {assert, driver} from 'mocha-webdriver';
import * as gu from 'test/nbrowser/gristUtils';
import {cleanupExtraWindows, setupTestSuite} from 'test/nbrowser/testUtils';

describe('CursorSaving', function() {
  this.timeout(20000);
  cleanupExtraWindows();
  const cleanup = setupTestSuite();
  const clipboard = gu.getLockableClipboard();
  afterEach(() => gu.checkForErrors());

  describe('WithRefLists', function() {
    before(async function() {
      const session = await gu.session().login();
      await session.tempDoc(cleanup, "CursorWithRefLists1.grist");
    });

    it('should remember positions when record is linked from multiple source records', async function() {
      // Select Tag 'a' (row 1), and Item 'Apples' (row 1), which has tags 'b' and 'a'.
      await clickAndCheck({section: 'Tags', rowNum: 1, col: 0}, 'a');
      await clickAndCheck({section: 'Items', rowNum: 1, col: 0}, 'Apple');

      await gu.reloadDoc();
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 1, col: 0});
      assert.deepEqual(await gu.getCursorPosition('Items'), {rowNum: 1, col: 0});
      assert.equal(await gu.getCardCell('Name', 'Items Card').getText(), 'Apple');

      // Now select a different Tag, but the same Item.
      await clickAndCheck({section: 'Tags', rowNum: 2, col: 0}, 'b');
      await clickAndCheck({section: 'Items', rowNum: 1, col: 0}, 'Apple');

      await gu.reloadDoc();
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 2, col: 0});
      assert.deepEqual(await gu.getCursorPosition('Items'), {rowNum: 1, col: 0});
      assert.equal(await gu.getCardCell('Name', 'Items Card').getText(), 'Apple');

      // Try the third section.
      await clickAndCheck({section: 'Items', rowNum: 3, col: 0}, 'Orange');
      await clickAndCheckCard({section: 'ITEMS Card', col: 'Name', rowNum: 1}, 'Orange');

      await gu.reloadDoc();
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 2, col: 0});
      assert.deepEqual(await gu.getCursorPosition('Items'), {rowNum: 3, col: 0});
      assert.equal(await gu.getActiveSectionTitle(), 'ITEMS Card');
      assert.equal(await gu.getCardCell('Name', 'ITEMS Card').getText(), 'Orange');

      // Try getting to the same card via different selections.
      await clickAndCheck({section: 'Tags', rowNum: 1, col: 0}, 'a');
      await clickAndCheck({section: 'Items', rowNum: 2, col: 0}, 'Orange');
      await clickAndCheckCard({section: 'ITEMS Card', col: 'Name', rowNum: 1}, 'Orange');

      await gu.reloadDoc();
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 1, col: 0});
      assert.deepEqual(await gu.getCursorPosition('Items'), {rowNum: 2, col: 0});
      assert.equal(await gu.getActiveSectionTitle(), 'ITEMS Card');
      assert.equal(await gu.getCardCell('Name', 'ITEMS Card').getText(), 'Orange');
    });

    it('should remember positions when "new" row is involved', async function() {
      // Try a position when when the parent record is on a "new" row.
      await clickAndCheck({section: 'Tags', rowNum: 2, col: 0}, 'b');
      await clickAndCheck({section: 'Items', rowNum: 4, col: 0}, '');
      await clickAndCheckCard({section: 'ITEMS Card', col: 'Tags', rowNum: 1}, '');

      await gu.reloadDoc();
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 2, col: 0});
      assert.deepEqual(await gu.getCursorPosition('Items'), {rowNum: 4, col: 0});
      assert.equal(await gu.getActiveSectionTitle(), 'ITEMS Card');
      assert.equal(await gu.getCardCell('Tags', 'ITEMS Card').getText(), '');

      // Try a position when when the grandparent parent record is on a "new" row.
      await clickAndCheck({section: 'Tags', rowNum: 4, col: 0}, '');
      assert.match(await gu.getSection('Items').find('.disable_viewpane').getText(), /No row selected/);
      await clickAndCheckCard({section: 'ITEMS Card', col: 'Tags', rowNum: 1}, '');

      await gu.reloadDoc();
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 4, col: 0});
      assert.match(await gu.getSection('Items').find('.disable_viewpane').getText(), /No row selected/);
      assert.equal(await gu.getActiveSectionTitle(), 'ITEMS Card');
      assert.equal(await gu.getCardCell('Tags', 'ITEMS Card').getText(), '');
    });

    it('should create anchor links that preserve row positions in linking sources', async function() {
      await clickAndCheck({section: 'Tags', rowNum: 1, col: 0}, 'a');
      await clickAndCheck({section: 'Items', rowNum: 1, col: 0}, 'Apple');
      await gu.openRowMenu(1);

      const anchorLinks: string[] = [];
      await clipboard.lockAndPerform(async () => { anchorLinks.push(await gu.getAnchor()); });

      // Now select a different Tag, but the same Item.
      await clickAndCheck({section: 'Tags', rowNum: 2, col: 0}, 'b');
      await clickAndCheck({section: 'Items', rowNum: 1, col: 0}, 'Apple');
      await clipboard.lockAndPerform(async () => { anchorLinks.push(await gu.getAnchor()); });

      // Try the third section.
      await clickAndCheck({section: 'Items', rowNum: 3, col: 0}, 'Orange');
      await clickAndCheckCard({section: 'ITEMS Card', col: 'Name', rowNum: 1}, 'Orange');
      await clipboard.lockAndPerform(async () => { anchorLinks.push(await gu.getAnchor()); });

      // A different way to get to the same value in third section.
      await clickAndCheck({section: 'Tags', rowNum: 1, col: 0}, 'a');
      await clickAndCheck({section: 'Items', rowNum: 2, col: 0}, 'Orange');
      await gu.getCardCell('Name', 'ITEMS Card').click();
      await clipboard.lockAndPerform(async () => { anchorLinks.push(await gu.getAnchor()); });

      // Now go through the anchor links, and make sure each gets us to the expected point.
      await driver.get(anchorLinks[0]);
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 1, col: 0});
      assert.deepEqual(await gu.getCursorPosition('Items'), {rowNum: 1, col: 0});
      assert.equal(await gu.getCardCell('Name', 'Items Card').getText(), 'Apple');

      await driver.get(anchorLinks[1]);
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 2, col: 0});
      assert.deepEqual(await gu.getCursorPosition('Items'), {rowNum: 1, col: 0});
      assert.equal(await gu.getCardCell('Name', 'Items Card').getText(), 'Apple');

      await driver.get(anchorLinks[2]);
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 2, col: 0});
      assert.deepEqual(await gu.getCursorPosition('Items'), {rowNum: 3, col: 0});
      assert.equal(await gu.getActiveSectionTitle(), 'ITEMS Card');
      assert.equal(await gu.getCardCell('Name', 'ITEMS Card').getText(), 'Orange');

      await driver.get(anchorLinks[3]);
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 1, col: 0});
      assert.deepEqual(await gu.getCursorPosition('Items'), {rowNum: 2, col: 0});
      assert.equal(await gu.getActiveSectionTitle(), 'ITEMS Card');
      assert.equal(await gu.getCardCell('Name', 'ITEMS Card').getText(), 'Orange');
    });

    it('should handle anchor links when "new" row is involved', async function() {
      const anchorLinks: string[] = [];

      // Try a position when when the parent record is on a "new" row.
      await clickAndCheck({section: 'Tags', rowNum: 2, col: 0}, 'b');
      await clickAndCheck({section: 'Items', rowNum: 4, col: 0}, '');
      await clickAndCheckCard({section: 'ITEMS Card', col: 'Tags', rowNum: 1}, '');

      await clipboard.lockAndPerform(async () => { anchorLinks.push(await gu.getAnchor()); });

      // Try a position when when the grandparent parent record is on a "new" row.
      await clickAndCheck({section: 'Tags', rowNum: 4, col: 0}, '');
      assert.match(await gu.getSection('Items').find('.disable_viewpane').getText(), /No row selected/);
      await clickAndCheckCard({section: 'ITEMS Card', col: 'Tags', rowNum: 1}, '');

      await clipboard.lockAndPerform(async () => { anchorLinks.push(await gu.getAnchor()); });

      await driver.get(anchorLinks[0]);
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 2, col: 0});
      assert.deepEqual(await gu.getCursorPosition('Items'), {rowNum: 4, col: 0});
      assert.equal(await gu.getActiveSectionTitle(), 'ITEMS Card');
      assert.equal(await gu.getCardCell('Tags', 'ITEMS Card').getText(), '');

      await driver.get(anchorLinks[1]);
      assert.deepEqual(await gu.getCursorPosition('Tags'), {rowNum: 4, col: 0});
      assert.match(await gu.getSection('Items').find('.disable_viewpane').getText(), /No row selected/);
      assert.equal(await gu.getActiveSectionTitle(), 'ITEMS Card');
      assert.equal(await gu.getCardCell('Tags', 'ITEMS Card').getText(), '');
    });
  });

  describe('WithRefs', function() {
    // This is a similar test to the above, but without RefLists. In particular it checks that
    // when a cursor is in the "new" row, enough is remembered to restore positions.

    before(async function() {
      const session = await gu.session().login();
      const doc = await session.tempDoc(cleanup, "World.grist", {load: false});
      await session.loadDoc(`/doc/${doc.id}/p/5`, {wait: true});
    });

    it('should remember row positions in linked sections', async function() {
      // Select a country and a city within it.
      await clickAndCheck({section: 'Country', rowNum: 2, col: 0}, 'AFG');
      await clickAndCheck({section: 'City', rowNum: 4, col: 1}, 'Balkh');
      await gu.reloadDoc();
      assert.deepEqual(await gu.getCursorPosition('Country'), {rowNum: 2, col: 0});
      assert.deepEqual(await gu.getCursorPosition('City'), {rowNum: 4, col: 1});

      // Now select a country, and the "new" row in the linked City widget.
      await clickAndCheck({section: 'Country', rowNum: 3, col: 0}, 'AGO');
      await clickAndCheck({section: 'City', rowNum: 6, col: 1}, '');
      await gu.reloadDoc();
      assert.deepEqual(await gu.getCursorPosition('Country'), {rowNum: 3, col: 0});
      assert.deepEqual(await gu.getCursorPosition('City'), {rowNum: 6, col: 1});
    });

    it('should create anchor links that preserve row positions in linked sections', async function() {
      const anchorLinks: string[] = [];

      // Select a country and a city within it.
      await clickAndCheck({section: 'Country', rowNum: 2, col: 0}, 'AFG');
      await clickAndCheck({section: 'City', rowNum: 4, col: 1}, 'Balkh');
      await clipboard.lockAndPerform(async () => { anchorLinks.push(await gu.getAnchor()); });

      // Now select a country, and the "new" row in the linked City widget.
      await clickAndCheck({section: 'Country', rowNum: 3, col: 0}, 'AGO');
      await clickAndCheck({section: 'City', rowNum: 6, col: 1}, '');

      await clipboard.lockAndPerform(async () => { anchorLinks.push(await gu.getAnchor()); });

      await driver.get(anchorLinks[0]);
      assert.deepEqual(await gu.getCursorPosition('Country'), {rowNum: 2, col: 0});
      assert.deepEqual(await gu.getCursorPosition('City'), {rowNum: 4, col: 1});

      await driver.get(anchorLinks[1]);
      assert.deepEqual(await gu.getCursorPosition('Country'), {rowNum: 3, col: 0});
      assert.deepEqual(await gu.getCursorPosition('City'), {rowNum: 6, col: 1});
    });
  });
});

async function clickAndCheck(options: gu.ICellSelect, expectedValue: string) {
  const cell = gu.getCell(options);
  await cell.click();
  assert.equal(await cell.getText(), expectedValue);
}

async function clickAndCheckCard(options: gu.ICellSelect, expectedValue: string) {
  const cell = gu.getDetailCell(options);
  await cell.click();
  assert.equal(await cell.getText(), expectedValue);
}