import { createDashboardModelFixture, createPanelSaveModel } from '../../state/__fixtures__/dashboardFixtures';

import { orderProperties, JSONArray, JSONValue, isObject, getDashboardStringDiff } from './jsonDiffText';

describe('orderProperties', () => {
  it('should sort simple objects', () => {
    // Simplest possible case
    const before = {
      firstProperty: 'foo',
      secondProperty: 'bar',
    };

    const after = {
      secondProperty: 'bar',
      firstProperty: 'foo',
    };

    // Call the function to test
    const result = orderProperties(before, after);

    expect(result).toEqual({
      firstProperty: 'foo',
      secondProperty: 'bar',
    });
  });

  it('should sort arrays', () => {
    const result = orderProperties([0, 1], [1, 0]);

    expect(result).toEqual([0, 1]);
  });

  it('should handle nested objects', () => {
    const before = {
      nested: {
        firstProperty: 'foo',
        secondProperty: 'bar',
      },
    };

    const after = {
      nested: {
        secondProperty: 'bar',
        firstProperty: 'foo',
      },
    };

    const result = orderProperties(before, after);

    expect(result).toEqual({
      nested: {
        firstProperty: 'foo',
        secondProperty: 'bar',
      },
    });
  });

  it('should handle arrays of objects with different order', () => {
    const before = [
      { id: 1, name: 'Alice' },
      { id: 2, name: 'Bob' },
    ];

    const after = [
      { id: 2, name: 'Bob' },
      { id: 1, name: 'Alice' },
    ];

    const result = orderProperties(before, after);

    expect(result).toEqual([
      { id: 1, name: 'Alice' },
      { id: 2, name: 'Bob' },
    ]);
  });

  it('should handle null values', () => {
    const before = {
      a: null,
      b: null,
    };

    const after = {
      b: null,
      a: null,
    };

    const result = orderProperties(before, after);

    expect(result).toEqual({
      a: null,
      b: null,
    });
  });

  it('should handle empty objects', () => {
    const before = {};
    const after = {};

    const result = orderProperties(before, after);

    expect(result).toEqual({});
  });

  it('should handle empty arrays', () => {
    const before: JSONValue[] = [];
    const after: JSONValue[] = [];

    const result = orderProperties(before, after);

    expect(result).toEqual([]);
  });

  it('should handle deeply nested objects', () => {
    const before = {
      a: {
        b: {
          c: 'foo',
        },
      },
      d: 'bar',
    };

    const after = {
      d: 'bar',
      a: {
        b: {
          c: 'foo',
        },
      },
    };

    const result = orderProperties(before, after);

    expect(result).toEqual({
      a: {
        b: {
          c: 'foo',
        },
      },
      d: 'bar',
    });
  });

  it('should handle arrays of nested objects', () => {
    const before = [
      { id: 1, nested: { name: 'Alice' } },
      { id: 2, nested: { name: 'Bob' } },
    ];

    const after = [
      { id: 2, nested: { name: 'Bob' } },
      { id: 1, nested: { name: 'Alice' } },
    ];

    const result = orderProperties(before, after);

    expect(result).toEqual([
      { id: 1, nested: { name: 'Alice' } },
      { id: 2, nested: { name: 'Bob' } },
    ]);
  });

  it('should handle mixed arrays of objects and primitive values', () => {
    const before = [{ id: 1 }, 42, [3, 2, 1]];

    const after = [{ id: 1 }, [3, 2, 1], 42];

    const result = orderProperties(before, after);

    expect(result).toEqual([{ id: 1 }, 42, [3, 2, 1]]);
  });

  it('should handle arrays of objects with nested arrays', () => {
    const before = [
      { id: 1, values: [3, 2, 1] },
      { id: 2, values: [6, 5, 4] },
    ];

    const after = [
      { id: 2, values: [6, 5, 4] },
      { id: 1, values: [3, 2, 1] },
    ];

    const result = orderProperties(before, after);

    expect(result).toEqual([
      { id: 1, values: [3, 2, 1] },
      { id: 2, values: [6, 5, 4] },
    ]);
  });

  it('should handle arrays of arrays', () => {
    const before = [
      [1, 2, 3],
      [4, 5, 6],
    ];

    const after = [
      [4, 5, 6],
      [1, 2, 3],
    ];

    const result = orderProperties(before, after);

    expect(result).toEqual([
      [1, 2, 3],
      [4, 5, 6],
    ]);
  });

  it('should match reordered and modified arrays to nearest keys', () => {
    const before = [
      { id: '1', name: 'Alice', country: 'England' },
      { id: '2', name: 'Bob', country: 'America' },
      { id: '3', name: 'Charlie', country: 'Foxtrot' },
    ];

    const after: JSONArray = [{ name: 'Charlie', country: 'Foxtrot' }, { name: 'Alice' }];

    const result = orderProperties(before, after);

    expect(result).toEqual([{ name: 'Alice' }, { name: 'Charlie', country: 'Foxtrot' }]);
  });
});

describe('isObject', () => {
  it('should return true for non-array objects', () => {
    expect(isObject({})).toBe(true);
    expect(isObject({ foo: 'bar' })).toBe(true);
    expect(isObject(null)).toBe(false);
    expect(isObject([])).toBe(false);
    expect(isObject('')).toBe(false);
    expect(isObject(123)).toBe(false);
    expect(isObject(true)).toBe(false);
  });
});

describe('getDashboardStringDiff', () => {
  const dashboard = {
    title: 'Original Title',
    schemaVersion: 38,
    panels: [
      createPanelSaveModel({
        id: 1,
        title: 'Original Panel Title',
        gridPos: { x: 0, y: 0, w: 2, h: 2 },
      }),
      createPanelSaveModel({
        id: 2,
        title: 'Panel to be moved',
        gridPos: { x: 2, y: 0, w: 2, h: 2 },
      }),
    ],
  };

  it('should no return changes when nothing changes', () => {
    const dashboardModel = createDashboardModelFixture(dashboard);

    const result = getDashboardStringDiff(dashboardModel);

    expect(result).toEqual({
      migrationDiff:
        'Index: Original Title\n' +
        '===================================================================\n' +
        '--- Original Title\t\n' +
        '+++ Original Title\t\n',
      userDiff:
        'Index: Original Title\n' +
        '===================================================================\n' +
        '--- Original Title\t\n' +
        '+++ Original Title\t\n',
    });
  });

  it('should return a diff of the dashboard title as user change', () => {
    const dashboardModel = createDashboardModelFixture(dashboard);
    dashboardModel.title = 'New Title';

    const result = getDashboardStringDiff(dashboardModel);

    expect(result.userDiff).toContain(`-  \"title\": \"Original Title\"`);
    expect(result.userDiff).toContain(`+  \"title\": \"New Title\",`);
  });
});
