/// <reference types="vitest/config" />
import fs from 'node:fs';
import { fileURLToPath, URL } from 'node:url';

import react from '@vitejs/plugin-react-swc';
import { visualizer } from 'rollup-plugin-visualizer';
import { Connect, defineConfig, Plugin, UserConfig } from 'vite';
import checker from 'vite-plugin-checker';
import compileTime from 'vite-plugin-compile-time';
import { createHtmlPlugin } from 'vite-plugin-html';
import { VitePWA } from 'vite-plugin-pwa';
import { viteStaticCopy } from 'vite-plugin-static-copy';

const { NODE_ENV, PORT } = process.env;

export default defineConfig(() => {
  const config: UserConfig = {
    build: {
      assetsDir: 'packs',
      assetsInlineLimit: 0,
      rollupOptions: {
        output: {
          assetFileNames: 'packs/assets/[name]-[hash].[ext]',
          chunkFileNames: 'packs/js/[name]-[hash].js',
          entryFileNames: 'packs/[name]-[hash].js',
        },
      },
      sourcemap: true,
    },
    assetsInclude: ['**/*.oga'],
    server: {
      port: Number(PORT ?? 3036),
    },
    plugins: [
      checker({ typescript: true }),
      compileTime(),
      createHtmlPlugin({
        template: 'index.html',
        minify: {
          collapseWhitespace: true,
          removeComments: false,
        },
        inject: {
          data: {
            snippets: readFileContents('custom/snippets.html'),
            csp: buildCSP(NODE_ENV),
          },
        },
      }),
      react(),
      VitePWA({
        injectRegister: null,
        strategies: 'injectManifest',
        injectManifest: {
          injectionPoint: undefined,
          plugins: [
            // @ts-ignore
            compileTime(),
          ],
        },
        manifest: {
          name: 'Soapbox',
          short_name: 'Soapbox',
          description: 'A social media frontend with a focus on custom branding and ease of use.',
          theme_color: '#0482d8',
        },
        srcDir: 'src/service-worker',
        filename: 'sw.ts',
      }),
      viteStaticCopy({
        targets: [{
          src: './src/instance',
          dest: '.',
        }, {
          src: './custom/instance',
          dest: '.',
        }],
      }),
      visualizer({
        emitFile: true,
        filename: 'report.html',
        title: 'Soapbox Bundle',
      }) as Plugin,
      {
        // Vite's default behavior is to serve index.html (HTTP 200) for unmatched routes, like a PWA.
        // Instead, 404 on known backend routes to more closely match a real server.
        name: 'vite-mastodon-dev',
        configureServer(server) {
          const notFoundHandler: Connect.SimpleHandleFunction = (_req, res) => {
            res.statusCode = 404;
            res.end();
          };

          server.middlewares.use('/api/', notFoundHandler);
          server.middlewares.use('/oauth/', notFoundHandler);
          server.middlewares.use('/nodeinfo/', notFoundHandler);
          server.middlewares.use('/.well-known/', notFoundHandler);
        },
      },
    ],
    resolve: {
      alias: [
        { find: 'soapbox', replacement: fileURLToPath(new URL('./src', import.meta.url)) },
      ],
    },
    // @ts-ignore
    test: {
      globals: true,
      environment: 'jsdom',
      setupFiles: 'src/jest/test-setup.ts',
    },
  };

  return config;
});

/** Build a sane default CSP string to embed in index.html in case the server doesn't return one. */
/* eslint-disable quotes */
function buildCSP(env: string | undefined): string {
  const csp = [
    "default-src 'none'",
    "script-src 'self' 'wasm-unsafe-eval'",
    "style-src 'self' 'unsafe-inline'",
    "frame-src 'self' https:",
    "font-src 'self'",
    "base-uri 'self'",
    "manifest-src 'self'",
  ];

  if (env === 'development') {
    csp.push(
      "connect-src 'self' blob: https: wss: http://localhost:* http://127.0.0.1:* ws://localhost:* ws://127.0.0.1:*",
      "img-src 'self' data: blob: https: http://localhost:* http://127.0.0.1:*",
      "media-src 'self' https: http://localhost:* http://127.0.0.1:*",
    );
  } else {
    csp.push(
      "connect-src 'self' blob: https: wss:",
      "img-src 'self' data: blob: https:",
      "media-src 'self' https:",
    );
  }

  return csp.join('; ');
}
/* eslint-enable quotes */

/** Return file as string, or return empty string if the file isn't found. */
function readFileContents(path: string) {
  try {
    return fs.readFileSync(path, 'utf8');
  } catch {
    return '';
  }
}
