import fs from 'fs';
import path from 'path';
import { Token, TokenGroup, TokenType } from '@supernovaio/sdk-exporters';
import {
  generateFileContent,
  addDisclaimer,
  filterTokensByTypeAndGroup,
  generateScssObjectOutput,
  generateJsObjectOutput,
  getGroups,
} from '../contentGenerator';
import { exampleDimensionAndStringTokens } from '../../../tests/fixtures/exampleDimensionAndStringTokens';
import { FileData } from '../../config/fileConfig';
import { exampleTypographyTokens } from '../../../tests/fixtures/exampleTypographyTokens';
import { exampleGroups } from '../../../tests/fixtures/exampleGroups';

const mockedExpectedResult = fs.readFileSync(
  path.join(__dirname, '../../../tests/fixtures/exampleFileContent.scss'),
  'utf-8',
);
const mappedTokens: Map<string, Token> = new Map([]);
const tokenGroups: Array<TokenGroup> = exampleGroups;

describe('contentGenerator', () => {
  describe('generateFileContent', () => {
    it('should generate file content', () => {
      const tokens = Array.from(exampleDimensionAndStringTokens.values());
      const fileData: FileData = {
        fileName: 'testFile',
        tokenTypes: [TokenType.dimension, TokenType.string],
        groupNames: ['Grid', 'String'],
        hasStylesObject: true,
        hasParentPrefix: true,
        sortByNumValue: false,
      };

      const fileContent = generateFileContent(tokens, mappedTokens, tokenGroups, fileData, false);

      expect(fileContent).toStrictEqual({ content: mockedExpectedResult });
    });
  });

  describe('addDisclaimer', () => {
    it('should add disclaimer to the top of the content', () => {
      const content = '/* This is a test content */';
      const expectedContent = `/* This file was generated by Supernova, don't change manually */\n${content}`;

      expect(addDisclaimer(content)).toBe(expectedContent);
    });
  });

  describe('filterTokensByTypeAndGroup', () => {
    const dataProviderItems = [
      {
        type: TokenType.dimension,
        group: 'Grid',
        tokenIdentifier: 'dimensionRef',
        description: 'dimension token type and Grid group',
      },
      {
        type: TokenType.string,
        group: 'Grid',
        tokenIdentifier: 'stringRef',
        description: 'string token type and Grid group',
      },
    ];

    const dataTypographyProviderItems = {
      type: TokenType.typography,
      group: 'Heading',
      tokenIdentifier: 'typographyRef1',
    };

    it.each(dataProviderItems)('should filter $description', ({ type, group, tokenIdentifier }) => {
      const tokens = Array.from(exampleDimensionAndStringTokens.values());
      const expectedTokens = [exampleDimensionAndStringTokens.get(tokenIdentifier) as Token];

      expect(filterTokensByTypeAndGroup(tokens, type, group)).toStrictEqual(expectedTokens);
    });

    it(`should filter ${dataTypographyProviderItems.type} token type and ${dataTypographyProviderItems.group} group and exclude tokens with "-Underline"`, () => {
      const tokens = Array.from(exampleTypographyTokens.values());
      const expectedTokens = [exampleTypographyTokens.get(dataTypographyProviderItems.tokenIdentifier) as Token];

      const filteredTokens = filterTokensByTypeAndGroup(
        tokens,
        dataTypographyProviderItems.type,
        dataTypographyProviderItems.group,
      );

      expect(filteredTokens).toStrictEqual(expectedTokens);
    });
  });

  describe('generateScssObjectOutput', () => {
    it('should generate SCSS object output', () => {
      const stylesObject = {
        $grids: { columns: '$grid-columns', spacing: { desktop: '$grid-spacing-desktop' } },
      };
      const expectedResult = fs.readFileSync(path.join(__dirname, '../__fixtures__/unformattedExample.scss'), 'utf-8');

      const scssOutput = generateScssObjectOutput(stylesObject);

      expect(scssOutput).toBe(expectedResult);
    });
  });

  describe('generateJsObjectOutput', () => {
    it('should generate JS object output', () => {
      const stylesObject = {
        grids: { columns: 'gridColumns', spacing: { desktop: 'gridSpacingDesktop' } },
      };
      const expectedResult = fs.readFileSync(path.join(__dirname, '../__fixtures__/unformattedExample.ts'), 'utf-8');

      const jsOutput = generateJsObjectOutput(stylesObject);

      expect(jsOutput).toBe(expectedResult);
    });
  });

  describe('getGroups', () => {
    it('should return group names', () => {
      const tokens = Array.from(exampleDimensionAndStringTokens.values());
      const excludeGroupNames = ['String'];
      const groupNames = ['Grid'];

      const groups = getGroups(tokens, excludeGroupNames, groupNames);

      expect(groups).toStrictEqual(['Grid']);
    });

    it('should return group names without excluded group names', () => {
      const tokens = Array.from(exampleDimensionAndStringTokens.values());
      const excludeGroupNames = ['String', 'Grid'];
      const groupNames = ['Grid'];

      const groups = getGroups(tokens, excludeGroupNames, groupNames);

      expect(groups).toStrictEqual([]);
    });

    it('should return group names with no excluded group names', () => {
      const tokens = Array.from(exampleDimensionAndStringTokens.values());
      const excludeGroupNames = null;
      const groupNames = ['Grid', 'String'];

      const groups = getGroups(tokens, excludeGroupNames, groupNames);

      expect(groups).toStrictEqual(['Grid', 'String']);
    });

    it('should not return group names', () => {
      const tokens = Array.from(exampleDimensionAndStringTokens.values());
      const excludeGroupNames = ['Grid', 'String'];
      const groupNames = [''];

      const groups = getGroups(tokens, excludeGroupNames, groupNames);

      expect(groups).toStrictEqual([]);
    });
  });
});
