/*****************************************************************************
 * Open MCT, Copyright (c) 2014-2024, United States Government
 * as represented by the Administrator of the National Aeronautics and Space
 * Administration. All rights reserved.
 *
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 * Open MCT includes source code licensed under additional open source
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 * this source code distribution or the Licensing information page available
 * at runtime from the About dialog for additional information.
 *****************************************************************************/
import fs from 'fs';

import {
  createDomainObjectWithDefaults,
  createExampleTelemetryObject,
  createNotification,
  createPlanFromJSON,
  createStableStateTelemetry,
  expandEntireTree,
  getCanvasPixels,
  linkParameterToObject,
  navigateToObjectWithFixedTimeBounds,
  navigateToObjectWithRealTime,
  setEndOffset,
  setFixedIndependentTimeConductorBounds,
  setFixedTimeMode,
  setRealTimeMode,
  setStartOffset,
  setTimeConductorBounds,
  waitForPlotsToRender
} from '../../appActions.js';
import { assertPlanActivities, setBoundsToSpanAllActivities } from '../../helper/planningUtils.js';
import { expect, test } from '../../pluginFixtures.js';

test.describe('AppActions @framework', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('./', { waitUntil: 'domcontentloaded' });
  });
  test('createDomainObjectsWithDefaults', async ({ page }) => {
    const e2eFolder = await createDomainObjectWithDefaults(page, {
      type: 'Folder',
      name: 'e2e folder'
    });

    await test.step('Create multiple flat objects in a row', async () => {
      const timer1 = await createDomainObjectWithDefaults(page, {
        type: 'Timer',
        name: 'Timer Foo',
        parent: e2eFolder.uuid
      });
      const timer2 = await createDomainObjectWithDefaults(page, {
        type: 'Timer',
        name: 'Timer Bar',
        parent: e2eFolder.uuid
      });
      const timer3 = await createDomainObjectWithDefaults(page, {
        type: 'Timer',
        name: 'Timer Baz',
        parent: e2eFolder.uuid
      });

      await page.goto(timer1.url);
      await expect(page.locator('.l-browse-bar__object-name')).toHaveText(timer1.name);
      await page.goto(timer2.url);
      await expect(page.locator('.l-browse-bar__object-name')).toHaveText(timer2.name);
      await page.goto(timer3.url);
      await expect(page.locator('.l-browse-bar__object-name')).toHaveText(timer3.name);
    });

    await test.step('Create multiple nested objects in a row', async () => {
      const folder1 = await createDomainObjectWithDefaults(page, {
        type: 'Folder',
        name: 'Folder Foo',
        parent: e2eFolder.uuid
      });
      const folder2 = await createDomainObjectWithDefaults(page, {
        type: 'Folder',
        name: 'Folder Bar',
        parent: folder1.uuid
      });
      const folder3 = await createDomainObjectWithDefaults(page, {
        type: 'Folder',
        name: 'Folder Baz',
        parent: folder2.uuid
      });
      await page.goto(folder1.url);
      await expect(page.locator('.l-browse-bar__object-name')).toHaveText(folder1.name);
      await page.goto(folder2.url);
      await expect(page.locator('.l-browse-bar__object-name')).toHaveText(folder2.name);
      await page.goto(folder3.url);
      await expect(page.locator('.l-browse-bar__object-name')).toHaveText(folder3.name);

      expect(folder1.url).toBe(`${e2eFolder.url}/${folder1.uuid}`);
      expect(folder2.url).toBe(`${e2eFolder.url}/${folder1.uuid}/${folder2.uuid}`);
      expect(folder3.url).toBe(`${e2eFolder.url}/${folder1.uuid}/${folder2.uuid}/${folder3.uuid}`);
    });
  });
  test('createExampleTelemetryObject', async ({ page }) => {
    const gauge = await createDomainObjectWithDefaults(page, {
      type: 'Gauge',
      name: 'Gauge with no data'
    });

    const swgWithParent = await createExampleTelemetryObject(page, gauge.uuid);

    await page.goto(swgWithParent.url);
    await expect(page.locator('.l-browse-bar__object-name')).toHaveText(swgWithParent.name);
    await page.getByLabel('More actions').click();
    await page.getByLabel('Edit Properties...').click();

    // Check Default values of created object
    await expect(page.getByLabel('Title', { exact: true })).toHaveValue('VIPER Rover Heading');
    await expect(page.getByRole('spinbutton', { name: 'Period' })).toHaveValue('10');
    await expect(page.getByRole('spinbutton', { name: 'Amplitude' })).toHaveValue('1');
    await expect(page.getByRole('spinbutton', { name: 'Offset' })).toHaveValue('0');
    await expect(page.getByRole('spinbutton', { name: 'Data Rate (hz)' })).toHaveValue('1');
    await expect(page.getByRole('spinbutton', { name: 'Phase (radians)' })).toHaveValue('0');
    await expect(page.getByRole('spinbutton', { name: 'Randomness' })).toHaveValue('0');
    await expect(page.getByRole('spinbutton', { name: 'Loading Delay (ms)' })).toHaveValue('0');

    await page.getByLabel('Cancel').click();

    const swgWithoutParent = await createExampleTelemetryObject(page);

    await page.getByLabel('Show selected item in tree').click();

    expect(swgWithParent.url).toBe(`${gauge.url}/${swgWithParent.uuid}`);
    expect(swgWithoutParent.url).toBe(`./#/browse/mine/${swgWithoutParent.uuid}`);
  });
  test('createNotification', async ({ page }) => {
    await createNotification(page, {
      message: 'Test info notification',
      severity: 'info'
    });
    await expect(page.locator('.c-message-banner__message')).toHaveText('Test info notification');
    await expect(page.locator('.c-message-banner')).toHaveClass(/info/);
    await page.locator('[aria-label="Dismiss"]').click();
    await createNotification(page, {
      message: 'Test alert notification',
      severity: 'alert'
    });
    await expect(page.locator('.c-message-banner__message')).toHaveText('Test alert notification');
    await expect(page.locator('.c-message-banner')).toHaveClass(/alert/);
    await page.locator('[aria-label="Dismiss"]').click();
    await createNotification(page, {
      message: 'Test error notification',
      severity: 'error'
    });
    await expect(page.locator('.c-message-banner__message')).toHaveText('Test error notification');
    await expect(page.locator('.c-message-banner')).toHaveClass(/error/);
    await page.locator('[aria-label="Dismiss"]').click();
  });
  test('createPlanFromJSON', async ({ page }) => {
    const examplePlanSmall1 = JSON.parse(
      fs.readFileSync(
        new URL('../../test-data/examplePlans/ExamplePlan_Small1.json', import.meta.url)
      )
    );
    const plan = await createPlanFromJSON(page, {
      name: 'Test Plan',
      json: examplePlanSmall1
    });
    await setBoundsToSpanAllActivities(page, examplePlanSmall1, plan.url);
    await assertPlanActivities(page, examplePlanSmall1, plan.url);
  });
  test('expandEntireTree', async ({ page }) => {
    const rootFolder = await createDomainObjectWithDefaults(page, {
      type: 'Folder'
    });
    const folder1 = await createDomainObjectWithDefaults(page, {
      type: 'Folder',
      parent: rootFolder.uuid
    });

    await createDomainObjectWithDefaults(page, {
      type: 'Clock',
      parent: folder1.uuid
    });
    const folder2 = await createDomainObjectWithDefaults(page, {
      type: 'Folder',
      parent: folder1.uuid
    });
    await createDomainObjectWithDefaults(page, {
      type: 'Folder',
      parent: folder1.uuid
    });
    await createDomainObjectWithDefaults(page, {
      type: 'Display Layout',
      parent: folder2.uuid
    });
    await createDomainObjectWithDefaults(page, {
      type: 'Folder',
      parent: folder2.uuid
    });

    await page.goto('./#/browse/mine');
    await expandEntireTree(page);
    const treePane = page.getByRole('tree', {
      name: 'Main Tree'
    });
    const treePaneCollapsedItems = treePane.getByRole('treeitem', { expanded: false });
    await expect(treePaneCollapsedItems).toHaveCount(0);

    await page.goto('./#/browse/mine');
    //Click the Create button
    await page.getByRole('button', { name: 'Create' }).click();

    // Click the object specified by 'type'
    await page.getByRole('menuitem', { name: 'Clock' }).click();
    await expandEntireTree(page, 'Create Modal Tree');
    const locatorTree = page.getByRole('tree', {
      name: 'Create Modal Tree'
    });
    const locatorTreeCollapsedItems = locatorTree.locator('role=treeitem[expanded=false]');
    await expect(locatorTreeCollapsedItems).toHaveCount(0);
  });
  test('getCanvasPixels', async ({ page }) => {
    let overlayPlot = await createDomainObjectWithDefaults(page, {
      type: 'Overlay Plot'
    });

    await createExampleTelemetryObject(page, overlayPlot.uuid);

    await page.goto(overlayPlot.url);
    //Get pixel data from Canvas
    const plotPixels = await getCanvasPixels(page, 'canvas');
    const plotPixelSize = plotPixels.length;
    expect(plotPixelSize).toBeGreaterThan(0);
  });
  test('navigateToObjectWithFixedTimeBounds', async ({ page }) => {
    const exampleTelemetry = await createExampleTelemetryObject(page);
    //Navigate without explicit bounds
    await navigateToObjectWithFixedTimeBounds(page, exampleTelemetry.url);
    await expect(page.getByLabel('Start bounds:')).toBeVisible();
    await expect(page.getByLabel('End bounds:')).toBeVisible();
    //Navigate with explicit bounds
    await navigateToObjectWithFixedTimeBounds(
      page,
      exampleTelemetry.url,
      1693592063607,
      1693593893607
    );
    await expect(page.getByLabel('Start bounds: 2023-09-01 18:')).toBeVisible();
    await expect(page.getByLabel('End bounds: 2023-09-01 18:44:')).toBeVisible();
  });
  test('navigateToObjectWithRealTime', async ({ page }) => {
    const exampleTelemetry = await createExampleTelemetryObject(page);
    //Navigate without explicit bounds
    await navigateToObjectWithRealTime(page, exampleTelemetry.url);
    await expect(page.getByLabel('Start offset:')).toBeVisible();
    await expect(page.getByLabel('End offset: 00:00:')).toBeVisible();
    //Navigate with explicit bounds
    await navigateToObjectWithRealTime(page, exampleTelemetry.url, 1693592063607, 1693593893607);
    await expect(page.getByLabel('Start offset: 18:14:')).toBeVisible();
    await expect(page.getByLabel('End offset: 18:44:')).toBeVisible();
  });
  test('setTimeConductorMode', async ({ page }) => {
    await test.step('setFixedTimeMode', async () => {
      await setFixedTimeMode(page);
      await expect(page.getByLabel('Start bounds:')).toBeVisible();
      await expect(page.getByLabel('End bounds:')).toBeVisible();
    });
    await test.step('setTimeConductorBounds', async () => {
      await setTimeConductorBounds(page, {
        startDate: '2024-01-01',
        endDate: '2024-01-02',
        startTime: '00:00:00',
        endTime: '23:59:59'
      });
      await expect(page.getByLabel('Start bounds: 2024-01-01 00:00:00')).toBeVisible();
      await expect(page.getByLabel('End bounds: 2024-01-02 23:59:59')).toBeVisible();
    });
    await test.step('setRealTimeMode', async () => {
      await setRealTimeMode(page);
      await expect(page.getByLabel('Start offset')).toBeVisible();
      await expect(page.getByLabel('End offset')).toBeVisible();
    });
    await test.step('setStartOffset', async () => {
      await setStartOffset(page, {
        startHours: '04',
        startMins: '20',
        startSecs: '22'
      });
      await expect(page.getByLabel('Start offset: 04:20:22')).toBeVisible();
    });
    await test.step('setEndOffset', async () => {
      await setEndOffset(page, {
        endHours: '04',
        endMins: '20',
        endSecs: '22'
      });
      await expect(page.getByLabel('End offset: 04:20:22')).toBeVisible();
    });
  });
  test('setFixedIndependentTimeConductorBounds', async ({ page }) => {
    // Create a Display Layout
    const displayLayout = await createDomainObjectWithDefaults(page, {
      type: 'Display Layout'
    });
    await createDomainObjectWithDefaults(page, {
      type: 'Example Imagery',
      parent: displayLayout.uuid
    });

    const startDate = '2021-12-30 01:01:00.000Z';
    const endDate = '2021-12-30 01:11:00.000Z';
    await setFixedIndependentTimeConductorBounds(page, { start: startDate, end: endDate });

    // check image date
    await expect(page.getByText('2021-12-30 01:11:00.000Z').first()).toBeVisible();

    // flip it off
    await page.getByRole('switch').click();
    // timestamp shouldn't be in the past anymore
    await expect(page.getByText('2021-12-30 01:11:00.000Z')).toBeHidden();
  });
  test.fail('waitForPlotsToRender', async ({ page }) => {
    // Create a SWG
    await createDomainObjectWithDefaults(page, {
      type: 'Sine Wave Generator'
    });
    // Edit the SWG
    await page.getByLabel('More actions').click();
    await page.getByLabel('Edit Properties...').click();
    // Set loading delay to 10 seconds
    await page.getByLabel('Loading Delay (ms)', { exact: true }).fill('10000');
    await page.getByLabel('Save').click();
    // Reload the page
    await page.reload();
    // Expect this step to fail
    await waitForPlotsToRender(page, { timeout: 1000 });
  });
  test('createStableStateTelemetry', async ({ page }) => {
    const stableStateTelemetry = await createStableStateTelemetry(page);
    expect(stableStateTelemetry.name).toBe('Stable State Generator');
    expect(stableStateTelemetry.url).toBe(`./#/browse/mine/${stableStateTelemetry.uuid}`);
    expect(stableStateTelemetry.uuid).toBeDefined();
  });
  test('linkParameterToObject', async ({ page }) => {
    const displayLayout = await createDomainObjectWithDefaults(page, {
      type: 'Display Layout',
      name: 'Test Display Layout'
    });
    const exampleTelemetry = await createExampleTelemetryObject(page);

    await linkParameterToObject(page, exampleTelemetry.name, displayLayout.name);
    await page.goto(displayLayout.url);
    await expect(page.getByRole('main').getByText('Test Display Layout')).toBeVisible();
    await expandEntireTree(page);
    await expect(page.getByLabel('Navigate to VIPER Rover').first()).toBeVisible();
  });
});
