import test from "node:test"
import assert from "node:assert/strict"
import { readFileSync } from "node:fs"

const settingsPanelsSource = readFileSync(new URL("../src/features/settings/SettingsPanels.tsx", import.meta.url), "utf8")
const settingsStylesSource = readFileSync(new URL("../src/styles/settings.css", import.meta.url), "utf8")

test("settings panels remove per-module status chips", () => {
  assert.doesNotMatch(settingsPanelsSource, /StatusChip/)
  assert.doesNotMatch(settingsPanelsSource, /已配置/)
  assert.doesNotMatch(settingsPanelsSource, /未配置/)
})

test("settings page buttons use the unified action button classes", () => {
  assert.match(settingsPanelsSource, /settings-action-button settings-action-button-secondary/)
  assert.match(settingsPanelsSource, /settings-action-button settings-action-button-primary/)
  assert.doesNotMatch(settingsPanelsSource, /settings-card-action-button/)
  assert.doesNotMatch(settingsPanelsSource, /settings-secondary-button/)
  assert.doesNotMatch(settingsStylesSource, /\.settings-panel \.status-chip/)
  assert.doesNotMatch(settingsStylesSource, /\.settings-card-action-button/)
  assert.match(settingsStylesSource, /\.settings-action-button\b/)
  assert.match(settingsStylesSource, /\.settings-actions-row\b/)
})

test("basic config action buttons share the same width rule", () => {
  assert.match(
    settingsStylesSource,
    /\.settings-inline-save-button,\s*\n\.settings-basic-directory-actions \.settings-action-button\s*\{\s*\n\s*width:\s*96px;\s*\n\s*min-width:\s*96px;/m
  )
})

test("workspace directory keeps only export diagnostics and change directory actions stacked", () => {
  assert.doesNotMatch(settingsPanelsSource, />\s*恢复当前\s*</)
  assert.doesNotMatch(settingsPanelsSource, />\s*保存目录\s*</)
  assert.match(settingsPanelsSource, /onClick=\{\(\) => void exportDiagnostics\(\)\}/)
  assert.match(settingsPanelsSource, /onClick=\{\(\) => void pickWorkspaceDirectory\(\)\}/)
  assert.match(
    settingsStylesSource,
    /\.settings-basic-directory-actions\s*\{[\s\S]*?flex-direction:\s*column;[\s\S]*?align-items:\s*stretch;/m
  )
})

test("xhs feishu actions are anchored inside the input block", () => {
  assert.match(settingsPanelsSource, /className="settings-xhs-feishu-form"/)
  assert.match(settingsPanelsSource, /renderActions\(\{\s*hasPending:\s*hasPendingXhsFeishuConfig,\s*onReset:\s*\(\)\s*=>\s*void onResetXhsFeishuConfig\(\),\s*onSave:\s*\(\)\s*=>\s*void onSaveXhsFeishuConfig\(\),\s*className:\s*"settings-xhs-feishu-actions"/m)
  assert.match(
    settingsStylesSource,
    /\.settings-xhs-feishu-form\s*\{[\s\S]*?display:\s*grid;[\s\S]*?gap:\s*8px;/m
  )
  assert.match(
    settingsStylesSource,
    /\.settings-xhs-feishu-actions\s*\{[\s\S]*?justify-content:\s*flex-end;[\s\S]*?margin-top:\s*0;/m
  )
})

test("settings panel actions are wired per section instead of global handlers", () => {
  assert.doesNotMatch(settingsPanelsSource, /\bhasPendingSettingsChange\b/)
  assert.doesNotMatch(settingsPanelsSource, /\bonSaveAll\b/)
  assert.match(settingsPanelsSource, /\bonSaveLobsterKey\b/)
  assert.doesNotMatch(settingsPanelsSource, /\bsaveWorkspaceDirectory\b/)
  assert.doesNotMatch(settingsPanelsSource, /\brestoreWorkspaceDirectory\b/)
  assert.match(settingsPanelsSource, /\bonSaveCopywritingConfig\b/)
  assert.match(settingsPanelsSource, /\bonSaveImageConfig\b/)
  assert.match(settingsPanelsSource, /\bonSaveVideoConfig\b/)
  assert.match(settingsPanelsSource, /\bonSaveDigitalHumanConfig\b/)
  assert.match(settingsPanelsSource, /\bonSaveDouyinRuntimeConfig\b/)
})

test("settings secret inputs default hidden and gate reveal buttons behind saved secrets", () => {
  assert.match(settingsPanelsSource, /type=\{visible \? "text" : "password"\}/)
  assert.match(settingsPanelsSource, /configured:\s*boolean/)
  assert.match(settingsPanelsSource, /const visible = configured && Boolean\(revealedSecrets\[secretId\]\)/)
  assert.match(settingsPanelsSource, /const secretPlaceholder = configured && !value && !visible \? "••••••••••••" : placeholder/)
  assert.match(settingsPanelsSource, /placeholder=\{secretPlaceholder\}/)
  assert.match(settingsPanelsSource, /editable = true/)
  assert.match(settingsPanelsSource, /readOnly=\{!editable\}/)
  assert.match(settingsPanelsSource, /configured \? \(/)
  assert.match(settingsPanelsSource, /secretId: "lobsterKey"/)
  assert.match(settingsPanelsSource, /configured: workspaceApiKeyConfigured/)
  assert.match(settingsPanelsSource, /secretId: "copywritingModelApiKey"/)
  assert.match(settingsPanelsSource, /configured: Boolean\(config\?\.expertModelConfig\.copywriting\.apiKeyConfigured\)/)
  assert.match(settingsPanelsSource, /secretId: "imageModelApiKey"/)
  assert.match(settingsPanelsSource, /secretId: "videoModelApiKey"/)
  assert.match(settingsPanelsSource, /secretId: "videoAnalyzerApiKey"/)
  assert.match(settingsPanelsSource, /secretId: "replicationBriefApiKey"/)
  assert.match(settingsPanelsSource, /secretId: "vectcutApiKey"/)
  assert.match(settingsPanelsSource, /"settings-secret-input-row", configured \? "has-reveal" : ""/)
  assert.match(settingsPanelsSource, /\["settings-secret-value-input", inputClassName\]\.filter\(Boolean\)\.join\(" "\)/)
  assert.match(settingsPanelsSource, /className="settings-secret-reveal-button settings-secret-reveal-button-adornment"/)
  assert.match(settingsPanelsSource, /onRevealSecret\(secretId\)/)
  assert.match(settingsStylesSource, /\.settings-secret-input-row\s*\{[\s\S]*?position:\s*relative;/m)
  assert.match(settingsStylesSource, /\.settings-secret-input-row\.has-reveal input\s*\{[\s\S]*?padding-right:\s*calc\(var\(--settings-control-height\) \+ 8px\);/m)
  assert.match(settingsStylesSource, /\.settings-secret-value-input\s*\{[\s\S]*?overflow:\s*hidden;[\s\S]*?text-overflow:\s*ellipsis;[\s\S]*?white-space:\s*nowrap;/m)
  assert.match(settingsStylesSource, /\.settings-secret-reveal-button\s*\{[\s\S]*?position:\s*absolute;[\s\S]*?right:\s*4px;/m)
  assert.match(settingsStylesSource, /\.settings-secret-reveal-button\s*\{[\s\S]*?padding:\s*0;[\s\S]*?z-index:\s*1;/m)
  assert.match(settingsStylesSource, /\.shell\.openclaw-theme \.settings-secret-reveal-button\s*\{[\s\S]*?transform:\s*translateY\(-50%\);/m)
  assert.match(settingsStylesSource, /\.settings-secret-reveal-button:hover:not\(:disabled\)\s*\{[\s\S]*?transform:\s*translateY\(-50%\);[\s\S]*?box-shadow:\s*none;/m)
  assert.match(settingsStylesSource, /\.shell\.openclaw-theme \.settings-secret-reveal-button:hover:not\(:disabled\)\s*\{[\s\S]*?transform:\s*translateY\(-50%\);[\s\S]*?box-shadow:\s*none;/m)
  assert.match(settingsStylesSource, /\.settings-secret-reveal-button:focus-visible,\s*\n\.settings-secret-reveal-button:active\s*\{[\s\S]*?transform:\s*translateY\(-50%\);/m)
  assert.match(settingsStylesSource, /\.settings-secret-reveal-button svg\s*\{[\s\S]*?width:\s*22px;[\s\S]*?height:\s*22px;/m)
})

test("expert model cards become editable after clicking edit", () => {
  assert.match(settingsPanelsSource, /const \[editingSettingsSections, setEditingSettingsSections\] = useState<EditingSettingsSections>\(\{\}\)/)
  assert.match(settingsPanelsSource, /const renderEditableActions = \(/)
  assert.match(settingsPanelsSource, /\{editing \? "取消" : "编辑"\}/)
  assert.match(settingsPanelsSource, /disabled=\{saving \|\| !editing \|\| !hasPending\}/)
  assert.match(settingsPanelsSource, /Promise\.resolve\(onSave\(\)\)\.then\(\(saved\) =>/)
  assert.match(settingsPanelsSource, /if \(saved !== false\)/)
  assert.match(settingsPanelsSource, /sectionId: "copywriting"/)
  assert.match(settingsPanelsSource, /sectionId: "image"/)
  assert.match(settingsPanelsSource, /sectionId: "video"/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.copywritingModelBaseUrl, setters\.setCopywritingModelBaseUrl, config\?\.expertModelConfig\.copywriting\.baseUrl, editingCopywritingConfig/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.copywritingModelModelId, setters\.setCopywritingModelModelId, config\?\.expertModelConfig\.copywriting\.modelId, editingCopywritingConfig/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.imageModelBaseUrl, setters\.setImageModelBaseUrl, config\?\.expertModelConfig\.image\.baseUrl, editingImageConfig/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.imageModelModelId, setters\.setImageModelModelId, config\?\.expertModelConfig\.image\.modelId, editingImageConfig/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.videoModelBaseUrl, setters\.setVideoModelBaseUrl, config\?\.expertModelConfig\.video\.baseUrl, editingVideoConfig/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.videoModelModelId, setters\.setVideoModelModelId, config\?\.expertModelConfig\.video\.modelId, editingVideoConfig/)
  assert.match(settingsPanelsSource, /onSave: onSaveCopywritingConfig/)
  assert.match(settingsPanelsSource, /onSave: onSaveImageConfig/)
  assert.match(settingsPanelsSource, /onSave: onSaveVideoConfig/)
  assert.match(settingsPanelsSource, /settings-readonly-config-value/)
})

test("douyin runtime base urls, model ids, and file base url enter editable mode", () => {
  assert.match(settingsPanelsSource, /sectionId: "douyinRuntime"/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.videoAnalyzerBaseUrl, setters\.setVideoAnalyzerBaseUrl, config\?\.douyinRuntimeConfig\.videoAnalyzer\.baseUrl, editingDouyinRuntimeConfig/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.videoAnalyzerModelId, setters\.setVideoAnalyzerModelId, config\?\.douyinRuntimeConfig\.videoAnalyzer\.modelId \|\| "doubao-vision", editingDouyinRuntimeConfig/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.replicationBriefBaseUrl, setters\.setReplicationBriefBaseUrl, config\?\.douyinRuntimeConfig\.replicationBrief\.baseUrl, editingDouyinRuntimeConfig/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.replicationBriefModelId, setters\.setReplicationBriefModelId, config\?\.douyinRuntimeConfig\.replicationBrief\.modelId \|\| "qwen-max", editingDouyinRuntimeConfig/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.vectcutBaseUrl, setters\.setVectcutBaseUrl, config\?\.douyinRuntimeConfig\.vectcut\.baseUrl, editingDouyinRuntimeConfig/)
  assert.match(settingsPanelsSource, /renderEditableConfigValue\("file_base_url", drafts\.vectcutFileBaseUrl, setters\.setVectcutFileBaseUrl, config\?\.douyinRuntimeConfig\.vectcut\.fileBaseUrl, editingDouyinRuntimeConfig/)
  assert.match(settingsPanelsSource, /onSave: onSaveDouyinRuntimeConfig/)
})

test("read-only config values expose full values by title and copy on click or keyboard", () => {
  assert.match(settingsPanelsSource, /const copyReadonlyConfigValue = \(value\?: string\) =>/)
  assert.match(settingsPanelsSource, /const \[copiedConfigValue, setCopiedConfigValue\] = useState<string \| null>\(null\)/)
  assert.match(settingsPanelsSource, /setCopiedConfigValue\(value\)/)
  assert.match(settingsPanelsSource, /settings-readonly-config-copy-feedback/)
  assert.match(settingsPanelsSource, />已复制</)
  assert.doesNotMatch(settingsPanelsSource, /\u2705已复制/)
  assert.match(settingsPanelsSource, /navigator\.clipboard\.writeText\(value\)/)
  assert.match(settingsPanelsSource, /const handleReadonlyConfigKeyDown = \(event: KeyboardEvent<HTMLDivElement>, value\?: string\) =>/)
  assert.match(settingsPanelsSource, /event\.key !== "Enter" && event\.key !== " "/)
  assert.match(settingsPanelsSource, /role="button"/)
  assert.match(settingsPanelsSource, /tabIndex=\{value \? 0 : -1\}/)
  assert.match(settingsPanelsSource, /title=\{value \|\| undefined\}/)
  assert.match(settingsPanelsSource, /onClick=\{\(\) => copyReadonlyConfigValue\(value\)\}/)
  assert.match(settingsPanelsSource, /onKeyDown=\{\(event\) => handleReadonlyConfigKeyDown\(event, value\)\}/)
  assert.match(settingsStylesSource, /\.settings-readonly-config-value\s*\{[\s\S]*?overflow:\s*hidden;[\s\S]*?text-overflow:\s*ellipsis;[\s\S]*?white-space:\s*nowrap;/m)
  assert.match(settingsStylesSource, /\.settings-readonly-config-copy-feedback\s*\{/)
  assert.match(settingsStylesSource, /\.settings-readonly-config-value\[role="button"\]\s*\{[\s\S]*?cursor:\s*copy;/m)
  assert.match(settingsStylesSource, /\.settings-readonly-config-value:focus-visible\s*\{/)
})
