import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { fileURLToPath } from 'node:url'

const appShell = readFileSync(
  fileURLToPath(new URL('../src/views/AppShellRouteView.vue', import.meta.url)),
  'utf8'
)

const aiSidebar = readFileSync(
  fileURLToPath(new URL('../src/components/layout/AiSidebarRail.vue', import.meta.url)),
  'utf8'
)

const aiSidebarStyles = readFileSync(
  fileURLToPath(new URL('../src/assets/styles/components/ai-sidebar-rail.css', import.meta.url)),
  'utf8'
)

const aiBusinessAccess = readFileSync(
  fileURLToPath(new URL('../src/utils/aiSidebarBusinessAccess.js', import.meta.url)),
  'utf8'
)

const appStyles = readFileSync(
  fileURLToPath(new URL('../src/assets/styles/app.css', import.meta.url)),
  'utf8'
)

const extractCssBlock = (source, selector) => source.match(new RegExp(`${selector}\\s*\\{([\\s\\S]*?)\\n\\}`))?.[1] || ''

test('workbench AI mode swaps the traditional rail for the AI three-layer rail', () => {
  assert.match(appShell, /import AiSidebarRail from '\.\.\/components\/layout\/AiSidebarRail\.vue'/)
  assert.match(appShell, /<Transition[\s\S]*name="sidebar-mode-fade"[\s\S]*mode="out-in"/)
  assert.match(appShell, /<AiSidebarRail[\s\S]*v-if="isAiShellMode"[\s\S]*key="ai-sidebar"/)
  assert.match(appShell, /const isAiShellMode = computed\(\(\) => workbenchMode\.value === 'ai'\)/)
  assert.match(appShell, /const isWorkbenchAiMode = computed\(\(\) => activeView\.value === 'workbench' && workbenchMode\.value === 'ai'\)/)
  assert.match(appShell, /@new-chat="openAiSidebarNewChat"/)
  assert.match(appShell, /@open-recent="openAiSidebarRecent"/)
  assert.match(appShell, /@rename-conversation="handleAiConversationRename"/)
  assert.match(appShell, /@logout="handleLogout"/)
  assert.match(appShell, /import \{ loadAiWorkbenchConversationHistory, saveAiWorkbenchConversation \} from '\.\.\/utils\/aiWorkbenchConversationStore\.js'/)
  assert.match(appShell, /function openAiSidebarNewChat\(\)/)
  assert.match(appShell, /function openAiSidebarRecent\(item = \{\}\)/)
  assert.match(appShell, /function handleAiConversationRename\(payload = \{\}\)/)
  assert.match(appShell, /import \{ computed, nextTick, ref, watch \} from 'vue'/)
  assert.match(appShell, /async function openAiConversationWorkspace\(type, payload = null\)/)
  assert.match(appShell, /const navigation = handleNavigate\('workbench'\)/)
  assert.match(appShell, /if \(navigation && typeof navigation\.then === 'function'\)[\s\S]*await navigation/)
  assert.match(appShell, /await nextTick\(\)/)
  assert.match(appShell, /dispatchAiSidebarCommand\(type, payload\)/)
  assert.match(appShell, /void openAiConversationWorkspace\('new-chat'\)/)
  assert.match(appShell, /void openAiConversationWorkspace\('open-recent', item\)/)
  assert.doesNotMatch(appShell, /openAiSidebarSearchChat/)
  assert.match(appShell, /const aiSidebarCommand = ref\(\{ seq: 0, type: '', payload: null \}\)/)
  assert.match(appShell, /const aiConversationHistory = ref\(\[\]\)/)
  assert.match(appShell, /:active-conversation-id="aiActiveConversationId"/)
  assert.match(appShell, /:conversation-history="aiConversationHistory"/)
  assert.match(appShell, /:brand-name="PRODUCT_DISPLAY_NAME"/)
  assert.match(appShell, /:brand-logo="companyProfile\.logo"/)
  assert.match(appShell, /:company-name="ENTERPRISE_DISPLAY_NAME"/)
  assert.match(appShell, /:ai-sidebar-command="aiSidebarCommand"/)
  assert.match(appShell, /@ai-conversation-change="handleAiConversationChange"/)
  assert.match(appShell, /@ai-conversation-history-change="handleAiConversationHistoryChange"/)
  assert.match(appShell, /function dispatchAiSidebarCommand\(type, payload = null\)/)
  assert.match(appShell, /function handleAiConversationHistoryChange\(payload = \[\]\)/)
  assert.match(appShell, /loadAiWorkbenchConversationHistory\(user \|\| \{\}\)/)
  assert.match(appShell, /saveAiWorkbenchConversation\(currentUser\.value \|\| \{\},[\s\S]*\.\.\.target,[\s\S]*title/)
  assert.match(appShell, /:current-user="currentUser"/)
  assert.doesNotMatch(appShell, /restoreLatestConversation:\s*true/)
  assert.match(appShell, /'workbench-ai-sidebar-active': isAiShellMode/)
  assert.match(appShell, /sidebarCollapsedBeforeAiMode\.value = sidebarCollapsed\.value/)
  assert.match(appShell, /sidebarCollapsed\.value = false/)
  assert.match(appShell, /sidebarCollapsed\.value = sidebarCollapsedBeforeAiMode\.value/)
  assert.match(appStyles, /\.app\s*\{[\s\S]*--sidebar-expanded-width:\s*304px;/)
  assert.match(appStyles, /\.sidebar-mode-fade-enter-active,[\s\S]*\.sidebar-mode-fade-leave-active\s*\{[\s\S]*opacity 180ms/)
})

test('AI sidebar has quick actions, business navigation and conversation history layers', () => {
  assert.match(aiSidebar, /aria-label="AI模式导航"/)
  assert.match(aiSidebar, /class="ai-rail-brand"/)
  assert.match(aiSidebar, /aria-label="当前产品标识"/)
  assert.match(aiSidebar, /displayBrandName/)
  assert.match(aiSidebar, /brandName:\s*\{\s*type:\s*String/)
  assert.match(aiSidebar, /brandLogo:\s*\{\s*type:\s*String/)
  assert.match(aiSidebar, /String\(props\.brandName \|\| '易财费控'\)/)
  assert.doesNotMatch(aiSidebar, /远光软件股份有限公司/)
  assert.doesNotMatch(aiSidebar, /AI Workbench/)
  assert.match(aiSidebar, /aria-label="对话操作"/)
  assert.match(aiSidebar, /新建对话/)
  assert.match(aiSidebar, /查询对话/)
  assert.doesNotMatch(aiSidebar, /自定义/)
  assert.doesNotMatch(aiSidebar, /业务工作舱/)
  assert.match(aiSidebar, /resolveAiSidebarBusinessViewIds/)
  assert.match(aiSidebar, /\.filter\(\(item\) => aiBusinessViewIds\.value\.has\(item\.id\)\)/)
  assert.match(aiSidebar, /class="ai-nav-list"/)
  assert.match(aiSidebar, /v-for="item in businessNavItems"/)
  assert.match(aiSidebar, /ai-nav-copy/)
  assert.match(aiSidebar, /item\.aiIconPaths/)
  assert.match(aiSidebar, /aria-current/)
  assert.doesNotMatch(aiSidebar, /displayHint/)
  assert.doesNotMatch(aiSidebar, /个人工作台/)
  assert.doesNotMatch(aiSidebar, /待办与助手/)
  assert.doesNotMatch(aiSidebar, /v-html="item\.icon"/)
  assert.match(aiSidebar, /最近对话/)
  assert.match(aiSidebar, /conversationHistory:\s*\{ type:\s*Array,\s*default:\s*\(\) => \[\] \}/)
  assert.match(aiSidebar, /const conversationSearchOpen = ref\(false\)/)
  assert.match(aiSidebar, /const conversationSearchQuery = ref\(''\)/)
  assert.match(aiSidebar, /function openConversationSearch\(\)/)
  assert.match(aiSidebar, /<template[\s\S]*v-for="action in quickActions"[\s\S]*:key="action\.event"/)
  assert.match(aiSidebar, /v-if="action\.event === 'search' && conversationSearchOpen"[\s\S]*class="ai-conversation-search"/)
  assert.match(aiSidebar, /v-else[\s\S]*class="ai-quick-btn"/)
  assert.match(aiSidebar, /placeholder="搜索对话标题"/)
  assert.match(aiSidebar, /v-model="conversationSearchQuery"/)
  assert.doesNotMatch(aiSidebar, /<\/button>\s*<label v-if="conversationSearchOpen" class="ai-conversation-search"/)
  assert.match(aiSidebar, /filteredConversationHistory/)
  assert.match(aiSidebar, /String\(recent\.title \|\| ''\)\.toLowerCase\(\)\.includes\(query\)/)
  assert.match(aiSidebar, /normalizedConversationHistory/)
  assert.match(aiSidebar, /v-for="recent in filteredConversationHistory"/)
  assert.match(aiSidebar, /activeConversationId === recent\.id/)
  assert.match(aiSidebar, /ai-recent-main/)
  assert.match(aiSidebar, /emit\('open-recent', recent\)/)
  assert.match(aiSidebar, /@dblclick\.stop="startEditingRecentTitle\(recent\)"/)
  assert.match(aiSidebar, /v-if="editingConversationId === recent\.id"/)
  assert.match(aiSidebar, /v-model="editingConversationTitle"/)
  assert.match(aiSidebar, /@keydown\.enter\.prevent="commitRecentTitleEdit\(recent\)"/)
  assert.match(aiSidebar, /emit\('rename-conversation'/)
  assert.match(aiSidebar, /暂无历史对话/)
  assert.match(aiSidebar, /没有匹配的对话/)
  assert.doesNotMatch(aiSidebar, /差旅报销口径核对/)
  assert.doesNotMatch(aiSidebar, /预算占用预警分析/)
  assert.doesNotMatch(aiSidebar, /票据识别结果复核/)
  assert.match(aiSidebar, /aria-label="当前用户"/)
  assert.match(aiSidebar, /displayUser\.avatar/)
  assert.match(aiSidebar, /displayUser\.subtitle/)
  assert.match(aiSidebar, /aria-label="用户操作"/)
  assert.match(aiSidebar, /emit\('logout'\)/)
  assert.match(aiSidebar, /defineEmits\(\['navigate', 'new-chat', 'open-recent', 'rename-conversation', 'logout', 'prefetch-view'\]\)/)
  assert.doesNotMatch(aiSidebar, /search-chat/)
  assert.doesNotMatch(aiSidebar, /打开系统设置/)
  assert.doesNotMatch(aiSidebar, /mdi-chevron-up/)
  assert.doesNotMatch(aiSidebar, /mockRecents/)
})

test('AI sidebar visual treatment keeps a restrained three-layer workspace', () => {
  const quickButtonBlock = extractCssBlock(aiSidebarStyles, '\\.ai-quick-btn')
  const quickPrimaryBlock = extractCssBlock(aiSidebarStyles, '\\.ai-quick-btn\\.primary')
  const navListBlock = extractCssBlock(aiSidebarStyles, '\\.ai-nav-list')

  assert.match(aiSidebarStyles, /--ai-rail-bg:\s*#f7f9fc;/)
  assert.match(aiSidebarStyles, /\.ai-rail\s*\{[\s\S]*grid-template-rows:\s*auto auto auto auto auto minmax\(0,\s*1fr\) auto;/)
  assert.match(aiSidebarStyles, /\.ai-rail::before\s*\{[\s\S]*repeating-linear-gradient/)
  assert.match(aiSidebarStyles, /\.ai-rail-brand\s*\{[\s\S]*min-height:\s*74px;[\s\S]*grid-template-columns:\s*42px minmax\(0,\s*1fr\);/)
  assert.match(aiSidebarStyles, /\.ai-brand-logo\s*\{[\s\S]*width:\s*42px;[\s\S]*height:\s*42px;/)
  assert.match(aiSidebarStyles, /\.ai-brand-logo svg\s*\{[\s\S]*width:\s*26px;[\s\S]*height:\s*26px;/)
  assert.match(aiSidebar, /const tablerIconPaths = \{/)
  assert.match(aiSidebar, /plus:\s*\[/)
  assert.match(aiSidebar, /search:\s*\[/)
  assert.match(aiSidebar, /fileText:\s*\[/)
  assert.match(aiSidebar, /book2:\s*\[/)
  assert.match(aiSidebar, /iconPaths:\s*tablerIconPaths\.plus/)
  assert.match(aiSidebar, /aiIconPaths:\s*sidebarMeta\[item\.id\]\?\.iconPaths/)
  assert.doesNotMatch(aiSidebar, /icon:\s*'mdi mdi-plus'/)
  assert.doesNotMatch(aiSidebar, /mdi mdi-file-document-outline/)
  assert.match(aiSidebarStyles, /\.ai-sidebar-tabler-icon\s*\{[\s\S]*stroke-width:\s*1\.85;/)
  assert.match(aiSidebarStyles, /\.ai-rail-quick\s*\{[\s\S]*gap:\s*6px;[\s\S]*padding:\s*8px 18px 12px;/)
  assert.match(quickButtonBlock, /min-height:\s*48px;/)
  assert.match(quickButtonBlock, /grid-template-columns:\s*32px minmax\(0,\s*1fr\);/)
  assert.match(quickButtonBlock, /gap:\s*12px;/)
  assert.match(quickButtonBlock, /padding:\s*7px 10px;/)
  assert.match(quickButtonBlock, /background:\s*transparent;/)
  assert.match(quickButtonBlock, /border-color:\s*transparent;/)
  assert.match(quickButtonBlock, /box-shadow:\s*none;/)
  assert.match(quickPrimaryBlock, /background:\s*transparent;/)
  assert.match(quickPrimaryBlock, /box-shadow:\s*none;/)
  assert.doesNotMatch(quickButtonBlock, /rgba\(255,\s*255,\s*255/)
  assert.match(navListBlock, /gap:\s*6px;/)
  assert.doesNotMatch(navListBlock, /grid-template-columns:\s*repeat\(2/)
  assert.doesNotMatch(navListBlock, /border-radius:/)
  assert.doesNotMatch(navListBlock, /box-shadow:/)
  assert.match(aiSidebarStyles, /\.ai-nav-btn\s*\{[\s\S]*min-height:\s*48px;/)
  assert.match(aiSidebarStyles, /\.ai-nav-btn\s*\{[\s\S]*grid-template-columns:\s*32px minmax\(0,\s*1fr\);/)
  assert.match(aiSidebarStyles, /\.ai-quick-btn:hover,\s*\.ai-quick-btn\.active,\s*\.ai-nav-btn:hover,\s*\.ai-nav-btn\.active\s*\{[\s\S]*background:\s*rgba\(15,\s*23,\s*42,\s*0\.035\);/)
  assert.doesNotMatch(aiSidebarStyles, /\.ai-nav-btn::before/)
  assert.doesNotMatch(aiSidebarStyles, /\.ai-nav-btn\.active::before/)
  assert.doesNotMatch(aiSidebarStyles, /\.ai-nav-btn\.active \.ai-nav-copy/)
  assert.doesNotMatch(aiSidebarStyles, /\.ai-rail\.rail-collapsed \.ai-nav-btn\.active/)
  assert.doesNotMatch(aiSidebarStyles, /linear-gradient\(90deg,\s*rgba\(45,\s*114,\s*217,\s*0\.095\)/)
  assert.match(aiSidebarStyles, /\.ai-rail\.rail-collapsed \.ai-nav-list\s*\{[\s\S]*grid-template-columns:\s*1fr;/)
  assert.match(aiSidebarStyles, /\.ai-rail-recents\s*\{[\s\S]*grid-template-rows:\s*auto minmax\(0,\s*1fr\);/)
  assert.match(aiSidebarStyles, /\.ai-recents-list\s*\{[\s\S]*overflow-y:\s*auto;[\s\S]*scrollbar-width:\s*none;/)
  assert.match(aiSidebarStyles, /\.ai-recents-list::-webkit-scrollbar\s*\{[\s\S]*display:\s*none;/)
  assert.match(aiSidebarStyles, /\.ai-recents-empty\s*\{[\s\S]*border:\s*1px dashed/)
  assert.match(aiSidebarStyles, /\.ai-recent-item\s*\{[\s\S]*min-height:\s*56px;/)
  assert.match(aiSidebarStyles, /\.ai-rail-user\s*\{[\s\S]*height:\s*72px;/)
  assert.match(aiSidebarStyles, /\.ai-rail-user\s*\{[\s\S]*border-radius:\s*0;/)
  assert.match(aiSidebarStyles, /\.ai-user-actions\s*\{[\s\S]*grid-template-columns:\s*44px;/)
  assert.match(aiSidebarStyles, /\.ai-rail\.rail-collapsed \.ai-rail-recents,/)
  assert.match(aiSidebarStyles, /\.ai-rail\.rail-collapsed \.ai-user-copy,/)
  assert.match(aiSidebarStyles, /\.ai-rail\.rail-collapsed \.ai-user-actions\s*\{[\s\S]*display:\s*none;/)
})

test('AI sidebar business layer is role scoped for Xiaocai steward mode', () => {
  assert.match(aiBusinessAccess, /AI_SIDEBAR_BASE_BUSINESS_VIEW_IDS = \['documents', 'receiptFolder', 'policies'\]/)
  assert.match(aiBusinessAccess, /ROLE_VIEW_ADDITIONS = \{[\s\S]*budget:\s*\['budget'\]/)
  assert.match(aiBusinessAccess, /roleCodeSet\.has\('budget_monitor'\)[\s\S]*ROLE_VIEW_ADDITIONS\.budget/)
  assert.doesNotMatch(aiBusinessAccess, /'workbench'/)
})
