import { Supernova, Token, TokenGroup, TokenTheme } from '@supernovaio/sdk-exporters';
import fs from 'fs';
import path from 'path';
import { exampleDimensionAndStringTokens } from '../../../tests/fixtures/exampleDimensionAndStringTokens';
import { exampleGroups } from '../../../tests/fixtures/exampleGroups';
import { nonThemedFilesData } from '../../config/fileConfig';
import {
  generateBarrelFile,
  generateFiles,
  generateOutputFilesByThemes,
  generateRootThemesFileContent,
  generateRootThemesFileImports,
  generateThemesRootFile,
  jsImportStatement,
  scssImportStatement,
} from '../fileGenerator';

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;
const emptyFile = "/* This file was generated by Supernova, don't change manually */\n\n";
const barrelFile = fs.readFileSync(path.join(__dirname, '../__fixtures__/barrelFileMock.scss'), 'utf-8');
const barrelColorFile = "@forward 'colors';\n";
const barrelColorTokensFile = "@forward 'color-tokens';\n";
const barrelJsFile = `export * from './borders';
export * from './gradients';
export * from './other';
export * from './radii';
export * from './shadows';
export * from './spacing';
export * from './typography';
`;
const barrelJsColorFile = "export * from './colors';\n";

const mockedTsFile = `/* This file was generated by Supernova, don't change manually */
export const gridSpacingDesktop = '32px';\n
export const gridColumns = '12';\n
export const grids = {
  spacing: {
    desktop: gridSpacingDesktop,
  },
  columns: gridColumns,
};\n`;

const mockedRootThemeFile = fs.readFileSync(path.join(__dirname, '../__fixtures__/mockedRootThemeFile.scss'), 'utf-8');

const mockedRootThemeJsFile = `import * as themeLight from './theme-light';
import * as themeLightInverted from './theme-light-inverted';\n
// The first theme is the default theme, as the left column in the Figma table.
export const themes = {
  themeLight: {
    tokens: themeLight,
  },
  themeLightInverted: {
    tokens: themeLightInverted,
  },
};
`;

const mockedColorTokensFile = `/* This file was generated by Supernova, don't change manually */
\n\n@mixin color-css-variables {\n\n}\n`;

const mockedThemeRootFileContent =
  'theme-light: (\n' +
  'variables: meta.module-variables(theme-light),\n' +
  'mixins: meta.module-mixins(theme-light),\n),\n' +
  'theme-light-inverted: (\n' +
  'variables: meta.module-variables(theme-light-inverted),\n' +
  'mixins: meta.module-mixins(theme-light-inverted),\n' +
  '),';

describe('fileGenerator', () => {
  describe('generateOutputFilesByThemes', () => {
    it('should generate output files by themes', async () => {
      const tokens = Array.from(exampleDimensionAndStringTokens.values());
      const sdk = {
        tokens: {
          computeTokensByApplyingThemes: jest.fn().mockReturnValue(tokens),
        },
      };
      const themes = [{ name: 'theme-light' }, { name: 'theme-light-inverted' }];
      const outputFiles = await generateOutputFilesByThemes(
        tokens,
        mappedTokens,
        tokenGroups,
        themes as TokenTheme[],
        sdk as unknown as Supernova,
      );

      expect(outputFiles).toStrictEqual([
        // Global files
        { path: './scss/global-tokens', fileName: '_borders.scss', content: emptyFile },
        { path: './scss/global-tokens', fileName: '_other.scss', content: mockedExpectedResult },
        { path: './scss/global-tokens', fileName: '_radii.scss', content: emptyFile },
        { path: './scss/global-tokens', fileName: '_spacing.scss', content: emptyFile },
        { path: './scss/global-tokens', fileName: '_shadows.scss', content: emptyFile },
        { path: './scss/global-tokens', fileName: '_gradients.scss', content: emptyFile },
        { path: './scss/global-tokens', fileName: '_typography.scss', content: emptyFile },
        { path: './js/global-tokens/', fileName: 'borders.ts', content: emptyFile },
        { path: './js/global-tokens/', fileName: 'other.ts', content: mockedTsFile },
        { path: './js/global-tokens/', fileName: 'radii.ts', content: emptyFile },
        { path: './js/global-tokens/', fileName: 'spacing.ts', content: emptyFile },
        { path: './js/global-tokens/', fileName: 'shadows.ts', content: emptyFile },
        { path: './js/global-tokens/', fileName: 'gradients.ts', content: emptyFile },
        { path: './js/global-tokens/', fileName: 'typography.ts', content: emptyFile },
        // Global barrel files
        { path: './scss/global-tokens/', fileName: 'index.scss', content: barrelFile },
        { path: './js/global-tokens/', fileName: 'index.ts', content: barrelJsFile },
        // Root barrel files
        { path: './scss/', fileName: '@tokens.scss', content: "@forward 'global-tokens';\n@forward 'themes';\n" },
        {
          path: './js/',
          fileName: 'index.ts',
          content: "export * from './global-tokens';\nexport * from './themes';\n",
        },
        // Themes files
        { path: './scss/themes/theme-light/', fileName: '_colors.scss', content: mockedColorTokensFile },
        { path: './js/themes/theme-light/', fileName: 'colors.ts', content: emptyFile },
        { path: './scss/themes/theme-light/', fileName: 'index.scss', content: barrelColorFile },
        { path: './js/themes/theme-light/', fileName: 'index.ts', content: barrelJsColorFile },
        { path: './scss/themes/theme-light-inverted/', fileName: '_colors.scss', content: mockedColorTokensFile },
        { path: './js/themes/theme-light-inverted/', fileName: 'colors.ts', content: emptyFile },
        { path: './scss/themes/theme-light-inverted/', fileName: 'index.scss', content: barrelColorFile },
        { path: './js/themes/theme-light-inverted/', fileName: 'index.ts', content: barrelJsColorFile },
        // Themes root barrel files
        { path: './scss/', fileName: '@themes.scss', content: mockedRootThemeFile },
        { path: './js/themes', fileName: 'index.ts', content: mockedRootThemeJsFile },
        { path: './scss/themes', fileName: 'index.scss', content: barrelColorTokensFile },
        { path: './scss/themes', fileName: '_color-tokens.scss', content: emptyFile },
      ]);
    });
  });

  describe('generateFiles', () => {
    it('should generate files', () => {
      const tokens = Array.from(exampleDimensionAndStringTokens.values());
      const files = generateFiles(tokens, mappedTokens, tokenGroups, nonThemedFilesData, false);

      expect(files).toStrictEqual([
        {
          fileName: 'borders',
          content: emptyFile,
        },
        { fileName: 'other', content: mockedExpectedResult },
        { fileName: 'radii', content: emptyFile },
        { fileName: 'spacing', content: emptyFile },
        { fileName: 'shadows', content: emptyFile },
        { fileName: 'gradients', content: emptyFile },
        { fileName: 'typography', content: emptyFile },
      ]);
    });
  });

  describe('generateBarrelFile', () => {
    const dataProvider = [
      {
        files: [{ fileName: 'borders', content: emptyFile }],
        description: 'should generate barrel file with one file',
        expectedBarrelFile: "@forward 'borders';\n",
      },
      {
        files: [
          { fileName: 'borders', content: emptyFile },
          { fileName: 'other', content: mockedExpectedResult },
        ],
        description: 'should generate barrel file with multiple files',
        expectedBarrelFile: "@forward 'borders';\n@forward 'other';\n",
      },
      {
        files: [
          { fileName: 'borders', content: emptyFile },
          { fileName: 'other', content: mockedExpectedResult },
        ],
        description: 'should generate barrel file with one file with js output',
        hasJsOutput: true,
        expectedBarrelFile: "export * from './borders';\nexport * from './other';\n",
      },
    ];

    it.each(dataProvider)('$description', ({ files, expectedBarrelFile, hasJsOutput }) => {
      const mockedBarrelFile = generateBarrelFile(files, hasJsOutput);

      expect(mockedBarrelFile).toBe(expectedBarrelFile);
    });
  });

  describe('generateThemesRootFile', () => {
    it('should generate themes root file content', () => {
      const themes = [{ name: 'theme-light' }, { name: 'theme-light-inverted' }];
      const content = generateThemesRootFile(themes as TokenTheme[], false);

      expect(content).toBe(mockedRootThemeFile);
    });

    it('should generate themes root file content with js output', () => {
      const themes = [{ name: 'theme-light' }, { name: 'theme-light-inverted' }];
      const content = generateThemesRootFile(themes as TokenTheme[], true);

      expect(content).toBe(mockedRootThemeJsFile);
    });
  });

  describe('generateRootThemesFileContent', () => {
    it('should generate root themes file content', () => {
      const themes = [{ name: 'theme-light' }, { name: 'theme-light-inverted' }];
      const content = generateRootThemesFileContent(themes as TokenTheme[], false);

      expect(content).toBe(mockedThemeRootFileContent);
    });

    it('should generate root themes file content with js output', () => {
      const themes = [{ name: 'theme-light' }, { name: 'theme-light-inverted' }];
      const content = generateRootThemesFileContent(themes as TokenTheme[], true);

      expect(content).toBe(
        // eslint-disable-next-line prettier/prettier, quotes -- special characters in the string
        'themeLight: {\ntokens: themeLight,\n},\nthemeLightInverted: {\ntokens: themeLightInverted,\n},',
      );
    });
  });

  describe('generateRootThemesFileImports', () => {
    it('should generate root themes file imports', () => {
      const themes = [{ name: 'theme-light' }, { name: 'theme-light-inverted' }];
      const content = generateRootThemesFileImports(themes as TokenTheme[], false);

      expect(content).toBe("@use 'themes/theme-light';\n@use 'themes/theme-light-inverted';");
    });

    it('should generate root themes file imports with js output', () => {
      const themes = [{ name: 'theme-light' }, { name: 'theme-light-inverted' }];
      const content = generateRootThemesFileImports(themes as TokenTheme[], true);

      expect(content).toBe(
        "import * as themeLight from './theme-light';\nimport * as themeLightInverted from './theme-light-inverted';",
      );
    });
  });

  describe('jsImportStatement', () => {
    it('should generate js import statement', () => {
      const content = jsImportStatement('theme-light');

      expect(content).toBe("import * as themeLight from './theme-light';");
    });
  });

  describe('scssImportStatement', () => {
    it('should generate scss import statement', () => {
      const content = scssImportStatement('theme-light');

      expect(content).toBe("@use 'themes/theme-light';");
    });
  });
});
