import { TestVariable } from '@grafana/scenes';
import { type Dashboard } from '@grafana/schema';
import { variableAdapters } from 'app/features/variables/adapters';
import { createCustomVariableAdapter } from 'app/features/variables/custom/adapter';
import { createDataSourceVariableAdapter } from 'app/features/variables/datasource/adapter';
import { createQueryVariableAdapter } from 'app/features/variables/query/adapter';

import {
  createDependencyEdges,
  getVariableName,
  createDependencyNodes,
  createUsagesNetwork,
  transformUsagesToNetwork,
} from './utils';

variableAdapters.setInit(() => [
  createDataSourceVariableAdapter(),
  createCustomVariableAdapter(),
  createQueryVariableAdapter(),
]);

const dashboardMock: Dashboard = {
  panels: [
    {
      datasource: {
        type: 'prometheus',
        uid: 'gdev-prometheus',
      },
      targets: [
        {
          datasource: {
            type: 'prometheus',
            uid: 'gdev-prometheus',
          },
          disableTextWrap: false,
          editorMode: 'code',
          expr: 'go_gc_duration_seconds{job=$query0)',
          fullMetaSearch: false,
          includeNullMetadata: true,
          instant: false,
          legendFormat: '__auto',
          range: true,
          refId: 'A',
          useBackend: false,
        },
        {
          datasource: {
            type: 'prometheus',
            uid: 'gdev-prometheus',
          },
          disableTextWrap: false,
          editorMode: 'code',
          expr: 'go_gc_duration_seconds{job=$query1)',
          fullMetaSearch: false,
          includeNullMetadata: true,
          instant: false,
          legendFormat: '__auto',
          range: true,
          refId: 'A',
          useBackend: false,
        },
      ],
      title: 'Panel Title',
      type: 'timeseries',
    },
  ],
  schemaVersion: 40,
};

describe('createDependencyNodes', () => {
  it('should create node for each variable', () => {
    const variables = [
      new TestVariable({ name: 'A', query: 'A.*', value: '', text: '', options: [] }),
      new TestVariable({ name: 'B', query: 'B.*', value: '', text: '', options: [] }),
      new TestVariable({ name: 'C', query: 'C.*', value: '', text: '', options: [] }),
    ];
    const graphNodes = createDependencyNodes(variables);
    expect(graphNodes[0].id).toBe('A');
    expect(graphNodes[1].id).toBe('B');
    expect(graphNodes[2].id).toBe('C');
  });
});

describe('createDependencyEdges', () => {
  it('should create edges for variable dependencies', () => {
    const variables = [
      new TestVariable({ name: 'A', query: 'A.*', value: '', text: '', options: [] }),
      new TestVariable({ name: 'B', query: '${A}.*', value: '', text: '', options: [] }),
      new TestVariable({ name: 'C', query: '${B}.*', value: '', text: '', options: [] }),
    ];
    const graphEdges = createDependencyEdges(variables);
    expect(graphEdges).toContainEqual({ from: 'B', to: 'A' });
    expect(graphEdges).toContainEqual({ from: 'C', to: 'B' });
  });
});

describe('createUsagesNetwork', () => {
  it('should create usage network for variables', () => {
    const variables = [
      new TestVariable({
        type: 'query',
        name: 'query0',
        loading: false,
        error: null,
      }),
      new TestVariable({
        type: 'query',
        name: 'query1',
        loading: false,
        error: null,
      }),
    ];

    const usagesNetwork = createUsagesNetwork(variables, dashboardMock);
    expect(usagesNetwork).toHaveLength(2);
    expect(usagesNetwork[0].variable.state.name).toBe('query0');
    expect(usagesNetwork[1].variable.state.name).toBe('query1');
  });

  it('should not create usage network for variables that are not part of the dashboard', () => {
    const variables = [
      new TestVariable({
        type: 'query',
        name: 'query3',
        loading: false,
        error: null,
      }),
    ];

    const usagesNetwork = createUsagesNetwork(variables, dashboardMock);
    expect(usagesNetwork).toHaveLength(0);
  });
});

describe('transformUsagesToNetwork', () => {
  it('should transform usages to network', () => {
    const variables = [
      new TestVariable({ name: 'A', query: 'A.*', value: '', text: '', options: [] }),
      new TestVariable({ name: 'B', query: 'B.*', value: '', text: '', options: [] }),
    ];
    const usages = [
      { variable: variables[0], tree: { key: 'value' } },
      { variable: variables[1], tree: { key: 'value' } },
    ];

    const network = transformUsagesToNetwork(usages);
    expect(network).toHaveLength(2);
    expect(network[0].nodes).toContainEqual({ id: 'dashboard', label: 'dashboard' });
    expect(network[0].edges).toHaveLength(2);
  });
});

describe('getVariableName', () => {
  it('should return undefined if no match is found', () => {
    expect(getVariableName('no variable here')).toBeUndefined();
  });

  it('should return undefined if variable matches inherited object prop names', () => {
    expect(getVariableName('${toString}')).toBeUndefined();
  });

  it('should return the variable name if it exists and does not match inherited object prop names', () => {
    expect(getVariableName('${myVariable}')).toBe('myVariable');
  });
});
