/// <reference types="vitest" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { TanStackRouterVite } from '@tanstack/router-vite-plugin'
import path from 'path'
import { execSync } from 'node:child_process'
import { readFileSync } from 'node:fs'

// Build identity injected at compile time. Used by VersionBadge / UpdateBanner.
//
// Priority chain (most authoritative first):
//   1. CI_COMMIT_TAG  → set ONLY on tag pipelines (e.g. "v1.3.1"). Это source
//      of truth для release builds. Strip leading 'v' → "1.3.1".
//   2. package.json.version → fallback floor для local dev и branch builds.
//      НЕ обновляется вручную — release flow добавит автоматический bump
//      позже (release script tag + npm version в одном commit'е).
//
// __APP_TAG__ — пустая строка если не tag build. VersionBadge решает что
// показать (приоритет: serverTag → clientTag → v${clientVersion}).
//
// __APP_COMMIT__ — 8-char short SHA. CI передаёт через CI_COMMIT_SHORT_SHA
// (GitLab default 8 chars), local через `git rev-parse --short=8`.
const APP_VERSION: string = JSON.parse(
  readFileSync(path.resolve(__dirname, 'package.json'), 'utf-8'),
).version
// 'none' — Dockerfile default ARG value (см. .gitlab-ci.yml passes
// --build-arg GIT_TAG="${CI_COMMIT_TAG:-none}"). Filter → пустая строка.
const RAW_TAG = process.env.CI_COMMIT_TAG ?? ''
const APP_TAG: string = RAW_TAG === 'none' ? '' : RAW_TAG.replace(/^v/, '')
// Distinguish "CI branch build" (badge → 'dev') vs "local pnpm dev" (→ 'local').
// Canonical CI signal — наличие CI_COMMIT_SHORT_SHA (GitLab CI always sets).
// process.env.CI тоже check'аем но он НЕ пробрасывается через Docker ARG'и
// (см. ordinis-admin-ui/Dockerfile), поэтому SHORT_SHA — primary detect.
const APP_CI: boolean =
  Boolean(process.env.CI_COMMIT_SHORT_SHA) || process.env.CI === 'true'
const APP_COMMIT: string =
  process.env.CI_COMMIT_SHORT_SHA ??
  (() => {
    try {
      return execSync('git rev-parse --short=8 HEAD', { stdio: ['ignore', 'pipe', 'ignore'] })
        .toString()
        .trim()
    } catch {
      return 'unknown'
    }
  })()
const APP_BUILT_AT: string = new Date().toISOString()

export default defineConfig({
  define: {
    __APP_VERSION__: JSON.stringify(APP_VERSION),
    __APP_TAG__: JSON.stringify(APP_TAG),
    __APP_CI__: JSON.stringify(APP_CI),
    __APP_COMMIT__: JSON.stringify(APP_COMMIT),
    __APP_BUILT_AT__: JSON.stringify(APP_BUILT_AT),
  },
  test: {
    environment: 'jsdom',
    setupFiles: ['./src/test/setup.ts'],
    globals: true,
    css: false,
  },
  plugins: [
    TanStackRouterVite({ routesDirectory: 'src/routes', generatedRouteTree: 'src/routeTree.gen.ts' }),
    react(),
    tailwindcss(),
  ],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
  server: {
    host: '0.0.0.0',
    port: 5173,
    proxy: {
      '/api': {
        target: process.env.VITE_API_PROXY ?? 'http://localhost:8080',
        changeOrigin: true,
        // secure: false нужен когда target — HTTPS endpoint с self-signed
        // сертификатом (port-forward с локальным CA или внутренний cluster
        // service). Для prod/staging Let's Encrypt cert validation не нужно
        // отключать но игнор не вредит для dev mode.
        secure: false,
        // ws: true — на будущее когда Server-Sent Events / WebSocket каналы
        // появятся (real-time notifications, audit stream).
        ws: true,
      },
    },
  },
  build: {
    chunkSizeWarningLimit: 600,
    rollupOptions: {
      output: {
        // Группировка по принципу: что меняется редко → отдельный chunk →
        // браузер кэширует надолго. Что меняется часто (наш код) — в main.
        manualChunks: (id) => {
          if (!id.includes('node_modules')) return undefined

          // React core — стабильный, основа всего
          if (id.match(/node_modules\/(react|react-dom|scheduler)\//)) return 'vendor-react'

          // TanStack — Router + Query, крупные но стабильные
          if (id.includes('node_modules/@tanstack/')) return 'vendor-tanstack'

          // @nstart/ui — самый тяжёлый: brand stickers/animations + tons of UI
          // (проверяй динамически: lottie, echarts если есть, motion)
          if (id.includes('node_modules/@nstart/ui')) return 'vendor-nstart-ui'

          // Иконки — отдельно, ленивая загрузка эффективна
          if (id.includes('node_modules/@phosphor-icons/')) return 'vendor-icons'

          // Формы + JSON Schema валидация
          if (id.match(/node_modules\/(react-hook-form|@hookform|ajv|ajv-formats)\//)) {
            return 'vendor-forms'
          }

          // OIDC / auth
          if (id.match(/node_modules\/(oidc-client-ts|react-oidc-context)\//)) {
            return 'vendor-auth'
          }

          // i18next — отдельно
          if (id.match(/node_modules\/(i18next|react-i18next|i18next-browser-languagedetector)\//)) {
            return 'vendor-i18n'
          }

          // Axios + парсеры
          if (id.match(/node_modules\/(axios|jose|crypto-js)\//)) return 'vendor-net'

          // Monaco editor — ~1.2MB gzip. Lazy через React.lazy в JsonEditor.tsx;
          // separate chunk даёт on-demand загрузку только когда CreateModal
          // открыт. Без isolated chunk Monaco попадал бы в vendor-misc
          // (главная страница). Аналог graph3d HOTFIX'а ниже — НО Monaco
          // не зависит от d3 utils, поэтому circular chunk риска нет.
          if (id.match(/node_modules\/(@monaco-editor|monaco-editor)\//)) {
            return 'vendor-monaco'
          }

          // react-day-picker — ~80kb gz. DateRangePicker лениво загружает
          // через React.lazy (используется только на audit/filtering pages).
          // Isolated chunk → on-demand fetch без trip в main bundle.
          // date-fns тоже сюда — day-picker dep, не используется elsewhere.
          if (id.match(/node_modules\/(react-day-picker|date-fns)\//)) {
            return 'vendor-day-picker'
          }

          // HOTFIX: graph3d isolated chunk был причиной runtime "Super
          // expression must either be null or a function" (cyclic chunk
          // dependency vendor-misc ↔ vendor-graph3d ломает порядок
          // initialization three.js class inheritance). Откатываем
          // isolation — three.js + d3-shared идут в общий vendor-misc.
          //
          // Trade-off: vendor-misc вырастет до ~700kb gz (с three.js),
          // default page load грузит three.js eager. Lazy advantage 3D
          // mode потерян. Это acceptable hotfix чтобы вернуть сайт; clean
          // chunk split с separate worker bundle / preload strategy —
          // separate follow-up. См. также: React.lazy на DictionaryGraph3D
          // остаётся валидным (component split), просто его vendor deps
          // теперь в общем chunk.
          return 'vendor-misc'
        },
      },
    },
  },
})
