import { cpSync, existsSync, mkdirSync, readdirSync } from 'fs';
import { join, resolve } from 'path';
import { fileURLToPath } from 'url';
import { defineConfig, type ResolvedConfig, type ViteDevServer } from 'vite';
import react from '@vitejs/plugin-react';
import { VitePWA } from 'vite-plugin-pwa';

const DEV_APP_UPDATE_PATH = '/__app-update';
const processEnv =
  (
    globalThis as typeof globalThis & {
      process?: {
        env?: Record<string, string | undefined>;
      };
    }
  ).process?.env ?? {};
const VITE_PUBLIC_HMR_HOST = processEnv.VITE_PUBLIC_HMR_HOST ?? 'test.sm-home.cloud';
const VITE_PUBLIC_HMR_PROTOCOL = processEnv.VITE_PUBLIC_HMR_PROTOCOL ?? 'wss';
const VITE_PUBLIC_HMR_CLIENT_PORT = Number(processEnv.VITE_PUBLIC_HMR_CLIENT_PORT ?? 443);
const VITE_EMPTY_OUT_DIR = processEnv.VITE_EMPTY_OUT_DIR !== 'false';
const VITE_FILTER_PUBLIC_DIR = processEnv.VITE_FILTER_PUBLIC_DIR === 'true';
const VITE_DISABLE_MODULE_PRELOAD = processEnv.VITE_DISABLE_MODULE_PRELOAD === 'true';
const VITE_DISABLE_PWA = processEnv.VITE_DISABLE_PWA === 'true';
const WORK_SERVER_HTTP_TARGET = processEnv.WORK_SERVER_URL?.trim() || 'http://work-server:3100';
const WORK_SERVER_WS_TARGET = (() => {
  try {
    const parsed = new URL(WORK_SERVER_HTTP_TARGET);
    parsed.protocol = parsed.protocol === 'https:' ? 'wss:' : 'ws:';
    return parsed.toString();
  } catch {
    return WORK_SERVER_HTTP_TARGET.replace(/^http/i, 'ws');
  }
})();
const ROOT_DIR = fileURLToPath(new URL('.', import.meta.url));

function shouldIgnoreDevUpdatePath(watchedPath: string) {
  return (
    watchedPath.includes('/.auto_codex/') ||
    watchedPath.includes('/dist/') ||
    watchedPath.includes('/app-dist/') ||
    watchedPath.includes('/test-app-dist/') ||
    watchedPath.includes('/coverage/') ||
    watchedPath.includes('/node_modules/') ||
    watchedPath.includes('/.docker/') ||
    watchedPath.includes('/.npm/') ||
    watchedPath.includes('/_cacache/') ||
    watchedPath.includes('/.tmp/') ||
    watchedPath.includes('/.codex/') ||
    watchedPath.includes('/vendor_imports/') ||
    watchedPath.includes('/etc/servers/') ||
    watchedPath.includes('/public/.codex_chat/') ||
    watchedPath.endsWith('/.server-command-runner-heartbeat.json')
  );
}

function createDevAppUpdatePlugin() {
  let updateToken = `${Date.now()}`;

  const bumpUpdateToken = () => {
    updateToken = `${Date.now()}`;
  };

  const shouldTrackPath = (watchedPath: string) => !shouldIgnoreDevUpdatePath(watchedPath);

  return {
    name: 'dev-app-update-probe',
    apply: 'serve' as const,
    configureServer(server: ViteDevServer) {
      const handleFileChange = (file: string) => {
        if (shouldTrackPath(file)) {
          bumpUpdateToken();
        }
      };

      server.watcher.on('add', handleFileChange);
      server.watcher.on('change', handleFileChange);
      server.watcher.on('unlink', handleFileChange);

      server.middlewares.use(DEV_APP_UPDATE_PATH, (_request, response) => {
        response.setHeader('Content-Type', 'application/json; charset=utf-8');
        response.setHeader('Cache-Control', 'no-store');
        response.end(
          JSON.stringify({
            token: updateToken,
            updatedAt: new Date(Number(updateToken)).toISOString(),
          }),
        );
      });
    },
  };
}

function createDevServiceWorkerPlugin() {
  return {
    name: 'dev-service-worker-entry',
    apply: 'serve' as const,
    configureServer(server: ViteDevServer) {
      server.middlewares.use((request, response, next) => {
        const requestUrl = request.url?.trim() ?? '';
        const requestPath = requestUrl.split('?', 1)[0];

        if (requestPath !== '/sw.js' && requestPath !== '/dev-sw.js') {
          next();
          return;
        }

        void server
          .transformRequest('/src/sw.js')
          .then((result) => {
            if (!result?.code) {
              response.statusCode = 404;
              response.setHeader('Content-Type', 'text/plain; charset=utf-8');
              response.end('Not Found');
              return;
            }

            response.statusCode = 200;
            response.setHeader('Content-Type', 'application/javascript; charset=utf-8');
            response.setHeader('Cache-Control', 'no-store');
            response.setHeader('Service-Worker-Allowed', '/');
            response.end(result.code);
          })
          .catch(next);
      });
    },
  };
}

function copyPublicAssetsExceptCodexChat() {
  let resolvedConfig: ResolvedConfig | null = null;

  const copyDirectory = (sourceDir: string, targetDir: string) => {
    if (!existsSync(sourceDir)) {
      return;
    }

    mkdirSync(targetDir, { recursive: true });

    for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
      if (entry.name === '.codex_chat') {
        continue;
      }

      const sourcePath = join(sourceDir, entry.name);
      const targetPath = join(targetDir, entry.name);

      if (entry.isDirectory()) {
        copyDirectory(sourcePath, targetPath);
        continue;
      }

      if (entry.isFile()) {
        mkdirSync(join(targetPath, '..'), { recursive: true });
        cpSync(sourcePath, targetPath, { force: true });
      }
    }
  };

  return {
    name: 'copy-public-assets-except-codex-chat',
    apply: 'build' as const,
    configResolved(config: ResolvedConfig) {
      resolvedConfig = config;
    },
    closeBundle() {
      if (!VITE_FILTER_PUBLIC_DIR || !resolvedConfig) {
        return;
      }

      const publicDir = join(resolvedConfig.root, 'public');
      const outDir = resolvedConfig.build.outDir.startsWith('/')
        ? resolvedConfig.build.outDir
        : join(resolvedConfig.root, resolvedConfig.build.outDir);

      copyDirectory(publicDir, outDir);
    },
  };
}

export default defineConfig({
  resolve: {
    extensions: ['.mjs', '.ts', '.tsx', '.js', '.jsx', '.mts', '.json'],
    alias: VITE_DISABLE_PWA
      ? {
          'virtual:pwa-register': resolve(ROOT_DIR, 'src/app/main/pwaRegisterStub.ts'),
        }
      : undefined,
  },
  build: {
    copyPublicDir: !VITE_FILTER_PUBLIC_DIR,
    emptyOutDir: VITE_EMPTY_OUT_DIR,
    modulePreload: VITE_DISABLE_MODULE_PRELOAD ? false : undefined,
  },
  server: {
    host: '0.0.0.0',
    port: 5173,
    hmr: {
      host: VITE_PUBLIC_HMR_HOST,
      protocol: VITE_PUBLIC_HMR_PROTOCOL as 'ws' | 'wss',
      clientPort: VITE_PUBLIC_HMR_CLIENT_PORT,
    },
    allowedHosts: ['sm-home.cloud', 'test.sm-home.cloud', 'preview.sm-home.cloud', 'rel.sm-home.cloud'],
    watch: {
      ignored: (watchedPath) => shouldIgnoreDevUpdatePath(watchedPath),
    },
    proxy: {
      '/api': {
        target: WORK_SERVER_HTTP_TARGET,
        changeOrigin: true,
      },
      '/ws/chat': {
        target: WORK_SERVER_WS_TARGET,
        ws: true,
        changeOrigin: true,
      },
      '/.codex_chat': {
        target: WORK_SERVER_HTTP_TARGET,
        changeOrigin: true,
      },
      '/public/.codex_chat': {
        target: WORK_SERVER_HTTP_TARGET,
        changeOrigin: true,
      },
    },
  },
  preview: {
    host: '0.0.0.0',
    port: 5173,
    allowedHosts: ['sm-home.cloud', 'test.sm-home.cloud', 'preview.sm-home.cloud', 'rel.sm-home.cloud'],
  },
  plugins: [
    react(),
    createDevAppUpdatePlugin(),
    createDevServiceWorkerPlugin(),
    copyPublicAssetsExceptCodexChat(),
    !VITE_DISABLE_PWA &&
      VitePWA({
        injectRegister: null,
        strategies: 'injectManifest',
        srcDir: 'src',
        filename: 'sw.js',
        registerType: 'prompt',
        includeAssets: ['favicon.svg', 'apple-touch-icon.svg'],
        workbox: {
          globPatterns: ['**/*.{js,css,html,ico,svg,woff2,webmanifest}'],
          maximumFileSizeToCacheInBytes: 8 * 1024 * 1024,
        },
        injectManifest: {
          globIgnores: ['**/.codex_chat/**', '**/*.png', '**/*.webp'],
          maximumFileSizeToCacheInBytes: 8 * 1024 * 1024,
        },
        manifest: {
          id: '/',
          name: 'AI Code App',
          short_name: 'AI Code App',
          description: 'Ant Design 기반 UI 샘플과 문서를 확인하는 AI Code App',
          theme_color: '#165dff',
          background_color: '#eff5ff',
          display: 'standalone',
          lang: 'ko',
          scope: '/',
          start_url: '/',
          icons: [
            {
              src: '/pwa-192x192.svg',
              sizes: '192x192',
              type: 'image/svg+xml',
            },
            {
              src: '/pwa-512x512.svg',
              sizes: '512x512',
              type: 'image/svg+xml',
              purpose: 'any maskable',
            },
          ],
          shortcuts: [
            {
              name: 'E-Reader',
              short_name: 'E-Reader',
              description: '인터넷 기사와 웹 콘텐츠를 전자책처럼 읽습니다.',
              url: '/play/apps?app=e-reader',
              icons: [
                {
                  src: '/pwa-192x192.svg',
                  sizes: '192x192',
                  type: 'image/svg+xml',
                },
              ],
            },
          ],
        },
        devOptions: {
          enabled: false,
        },
      }),
  ].filter(Boolean),
});
