import { describe, it, expect } from 'vitest';

import { UtilsV1 as UtilsV1Pb } from '@superblocksteam/types';

import { AnyPageDSL, ApplicationConfiguration, ApplicationSettings } from '../types/index.js';
import {
  ApplicationSignatureTreeV1,
  ApplicationSignatureTreeV2,
  createApplicationSignatureTree,
  getAlgorithmName,
  verifyApplicationHashTree
} from './index';

const pageDsl = {
  version: 8,
  widgetId: '0',
  children: []
} as unknown as AnyPageDSL;
const appSettings = {} as ApplicationSettings;
const appConfiguration = {
  routes: {}
} as ApplicationConfiguration;
const hashTreeV1: ApplicationSignatureTreeV1 = {
  v: 1,
  page: {
    hash: 'ZMpaDRZaIl1QxyEwrjoPUQJbkcCtTsNXbVySIGlcgVg=',
    version: 8
  },
  settings: {
    components: 'RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=',
    rest: 'RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o='
  }
};
const hashTreeV2: ApplicationSignatureTreeV2 = {
  v: 2,
  pages: {
    'fake-page-id': {
      hash: 'ZMpaDRZaIl1QxyEwrjoPUQJbkcCtTsNXbVySIGlcgVg=',
      version: 8
    }
  },
  settings: {
    components: 'RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=',
    rest: 'RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o='
  },
  configuration: {
    version: 1,
    hash: 'boT9TuU6iU3HiVIO8vh8YU+SgXAozIHLSuhRcux09F8='
  }
};

describe('createApplicationSignatureTree', () => {
  it('returns a v2 signature tree', async () => {
    const calculatedHashTree = await createApplicationSignatureTree({
      pages: { 'fake-page-id': pageDsl },
      appSettings,
      configuration: appConfiguration
    });
    expect(calculatedHashTree).toEqual(hashTreeV2);
  });
});

describe('verifyApplicationHashTree', () => {
  describe('v1', () => {
    it('verifies a v1 signature tree', async () => {
      const result = await verifyApplicationHashTree({ hashTree: hashTreeV1, pages: { 'fake-page-id': pageDsl }, appSettings });
      expect(result).toBe(true);
    });

    it('returns false if the page hash does not match', async () => {
      const badHashTree = { ...hashTreeV1, page: { ...hashTreeV1.page, hash: 'bad-hash' } };
      const result = await verifyApplicationHashTree({ hashTree: badHashTree, pages: { 'fake-page-id': pageDsl }, appSettings });
      expect(result).toBe(false);
    });

    it('returns false if the settings hash does not match', async () => {
      const badHashTree = { ...hashTreeV1, settings: { ...hashTreeV1.settings, components: 'bad-hash' } };
      const result = await verifyApplicationHashTree({ hashTree: badHashTree, pages: { 'fake-page-id': pageDsl }, appSettings });
      expect(result).toBe(false);
    });
  });

  describe('v2', () => {
    it('verifies a v2 signature tree', async () => {
      const result = await verifyApplicationHashTree({ hashTree: hashTreeV2, pages: { 'fake-page-id': pageDsl }, appSettings });
      expect(result).toBe(true);
    });

    it('returns false if the page hash does not match', async () => {
      const badHashTree = { ...hashTreeV2, pages: { 'fake-page-id': { ...hashTreeV2.pages['fake-page-id'], hash: 'bad-hash' } } };
      const result = await verifyApplicationHashTree({ hashTree: badHashTree, pages: { 'fake-page-id': pageDsl }, appSettings });
      expect(result).toBe(false);
    });

    it('returns false if the settings hash does not match', async () => {
      const badHashTree = { ...hashTreeV2, settings: { ...hashTreeV2.settings, components: 'bad-hash' } };
      const result = await verifyApplicationHashTree({ hashTree: badHashTree, pages: { 'fake-page-id': pageDsl }, appSettings });
      expect(result).toBe(false);
    });
  });
});

describe('get algorithm name', () => {
  it.each([
    {
      name: 'unspecified algorithm',
      algorithm: UtilsV1Pb.Signature_Algorithm.UNSPECIFIED,
      expected: 'ALGORITHM_UNSPECIFIED'
    },
    {
      name: 'ED25519 algorithm',
      algorithm: UtilsV1Pb.Signature_Algorithm.ED25519,
      expected: 'ALGORITHM_ED25519'
    }
  ])('should return $expected for $name', ({ algorithm, expected }) => {
    const actual = getAlgorithmName(algorithm);
    expect(actual).toEqual(expected);
  });
});
