import { thunkTester } from 'test/core/thunk/thunkTester';

import { DataSourceInstanceSettings, ThresholdsMode } from '@grafana/data';
import { defaultDashboard, FieldColorModeId } from '@grafana/schema';
import { browseDashboardsAPI } from 'app/features/browse-dashboards/api/browseDashboardsAPI';
import { getLibraryPanel } from 'app/features/library-panels/state/api';

import { PanelModel } from '../../dashboard/state';
import { LibraryElementDTO } from '../../library-panels/types';
import { DashboardJson } from '../types';
import { validateDashboardJson } from '../utils/validation';

import { getLibraryPanelInputs, importDashboard, processDashboard } from './actions';
import { DataSourceInput, ImportDashboardDTO, initialImportDashboardState, InputType } from './reducers';

jest.mock('app/features/library-panels/state/api');
const mocks = {
  getLibraryPanel: jest.mocked(getLibraryPanel),
};

describe('importDashboard', () => {
  it('Should send data source uid', async () => {
    // note: the actual action returned is more complicated
    // but we don't really care about the return type in this test
    // we're only testing that the correct data is passed to initiate
    const mockAction = jest.fn().mockImplementation(() => ({
      type: 'foo',
    }));
    const importDashboardRtkQueryMock = jest
      .spyOn(browseDashboardsAPI.endpoints.importDashboard, 'initiate')
      .mockImplementation(mockAction);
    const form: ImportDashboardDTO = {
      title: 'Asda',
      uid: '12',
      gnetId: 'asd',
      constants: [],
      dataSources: [
        {
          id: 1,
          uid: 'ds-uid',
          name: 'ds-name',
          type: 'prometheus',
        } as DataSourceInstanceSettings,
      ],
      elements: [],
      folder: {
        uid: '5v6e5VH4z',
        title: 'title',
      },
    };

    await thunkTester({
      importDashboard: {
        ...initialImportDashboardState,
        inputs: {
          dataSources: [
            {
              name: 'ds-name',
              pluginId: 'prometheus',
              type: InputType.DataSource,
            },
          ] as DataSourceInput[],
          constants: [],
          libraryPanels: [],
        },
      },
    })
      .givenThunk(importDashboard)
      .whenThunkIsDispatched(form);

    expect(importDashboardRtkQueryMock).toHaveBeenCalledWith({
      dashboard: {
        title: 'Asda',
        uid: '12',
      },
      folderUid: '5v6e5VH4z',
      inputs: [
        {
          name: 'ds-name',
          pluginId: 'prometheus',
          type: 'datasource',
          value: 'ds-uid',
        },
      ],
      overwrite: true,
    });
  });
});

describe('validateDashboardJson', () => {
  it('Should return true if correct json', async () => {
    const jsonImportCorrectFormat = '{"title": "Correct Format", "tags": ["tag1", "tag2"], "schemaVersion": 36}';
    const validateDashboardJsonCorrectFormat = await validateDashboardJson(jsonImportCorrectFormat);
    expect(validateDashboardJsonCorrectFormat).toBe(true);
  });
  it('Should not return true if nested tags', async () => {
    const jsonImportNestedTags =
      '{"title": "Nested tags","tags": ["tag1", "tag2", ["nestedTag1", "nestedTag2"]],"schemaVersion": 36}';
    const validateDashboardJsonNestedTags = await validateDashboardJson(jsonImportNestedTags);
    expect(validateDashboardJsonNestedTags).toBe('tags expected array of strings');
  });
  it('Should not return true if not an array', async () => {
    const jsonImportNotArray = '{"title": "Not Array","tags": "tag1","schemaVersion":36}';
    const validateDashboardJsonNotArray = await validateDashboardJson(jsonImportNotArray);
    expect(validateDashboardJsonNotArray).toBe('tags expected array');
  });
  it('Should not return true if not an array and is blank string', async () => {
    const jsonImportEmptyTags = '{"schemaVersion": 36,"tags": "", "title": "Empty Tags"}';
    const validateDashboardJsonEmptyTags = await validateDashboardJson(jsonImportEmptyTags);
    expect(validateDashboardJsonEmptyTags).toBe('tags expected array');
  });
  it('Should not return true if not valid JSON', async () => {
    const jsonImportInvalidJson = '{"schemaVersion": 36,"tags": {"tag", "nested tag"}, "title": "Nested lists"}';
    const validateDashboardJsonNotValid = await validateDashboardJson(jsonImportInvalidJson);
    expect(validateDashboardJsonNotValid).toBe('Not valid JSON');
  });
});

describe('processDashboard', () => {
  const panel = new PanelModel({
    datasource: {
      type: 'mysql',
      uid: '${DS_GDEV-MYSQL}',
    },
  });

  const panelWithLibPanel = {
    gridPos: {
      h: 8,
      w: 12,
      x: 0,
      y: 8,
    },
    id: 3,
    libraryPanel: {
      uid: 'a0379b21-fa20-4313-bf12-d7fd7ceb6f90',
      name: 'another prom lib panel',
    },
  };
  const libPanel = {
    'a0379b21-fa20-4313-bf12-d7fd7ceb6f90': {
      name: 'another prom lib panel',
      uid: 'a0379b21-fa20-4313-bf12-d7fd7ceb6f90',
      kind: 1,
      model: {
        datasource: {
          type: 'prometheus',
          uid: '${DS_GDEV-PROMETHEUS-FOR-LIBRARY-PANEL}',
        },
        description: '',
        fieldConfig: {
          defaults: {
            color: {
              mode: 'palette-classic',
            },
            custom: {
              axisCenteredZero: false,
              axisColorMode: 'text',
              axisLabel: '',
              axisPlacement: 'auto',
              barAlignment: 0,
              drawStyle: 'line',
              fillOpacity: 0,
              gradientMode: 'none',
              hideFrom: {
                legend: false,
                tooltip: false,
                viz: false,
              },
              lineInterpolation: 'linear',
              lineWidth: 1,
              pointSize: 5,
              scaleDistribution: {
                type: 'linear',
              },
              showPoints: 'auto',
              spanNulls: false,
              stacking: {
                group: 'A',
                mode: 'none',
              },
              thresholdsStyle: {
                mode: 'off',
              },
            },
            mappings: [],
            thresholds: {
              mode: 'absolute',
              steps: [
                {
                  color: 'green',
                  value: null,
                },
                {
                  color: 'red',
                  value: 80,
                },
              ],
            },
          },
          overrides: [],
        },
        libraryPanel: {
          name: 'another prom lib panel',
          uid: 'a0379b21-fa20-4313-bf12-d7fd7ceb6f90',
        },
        options: {
          legend: {
            calcs: [],
            displayMode: 'list',
            placement: 'bottom',
            showLegend: true,
          },
          tooltip: {
            mode: 'single',
            sort: 'none',
          },
        },
        targets: [
          {
            datasource: {
              type: 'prometheus',
              uid: 'gdev-prometheus',
            },
            editorMode: 'builder',
            expr: 'access_evaluation_duration_bucket',
            instant: false,
            range: true,
            refId: 'A',
          },
        ],
        title: 'Panel Title',
        type: 'timeseries',
      },
    },
  };

  const panelWithSecondLibPanel = {
    gridPos: {
      h: 8,
      w: 12,
      x: 0,
      y: 16,
    },
    id: 1,
    libraryPanel: {
      uid: 'c46a6b49-de40-43b3-982c-1b5e1ec084a4',
      name: 'Testing lib panel',
    },
  };
  const secondLibPanel = {
    'c46a6b49-de40-43b3-982c-1b5e1ec084a4': {
      name: 'Testing lib panel',
      uid: 'c46a6b49-de40-43b3-982c-1b5e1ec084a4',
      kind: 1,
      model: {
        datasource: {
          type: 'prometheus',
          uid: '${DS_GDEV-PROMETHEUS-FOR-LIBRARY-PANEL}',
        },
        description: '',
        fieldConfig: {
          defaults: {
            color: {
              mode: 'palette-classic',
            },
            custom: {
              axisCenteredZero: false,
              axisColorMode: 'text',
              axisLabel: '',
              axisPlacement: 'auto',
              barAlignment: 0,
              drawStyle: 'line',
              fillOpacity: 0,
              gradientMode: 'none',
              hideFrom: {
                legend: false,
                tooltip: false,
                viz: false,
              },
              lineInterpolation: 'linear',
              lineWidth: 1,
              pointSize: 5,
              scaleDistribution: {
                type: 'linear',
              },
              showPoints: 'auto',
              spanNulls: false,
              stacking: {
                group: 'A',
                mode: 'none',
              },
              thresholdsStyle: {
                mode: 'off',
              },
            },
            mappings: [],
            thresholds: {
              mode: 'absolute',
              steps: [
                {
                  color: 'green',
                  value: null,
                },
                {
                  color: 'red',
                  value: 80,
                },
              ],
            },
          },
          overrides: [],
        },
        libraryPanel: {
          name: 'Testing lib panel',
          uid: 'c46a6b49-de40-43b3-982c-1b5e1ec084a4',
        },
        options: {
          legend: {
            calcs: [],
            displayMode: 'list',
            placement: 'bottom',
            showLegend: true,
          },
          tooltip: {
            mode: 'single',
            sort: 'none',
          },
        },
        targets: [
          {
            datasource: {
              type: 'prometheus',
              uid: 'gdev-prometheus',
            },
            editorMode: 'builder',
            expr: 'access_evaluation_duration_count',
            instant: false,
            range: true,
            refId: 'A',
          },
        ],
        title: 'Panel Title',
        type: 'timeseries',
      },
    },
  };

  const importedJson: DashboardJson = {
    ...defaultDashboard,
    __inputs: [
      {
        name: 'DS_GDEV-MYSQL',
        label: 'gdev-mysql',
        description: '',
        type: 'datasource',
        value: '',
      },
      {
        name: 'DS_GDEV-PROMETHEUS-FOR-LIBRARY-PANEL',
        label: 'gdev-prometheus',
        description: '',
        type: 'datasource',
        value: '',
        usage: {
          libraryPanels: [
            {
              name: 'another prom lib panel',
              uid: 'a0379b21-fa20-4313-bf12-d7fd7ceb6f90',
            },
          ],
        },
      },
    ],
    __elements: {
      ...libPanel,
    },
    __requires: [
      {
        type: 'grafana',
        id: 'grafana',
        name: 'Grafana',
        version: '10.1.0-pre',
      },
      {
        type: 'datasource',
        id: 'mysql',
        name: 'MySQL',
        version: '1.0.0',
      },
      {
        type: 'datasource',
        id: 'prometheus',
        name: 'Prometheus',
        version: '1.0.0',
      },
      {
        type: 'panel',
        id: 'table',
        name: 'Table',
        version: '',
      },
    ],
    panels: [],
  };

  it("Should return 2 inputs, 1 for library panel because it's used for 2 panels", async () => {
    mocks.getLibraryPanel.mockImplementation(() => {
      throw { status: 404 };
    });
    const importDashboardState = initialImportDashboardState;
    const dashboardJson: DashboardJson = {
      ...importedJson,
      panels: [panel, panelWithLibPanel, panelWithLibPanel],
    };
    const libPanelInputs = await getLibraryPanelInputs(dashboardJson);
    const newDashboardState = {
      ...importDashboardState,
      inputs: {
        ...importDashboardState.inputs,
        libraryPanels: libPanelInputs!,
      },
    };

    const processedDashboard = processDashboard(dashboardJson, newDashboardState);
    const dsInputsForLibPanels = processedDashboard.__inputs!.filter((input) => !!input.usage?.libraryPanels);
    expect(processedDashboard.__inputs).toHaveLength(2);
    expect(dsInputsForLibPanels).toHaveLength(1);
  });
  it('Should return 3 inputs, 2 for library panels', async () => {
    mocks.getLibraryPanel.mockImplementation(() => {
      throw { status: 404 };
    });
    const importDashboardState = initialImportDashboardState;
    const dashboardJson: DashboardJson = {
      ...importedJson,
      __inputs: [
        {
          name: 'DS_GDEV-MYSQL',
          label: 'gdev-mysql',
          description: '',
          type: 'datasource',
          value: '',
        },
        {
          name: 'DS_GDEV-PROMETHEUS-FOR-LIBRARY-PANEL',
          label: 'gdev-prometheus',
          description: '',
          type: 'datasource',
          value: '',
          usage: {
            libraryPanels: [
              {
                name: 'another prom lib panel',
                uid: 'a0379b21-fa20-4313-bf12-d7fd7ceb6f90',
              },
            ],
          },
        },
        {
          name: 'DS_GDEV-MYSQL-FOR-LIBRARY-PANEL',
          label: 'gdev-mysql-2',
          description: '',
          type: 'datasource',
          value: '',
          usage: {
            libraryPanels: [
              {
                uid: 'c46a6b49-de40-43b3-982c-1b5e1ec084a4',
                name: 'Testing lib panel',
              },
            ],
          },
        },
      ],
      __elements: {
        ...libPanel,
        ...secondLibPanel,
      },
      panels: [panel, panelWithLibPanel, panelWithSecondLibPanel],
    };
    const libPanelInputs = await getLibraryPanelInputs(dashboardJson);
    const newDashboardState = {
      ...importDashboardState,
      inputs: {
        ...importDashboardState.inputs,
        libraryPanels: libPanelInputs!,
      },
    };

    const processedDashboard = processDashboard(dashboardJson, newDashboardState);
    const dsInputsForLibPanels = processedDashboard.__inputs!.filter((input) => !!input.usage?.libraryPanels);
    expect(processedDashboard.__inputs).toHaveLength(3);
    expect(dsInputsForLibPanels).toHaveLength(2);
  });

  it('Should return 1 input, since library panels already exist in the instance', async () => {
    const getLibPanelFirstRS: LibraryElementDTO = {
      folderUid: '',
      uid: 'a0379b21-fa20-4313-bf12-d7fd7ceb6f90',
      name: 'another prom lib panel',
      type: 'timeseries',
      description: '',
      model: {
        transparent: false,
        transformations: [],
        datasource: {
          type: 'prometheus',
          uid: 'gdev-prometheus',
        },
        description: '',
        fieldConfig: {
          defaults: {
            color: {
              mode: FieldColorModeId.PaletteClassic,
            },
            custom: {
              axisCenteredZero: false,
              axisColorMode: 'text',
              axisLabel: '',
              axisPlacement: 'auto',
              barAlignment: 0,
              drawStyle: 'line',
              fillOpacity: 0,
              gradientMode: 'none',
              hideFrom: {
                legend: false,
                tooltip: false,
                viz: false,
              },
              lineInterpolation: 'linear',
              lineWidth: 1,
              pointSize: 5,
              scaleDistribution: {
                type: 'linear',
              },
              showPoints: 'auto',
              spanNulls: false,
              stacking: {
                group: 'A',
                mode: 'none',
              },
              thresholdsStyle: {
                mode: 'off',
              },
            },
            mappings: [],
            thresholds: {
              mode: ThresholdsMode.Absolute,
              steps: [
                {
                  color: 'green',
                  value: null,
                },
                {
                  color: 'red',
                  value: 80,
                },
              ],
            },
          },
          overrides: [],
        },
        options: {
          legend: {
            calcs: [],
            displayMode: 'list',
            placement: 'bottom',
            showLegend: true,
          },
          tooltip: {
            mode: 'single',
            sort: 'none',
          },
        },
        targets: [
          {
            datasource: {
              type: 'prometheus',
              uid: 'gdev-prometheus',
            },
            editorMode: 'builder',
            expr: 'access_evaluation_duration_bucket',
            instant: false,
            range: true,
            refId: 'A',
          },
        ],
        title: 'Panel Title',
        type: 'timeseries',
      },
      version: 1,
    };

    const getLibPanelSecondRS: LibraryElementDTO = {
      folderUid: '',
      uid: 'c46a6b49-de40-43b3-982c-1b5e1ec084a4',
      name: 'Testing lib panel',
      type: 'timeseries',
      description: '',
      model: {
        transparent: false,
        transformations: [],
        datasource: {
          type: 'prometheus',
          uid: 'gdev-prometheus',
        },
        description: '',
        fieldConfig: {
          defaults: {
            color: {
              mode: FieldColorModeId.PaletteClassic,
            },
            custom: {
              axisCenteredZero: false,
              axisColorMode: 'text',
              axisLabel: '',
              axisPlacement: 'auto',
              barAlignment: 0,
              drawStyle: 'line',
              fillOpacity: 0,
              gradientMode: 'none',
              hideFrom: {
                legend: false,
                tooltip: false,
                viz: false,
              },
              lineInterpolation: 'linear',
              lineWidth: 1,
              pointSize: 5,
              scaleDistribution: {
                type: 'linear',
              },
              showPoints: 'auto',
              spanNulls: false,
              stacking: {
                group: 'A',
                mode: 'none',
              },
              thresholdsStyle: {
                mode: 'off',
              },
            },
            mappings: [],
            thresholds: {
              mode: ThresholdsMode.Absolute,
              steps: [
                {
                  color: 'green',
                  value: null,
                },
                {
                  color: 'red',
                  value: 80,
                },
              ],
            },
          },
          overrides: [],
        },
        options: {
          legend: {
            calcs: [],
            displayMode: 'list',
            placement: 'bottom',
            showLegend: true,
          },
          tooltip: {
            mode: 'single',
            sort: 'none',
          },
        },
        targets: [
          {
            datasource: {
              type: 'prometheus',
              uid: 'gdev-prometheus',
            },
            editorMode: 'builder',
            expr: 'access_evaluation_duration_count',
            instant: false,
            range: true,
            refId: 'A',
          },
        ],
        title: 'Panel Title',
        type: 'timeseries',
      },
      version: 1,
    };
    mocks.getLibraryPanel
      .mockReturnValueOnce(Promise.resolve(getLibPanelFirstRS))
      .mockReturnValueOnce(Promise.resolve(getLibPanelSecondRS));

    const importDashboardState = initialImportDashboardState;
    const dashboardJson: DashboardJson = {
      ...importedJson,
      __inputs: [
        {
          name: 'DS_GDEV-MYSQL',
          label: 'gdev-mysql',
          description: '',
          type: 'datasource',
          value: '',
        },
        {
          name: 'DS_GDEV-PROMETHEUS-FOR-LIBRARY-PANEL',
          label: 'gdev-prometheus',
          description: '',
          type: 'datasource',
          value: '',
          usage: {
            libraryPanels: [
              {
                name: 'another prom lib panel',
                uid: 'a0379b21-fa20-4313-bf12-d7fd7ceb6f90',
              },
            ],
          },
        },
        {
          name: 'DS_GDEV-MYSQL-FOR-LIBRARY-PANEL',
          label: 'gdev-mysql-2',
          description: '',
          type: 'datasource',
          value: '',
          usage: {
            libraryPanels: [
              {
                uid: 'c46a6b49-de40-43b3-982c-1b5e1ec084a4',
                name: 'Testing lib panel',
              },
            ],
          },
        },
      ],
      __elements: {
        ...libPanel,
        ...secondLibPanel,
      },
      panels: [panel, panelWithLibPanel, panelWithSecondLibPanel],
    };
    const libPanelInputs = await getLibraryPanelInputs(dashboardJson);
    const newDashboardState = {
      ...importDashboardState,
      inputs: {
        ...importDashboardState.inputs,
        libraryPanels: libPanelInputs!,
      },
    };

    const processedDashboard = processDashboard(dashboardJson, newDashboardState);
    const dsInputsForLibPanels = processedDashboard.__inputs!.filter((input) => !!input.usage?.libraryPanels);
    expect(processedDashboard.__inputs).toHaveLength(1);
    expect(dsInputsForLibPanels).toHaveLength(0);
  });
});
