<template>
  <section class="page factory-page">
    <div class="factory-shell">
      <header class="factory-page-header">
        <div class="factory-heading">
          <div class="factory-title-row">
            <h1>Factory 版本工作台</h1>
            <el-tag v-if="versions.length" effect="plain">{{ versions.length }} 个版本</el-tag>
          </div>
          <p>{{ project?.projectName || `项目 ${projectId}` }}</p>
        </div>
        <div class="toolbar factory-header-actions">
          <el-button :icon="ArrowLeft" @click="router.push('/projects')">返回项目</el-button>
          <el-button :icon="Refresh" :loading="pageLoading" @click="refreshWorkspace">刷新</el-button>
        </div>
      </header>

      <div v-loading="pageLoading" class="factory-workspace">
        <aside class="version-pane" aria-label="ProjectSpec 版本历史">
          <div class="pane-header">
            <div>
              <h2>版本历史</h2>
              <span>不可变 ProjectSpec</span>
            </div>
          </div>

          <div v-if="versions.length" class="version-list" role="list">
            <button
              v-for="(version, index) in versions"
              :key="version.specVersionId"
              type="button"
              class="version-row"
              :class="{ 'version-row--selected': version.specVersionId === selectedVersionId }"
              :aria-current="version.specVersionId === selectedVersionId ? 'true' : undefined"
              @click="selectVersion(version.specVersionId)"
            >
              <span class="version-row-top">
                <strong>v{{ version.versionNo }}</strong>
                <span class="version-row-tags">
                  <el-tag v-if="index === 0" size="small" type="success">当前</el-tag>
                  <el-tag size="small" :type="sourceTagType(version.changeSource)" effect="plain">
                    {{ sourceLabel(version.changeSource) }}
                  </el-tag>
                </span>
              </span>
              <span class="version-summary">{{ version.changeSummary || '无变更摘要' }}</span>
              <span class="version-meta">
                <code>{{ shortHash(version.contentHash) }}</code>
                <time>{{ version.createTime || '-' }}</time>
              </span>
            </button>
          </div>
          <el-empty v-else :image-size="72" description="暂无 ProjectSpec 版本" />
        </aside>

        <main class="detail-pane">
          <template v-if="selectedVersion">
            <div class="selected-version-header">
              <div class="selected-version-title">
                <div class="selected-title-line">
                  <h2>v{{ selectedVersion.versionNo }}</h2>
                  <el-tag :type="selectedIsLatest ? 'success' : 'info'">
                    {{ selectedIsLatest ? '当前版本' : '历史版本' }}
                  </el-tag>
                  <el-tag :type="validationTag.type" effect="plain">{{ validationTag.label }}</el-tag>
                </div>
                <p>{{ selectedVersion.changeSummary || '无变更摘要' }}</p>
                <div class="selected-hash">
                  <span>Spec SHA-256</span>
                  <code>{{ selectedVersion.contentHash }}</code>
                  <el-tooltip content="复制哈希" placement="top">
                    <el-button
                      :icon="CopyDocument"
                      circle
                      text
                      aria-label="复制 Spec 哈希"
                      @click="copyText(selectedVersion.contentHash, 'Spec 哈希已复制')"
                    />
                  </el-tooltip>
                </div>
              </div>
              <div class="selected-version-actions">
                <el-button
                  type="primary"
                  :icon="VideoPlay"
                  :loading="generating"
                  @click="generateSelectedVersion"
                >
                  生成并校验指纹
                </el-button>
                <el-button
                  :icon="RefreshLeft"
                  :disabled="selectedIsLatest"
                  :loading="rollingBack"
                  @click="rollbackSelectedVersion"
                >
                  回滚为新版本
                </el-button>
              </div>
            </div>

            <el-tabs v-model="activeTab" class="factory-tabs">
              <el-tab-pane label="ProjectSpec" name="spec">
                <div v-loading="detailLoading" class="tab-content spec-tab">
                  <div class="tab-toolbar">
                    <div class="tab-toolbar-title">
                      <strong>规范化 JSON</strong>
                      <span>Schema {{ selectedDetail?.schemaVersion || '-' }}</span>
                    </div>
                    <el-tooltip content="复制 ProjectSpec" placement="top">
                      <el-button
                        :icon="CopyDocument"
                        circle
                        plain
                        aria-label="复制 ProjectSpec"
                        @click="copyText(formattedSpec, 'ProjectSpec 已复制')"
                      />
                    </el-tooltip>
                  </div>
                  <pre class="spec-code">{{ formattedSpec }}</pre>

                  <section class="validation-section" aria-label="ProjectSpec 校验结果">
                    <div class="section-heading">
                      <h3>校验结果</h3>
                      <div v-if="validationResult" class="validation-counts">
                        <el-tag :type="validationResult.valid ? 'success' : 'danger'">
                          {{ validationResult.valid ? '通过' : `${validationResult.errorCount || 0} 个错误` }}
                        </el-tag>
                        <el-tag v-if="validationResult.warningCount" type="warning" effect="plain">
                          {{ validationResult.warningCount }} 个警告
                        </el-tag>
                      </div>
                    </div>
                    <el-table
                      v-if="validationIssues.length"
                      :data="validationIssues"
                      size="small"
                      class="issue-table"
                    >
                      <el-table-column label="级别" width="86">
                        <template #default="{ row }">
                          <el-tag :type="row.severity === 'ERROR' ? 'danger' : 'warning'" size="small">
                            {{ row.severity }}
                          </el-tag>
                        </template>
                      </el-table-column>
                      <el-table-column prop="code" label="代码" min-width="180" />
                      <el-table-column prop="path" label="路径" min-width="240" show-overflow-tooltip />
                      <el-table-column prop="message" label="说明" min-width="280" />
                    </el-table>
                    <el-empty v-else :image-size="58" description="没有校验问题" />
                  </section>
                </div>
              </el-tab-pane>

              <el-tab-pane label="语义 Diff" name="diff">
                <div class="tab-content diff-tab">
                  <div class="diff-toolbar">
                    <div class="compare-control">
                      <span>基线版本</span>
                      <el-select
                        v-model="comparisonVersionId"
                        placeholder="选择版本"
                        :disabled="comparisonOptions.length === 0"
                        @change="loadDiff"
                      >
                        <el-option
                          v-for="version in comparisonOptions"
                          :key="version.specVersionId"
                          :label="`v${version.versionNo} · ${sourceLabel(version.changeSource)}`"
                          :value="version.specVersionId"
                        />
                      </el-select>
                    </div>
                    <span v-if="comparisonVersion" class="diff-direction">
                      v{{ comparisonVersion.versionNo }} → v{{ selectedVersion.versionNo }}
                    </span>
                  </div>

                  <div v-if="diffResult" class="diff-summary" aria-label="Diff 汇总">
                    <div><strong>{{ diffResult.totalChanges || 0 }}</strong><span>全部变更</span></div>
                    <div><strong>{{ diffResult.addedCount || 0 }}</strong><span>新增</span></div>
                    <div><strong>{{ diffResult.changedCount || 0 }}</strong><span>修改</span></div>
                    <div><strong>{{ diffResult.removedCount || 0 }}</strong><span>删除</span></div>
                  </div>

                  <el-table
                    v-if="diffChanges.length"
                    v-loading="diffLoading"
                    :data="diffChanges"
                    size="small"
                    class="diff-table"
                  >
                    <el-table-column label="类型" width="92">
                      <template #default="{ row }">
                        <el-tag :type="diffTagType(row.type)" size="small">{{ diffTypeLabel(row.type) }}</el-tag>
                      </template>
                    </el-table-column>
                    <el-table-column prop="section" label="区域" width="130" />
                    <el-table-column prop="path" label="语义路径" min-width="260" show-overflow-tooltip />
                    <el-table-column label="变更前" min-width="220">
                      <template #default="{ row }"><code class="diff-value">{{ formatValue(row.beforeValue) }}</code></template>
                    </el-table-column>
                    <el-table-column label="变更后" min-width="220">
                      <template #default="{ row }"><code class="diff-value">{{ formatValue(row.afterValue) }}</code></template>
                    </el-table-column>
                  </el-table>
                  <el-empty
                    v-else
                    v-loading="diffLoading"
                    :image-size="72"
                    :description="comparisonVersionId ? '两个版本没有语义差异' : '没有可比较的历史版本'"
                  />
                </div>
              </el-tab-pane>

              <el-tab-pane label="生成运行" name="manifest">
                <div v-loading="runLoading" class="tab-content manifest-tab">
                  <div class="run-history-toolbar">
                    <div class="compare-control">
                      <span>生成运行</span>
                      <el-select
                        v-model="selectedGenerationRunId"
                        placeholder="暂无运行"
                        :disabled="generationRuns.length === 0"
                        @change="selectGenerationRun"
                      >
                        <el-option
                          v-for="run in generationRuns"
                          :key="run.generationRunId"
                          :label="runOptionLabel(run)"
                          :value="run.generationRunId"
                        />
                      </el-select>
                    </div>
                    <div class="compare-control">
                      <span>对比运行</span>
                      <el-select
                        v-model="comparisonRunId"
                        clearable
                        placeholder="选择对比运行"
                        :disabled="comparisonRunOptions.length === 0"
                        @change="loadRunDiff"
                      >
                        <el-option
                          v-for="run in comparisonRunOptions"
                          :key="run.generationRunId"
                          :label="runOptionLabel(run)"
                          :value="run.generationRunId"
                        />
                      </el-select>
                    </div>
                  </div>

                  <template v-if="generationResult">
                    <div class="run-identity-row">
                      <div>
                        <span>运行 ID</span>
                        <strong>#{{ generationResult.generationRunId }}</strong>
                      </div>
                      <div>
                        <span>ProjectSpec</span>
                        <strong>v{{ generationResult.versionNo }}</strong>
                      </div>
                      <div>
                        <span>完成时间</span>
                        <strong>{{ generationResult.createTime || '-' }}</strong>
                      </div>
                      <el-tag type="success">{{ generationResult.status || 'SUCCEEDED' }}</el-tag>
                    </div>

                    <div class="fingerprint-grid" aria-label="生成环境指纹">
                      <div>
                        <span>Adapter · {{ generationResult.adapterCode }} / {{ generationResult.adapterVersion }}</span>
                        <code>{{ generationResult.adapterFingerprint }}</code>
                        <small class="capability-meta">
                          Capability {{ shortHash(generationResult.adapterCapabilityFingerprint) }}
                          · {{ adapterCapabilitySummary(generationResult.adapterCapabilitiesJson) }}
                        </small>
                      </div>
                      <div>
                        <span>Plugin 集合</span>
                        <code>{{ generationResult.pluginFingerprint }}</code>
                      </div>
                      <div>
                        <span>Template · {{ generationResult.templateCode }} / {{ generationResult.templateVersion }}</span>
                        <code>{{ generationResult.templateFingerprint }}</code>
                      </div>
                      <div>
                        <span>完整生成环境</span>
                        <code>{{ generationResult.environmentFingerprint }}</code>
                      </div>
                    </div>

                    <div class="manifest-summary">
                      <div><strong>{{ generationResult.targetCount || 0 }}</strong><span>生成目标</span></div>
                      <div><strong>{{ generationResult.fileCount || 0 }}</strong><span>文件</span></div>
                      <div><strong>{{ formatBytes(generationResult.totalBytes) }}</strong><span>内容体积</span></div>
                      <div class="manifest-hash-cell">
                        <span>聚合 SHA-256</span>
                        <code>{{ generationResult.aggregateContentHash }}</code>
                      </div>
                    </div>

                    <section v-if="runDiffResult" v-loading="runDiffLoading" class="run-diff-section" aria-label="生成产物 Diff">
                      <div class="section-heading">
                        <h3>产物 Diff</h3>
                        <div class="run-change-flags">
                          <el-tag :type="runDiffResult.specChanged ? 'warning' : 'info'" effect="plain">
                            Spec {{ runDiffResult.specChanged ? '已变化' : '一致' }}
                          </el-tag>
                          <el-tag :type="runDiffResult.environmentChanged ? 'warning' : 'success'" effect="plain">
                            环境 {{ runDiffResult.environmentChanged ? '已变化' : '一致' }}
                          </el-tag>
                        </div>
                      </div>
                      <div class="diff-summary">
                        <div><strong>{{ runDiffResult.totalChanges || 0 }}</strong><span>全部变化</span></div>
                        <div><strong>{{ runDiffResult.addedCount || 0 }}</strong><span>新增</span></div>
                        <div><strong>{{ runDiffResult.changedCount || 0 }}</strong><span>修改</span></div>
                        <div><strong>{{ runDiffResult.removedCount || 0 }}</strong><span>删除</span></div>
                      </div>
                      <el-table :data="runDiffResult.changes || []" size="small" class="run-diff-table">
                        <el-table-column label="类型" width="92">
                          <template #default="{ row }">
                            <el-tag :type="diffTagType(row.type)" size="small">{{ diffTypeLabel(row.type) }}</el-tag>
                          </template>
                        </el-table-column>
                        <el-table-column label="目标" width="120">
                          <template #default="{ row }">{{ targetLabel(row.templateType) }}</template>
                        </el-table-column>
                        <el-table-column prop="path" label="文件路径" min-width="340" show-overflow-tooltip />
                        <el-table-column label="变更前" min-width="180">
                          <template #default="{ row }">
                            <code>{{ shortHash(row.beforeContentHash) }}</code>
                            <span class="run-file-size">{{ formatOptionalBytes(row.beforeSize) }}</span>
                          </template>
                        </el-table-column>
                        <el-table-column label="变更后" min-width="180">
                          <template #default="{ row }">
                            <code>{{ shortHash(row.afterContentHash) }}</code>
                            <span class="run-file-size">{{ formatOptionalBytes(row.afterSize) }}</span>
                          </template>
                        </el-table-column>
                      </el-table>
                    </section>

                    <el-collapse v-model="openManifestTargets" class="manifest-targets">
                      <el-collapse-item
                        v-for="target in generationResult.targets || []"
                        :key="target.templateType"
                        :name="target.templateType"
                      >
                        <template #title>
                          <div class="target-title">
                            <strong>{{ targetLabel(target.templateType) }}</strong>
                            <span>{{ target.fileCount || 0 }} 个文件 · {{ formatBytes(target.totalBytes) }}</span>
                            <code>{{ shortHash(target.contentHash) }}</code>
                          </div>
                        </template>
                        <el-table :data="target.files || []" size="small" class="manifest-file-table">
                          <el-table-column prop="path" label="文件路径" min-width="420" show-overflow-tooltip />
                          <el-table-column label="大小" width="110">
                            <template #default="{ row }">{{ formatBytes(row.size) }}</template>
                          </el-table-column>
                          <el-table-column label="SHA-256" min-width="250">
                            <template #default="{ row }"><code>{{ row.contentHash }}</code></template>
                          </el-table-column>
                        </el-table>
                      </el-collapse-item>
                    </el-collapse>
                  </template>
                  <el-empty v-else :image-size="86" description="尚无持久化生成运行">
                    <el-button type="primary" :icon="VideoPlay" :loading="generating" @click="generateSelectedVersion">
                      生成并校验指纹
                    </el-button>
                  </el-empty>
                </div>
              </el-tab-pane>
            </el-tabs>
          </template>
          <el-empty v-else description="请选择一个 ProjectSpec 版本" />
        </main>
      </div>
    </div>
  </section>
</template>

<script setup>
import { computed, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ArrowLeft, CopyDocument, Refresh, RefreshLeft, VideoPlay } from '@element-plus/icons-vue'
import {
  diffProjectSpecGenerationRuns,
  diffProjectSpecVersions,
  generateProjectSpecVersion,
  getProject,
  getProjectSpecGenerationRun,
  getProjectSpecVersion,
  listProjectSpecGenerationRuns,
  listProjectSpecVersions,
  rollbackProjectSpecVersion,
  validateProjectSpec
} from '@/api/project'

const SOURCE_LABELS = {
  AI: 'AI',
  AUTO_REPAIR: '自动修复',
  IMPORT: '导入',
  MANUAL_SNAPSHOT: '手动快照',
  PATCH: 'Patch',
  ROLLBACK: '回滚',
  SYSTEM: '系统'
}

const TARGET_LABELS = {
  backend: '后端',
  frontend: '用户前端',
  admin_frontend: '管理前端',
  sql: 'SQL'
}

const route = useRoute()
const router = useRouter()
const projectId = computed(() => Number(route.params.projectId))
const project = ref(null)
const versions = ref([])
const selectedVersionId = ref(null)
const selectedDetail = ref(null)
const validationResult = ref(null)
const comparisonVersionId = ref(null)
const diffResult = ref(null)
const generationRuns = ref([])
const selectedGenerationRunId = ref(null)
const selectedGenerationRun = ref(null)
const comparisonRunId = ref(null)
const runDiffResult = ref(null)
const activeTab = ref('spec')
const openManifestTargets = ref([])
const pageLoading = ref(false)
const detailLoading = ref(false)
const diffLoading = ref(false)
const runLoading = ref(false)
const runDiffLoading = ref(false)
const generating = ref(false)
const rollingBack = ref(false)
let detailRequestSequence = 0
let runRequestSequence = 0
let runDiffRequestSequence = 0

const selectedVersion = computed(() => versions.value.find(
  (item) => item.specVersionId === selectedVersionId.value
) || null)
const selectedIsLatest = computed(() => Boolean(
  versions.value.length && selectedVersionId.value === versions.value[0].specVersionId
))
const comparisonOptions = computed(() => versions.value.filter(
  (item) => item.specVersionId !== selectedVersionId.value
))
const comparisonVersion = computed(() => versions.value.find(
  (item) => item.specVersionId === comparisonVersionId.value
) || null)
const validationIssues = computed(() => validationResult.value?.issues || [])
const diffChanges = computed(() => diffResult.value?.changes || [])
const generationResult = computed(() => selectedGenerationRun.value)
const comparisonRunOptions = computed(() => generationRuns.value.filter(
  (item) => item.generationRunId !== selectedGenerationRunId.value
))
const formattedSpec = computed(() => {
  if (selectedDetail.value?.canonicalJson) {
    try {
      return JSON.stringify(JSON.parse(selectedDetail.value.canonicalJson), null, 2)
    } catch (_error) {
      return selectedDetail.value.canonicalJson
    }
  }
  return selectedDetail.value?.spec ? JSON.stringify(selectedDetail.value.spec, null, 2) : ''
})
const validationTag = computed(() => {
  if (!validationResult.value) return { label: '待校验', type: 'info' }
  if (validationResult.value.valid) return { label: '校验通过', type: 'success' }
  return { label: `${validationResult.value.errorCount || 0} 个错误`, type: 'danger' }
})

function sourceLabel(source) {
  return SOURCE_LABELS[source] || source || '未知'
}

function sourceTagType(source) {
  if (source === 'AI') return 'primary'
  if (source === 'ROLLBACK') return 'warning'
  if (source === 'PATCH') return 'success'
  return 'info'
}

function shortHash(hash) {
  return hash ? `${hash.slice(0, 12)}…` : '-'
}

function targetLabel(target) {
  return TARGET_LABELS[target] || target
}

function adapterCapabilitySummary(capabilitiesJson) {
  try {
    const capabilities = typeof capabilitiesJson === 'string'
      ? JSON.parse(capabilitiesJson)
      : capabilitiesJson
    const targets = Array.isArray(capabilities?.outputTargets)
      ? capabilities.outputTargets
      : []
    return targets.length ? targets.map(targetLabel).join(' / ') : '未声明输出目标'
  } catch (error) {
    return '能力快照不可解析'
  }
}

function formatBytes(value) {
  const bytes = Number(value || 0)
  if (bytes < 1024) return `${bytes} B`
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
  return `${(bytes / 1024 / 1024).toFixed(1)} MB`
}

function formatOptionalBytes(value) {
  return value === undefined || value === null ? '-' : formatBytes(value)
}

function runOptionLabel(run) {
  return `#${run.generationRunId} · Spec v${run.versionNo} · ${run.createTime || '-'}`
}

function formatValue(value) {
  if (value === undefined || value === null) return '-'
  return typeof value === 'string' ? value : JSON.stringify(value)
}

function diffTagType(type) {
  if (type === 'ADDED') return 'success'
  if (type === 'REMOVED') return 'danger'
  return 'warning'
}

function diffTypeLabel(type) {
  if (type === 'ADDED') return '新增'
  if (type === 'REMOVED') return '删除'
  return '修改'
}

function defaultComparisonVersionId(versionId) {
  if (versions.value.length < 2) return null
  const selectedIndex = versions.value.findIndex((item) => item.specVersionId === versionId)
  if (selectedIndex === 0) return versions.value[1].specVersionId
  return versions.value[0].specVersionId
}

function defaultComparisonRunId(runId) {
  if (generationRuns.value.length < 2) return null
  const selectedIndex = generationRuns.value.findIndex((item) => item.generationRunId === runId)
  if (selectedIndex === 0) return generationRuns.value[1].generationRunId
  return generationRuns.value[0].generationRunId
}

async function loadVersions(preferredVersionId) {
  const result = await listProjectSpecVersions(projectId.value)
  versions.value = Array.isArray(result) ? result : []
  const preferred = versions.value.find((item) => item.specVersionId === preferredVersionId)
  return preferred?.specVersionId || versions.value[0]?.specVersionId || null
}

async function loadGenerationRuns(preferredRunId) {
  const result = await listProjectSpecGenerationRuns(projectId.value)
  generationRuns.value = Array.isArray(result) ? result : []
  const preferred = generationRuns.value.find((item) => item.generationRunId === preferredRunId)
  return preferred?.generationRunId || generationRuns.value[0]?.generationRunId || null
}

async function loadWorkspace(preferredVersionId, preferredRunId) {
  pageLoading.value = true
  try {
    const [projectResult, nextVersionId, nextRunId] = await Promise.all([
      getProject(projectId.value),
      loadVersions(preferredVersionId),
      loadGenerationRuns(preferredRunId)
    ])
    project.value = projectResult
    if (nextVersionId) {
      await selectVersion(nextVersionId)
    } else {
      selectedVersionId.value = null
      selectedDetail.value = null
      validationResult.value = null
    }
    if (nextRunId) {
      await selectGenerationRun(nextRunId)
    } else {
      selectedGenerationRunId.value = null
      selectedGenerationRun.value = null
      comparisonRunId.value = null
      runDiffResult.value = null
    }
  } catch (error) {
    ElMessage.error(error.message || '加载 Factory 版本工作台失败')
  } finally {
    pageLoading.value = false
  }
}

async function refreshWorkspace() {
  await loadWorkspace(selectedVersionId.value, selectedGenerationRunId.value)
}

async function selectVersion(versionId) {
  if (!versionId) return
  const requestSequence = ++detailRequestSequence
  selectedVersionId.value = versionId
  selectedDetail.value = null
  validationResult.value = null
  diffResult.value = null
  comparisonVersionId.value = defaultComparisonVersionId(versionId)
  detailLoading.value = true
  try {
    const detail = await getProjectSpecVersion(projectId.value, versionId)
    const validation = await validateProjectSpec(projectId.value, detail.spec)
    if (requestSequence !== detailRequestSequence) return
    selectedDetail.value = detail
    validationResult.value = validation
    await loadDiff()
  } catch (error) {
    if (requestSequence === detailRequestSequence) {
      ElMessage.error(error.message || '加载 ProjectSpec 版本失败')
    }
  } finally {
    if (requestSequence === detailRequestSequence) detailLoading.value = false
  }
}

async function loadDiff() {
  if (!comparisonVersionId.value || !selectedVersionId.value) {
    diffResult.value = null
    return
  }
  diffLoading.value = true
  try {
    diffResult.value = await diffProjectSpecVersions(
      projectId.value,
      comparisonVersionId.value,
      selectedVersionId.value
    )
  } catch (error) {
    diffResult.value = null
    ElMessage.error(error.message || '加载版本 Diff 失败')
  } finally {
    diffLoading.value = false
  }
}

async function selectGenerationRun(generationRunId) {
  if (!generationRunId) return
  const requestSequence = ++runRequestSequence
  selectedGenerationRunId.value = generationRunId
  selectedGenerationRun.value = null
  runDiffResult.value = null
  comparisonRunId.value = defaultComparisonRunId(generationRunId)
  runLoading.value = true
  try {
    const detail = await getProjectSpecGenerationRun(projectId.value, generationRunId)
    if (requestSequence !== runRequestSequence) return
    selectedGenerationRun.value = detail
    openManifestTargets.value = (detail.targets || []).map((target) => target.templateType)
    await loadRunDiff()
  } catch (error) {
    if (requestSequence === runRequestSequence) {
      ElMessage.error(error.message || '加载生成运行失败')
    }
  } finally {
    if (requestSequence === runRequestSequence) runLoading.value = false
  }
}

async function loadRunDiff() {
  const requestSequence = ++runDiffRequestSequence
  if (!comparisonRunId.value || !selectedGenerationRunId.value) {
    runDiffResult.value = null
    runDiffLoading.value = false
    return
  }
  runDiffLoading.value = true
  try {
    const result = await diffProjectSpecGenerationRuns(
      projectId.value,
      comparisonRunId.value,
      selectedGenerationRunId.value
    )
    if (requestSequence === runDiffRequestSequence) runDiffResult.value = result
  } catch (error) {
    if (requestSequence === runDiffRequestSequence) {
      runDiffResult.value = null
      ElMessage.error(error.message || '加载生成产物 Diff 失败')
    }
  } finally {
    if (requestSequence === runDiffRequestSequence) runDiffLoading.value = false
  }
}

async function generateSelectedVersion() {
  if (!selectedVersionId.value) return
  generating.value = true
  try {
    const result = await generateProjectSpecVersion(projectId.value, selectedVersionId.value)
    const nextRunId = await loadGenerationRuns(result.generationRunId)
    await selectGenerationRun(nextRunId)
    activeTab.value = 'manifest'
    ElMessage.success(`生成运行 #${result.generationRunId} 已持久化`)
  } catch (error) {
    ElMessage.error(error.message || '按版本生成失败')
  } finally {
    generating.value = false
  }
}

async function rollbackSelectedVersion() {
  if (!selectedVersion.value || selectedIsLatest.value) return
  try {
    await ElMessageBox.confirm(
      `将 v${selectedVersion.value.versionNo} 的内容创建为新的当前版本？`,
      '回滚 ProjectSpec',
      {
        type: 'warning',
        confirmButtonText: '创建回滚版本',
        cancelButtonText: '取消'
      }
    )
    rollingBack.value = true
    const result = await rollbackProjectSpecVersion(projectId.value, selectedVersionId.value, {
      changeSummary: `Rollback to version ${selectedVersion.value.versionNo} from Factory workspace`
    })
    const nextVersionId = await loadVersions(result.specVersionId)
    await selectVersion(nextVersionId)
    ElMessage.success(`已创建 v${result.versionNo} 回滚版本`)
  } catch (error) {
    if (error !== 'cancel' && error !== 'close') {
      ElMessage.error(error.message || '回滚 ProjectSpec 失败')
    }
  } finally {
    rollingBack.value = false
  }
}

async function copyText(value, successMessage) {
  if (!value) return
  try {
    await navigator.clipboard.writeText(value)
    ElMessage.success(successMessage)
  } catch (_error) {
    ElMessage.warning('当前浏览器无法复制到剪贴板')
  }
}

onMounted(() => loadWorkspace())
</script>

<style scoped lang="scss">
.factory-page {
  width: calc(100% - 40px);
  max-width: none;
}

.factory-shell {
  overflow: hidden;
  border: 1px solid #d9e0ea;
  border-radius: 8px;
  background: #ffffff;
}

.factory-page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  min-height: 76px;
  padding: 14px 20px;
  border-bottom: 1px solid #e7ecf3;
}

.factory-heading,
.selected-version-title,
.detail-pane {
  min-width: 0;
}

.factory-title-row,
.selected-title-line,
.version-row-top,
.version-row-tags,
.selected-hash,
.validation-counts,
.target-title {
  display: flex;
  align-items: center;
  gap: 10px;
}

.factory-title-row h1,
.pane-header h2,
.selected-title-line h2,
.section-heading h3 {
  margin: 0;
  color: #172033;
  letter-spacing: 0;
}

.factory-title-row h1 {
  font-size: 20px;
}

.factory-heading p,
.selected-version-title p {
  margin: 5px 0 0;
  color: #667085;
  font-size: 13px;
}

.factory-workspace {
  display: grid;
  grid-template-columns: minmax(290px, 340px) minmax(0, 1fr);
  min-height: calc(100vh - 190px);
}

.version-pane {
  min-width: 0;
  border-right: 1px solid #e7ecf3;
  background: #fbfcfe;
}

.pane-header {
  display: flex;
  align-items: center;
  min-height: 64px;
  padding: 12px 16px;
  border-bottom: 1px solid #e7ecf3;
}

.pane-header h2 {
  font-size: 15px;
}

.pane-header span {
  display: block;
  margin-top: 3px;
  color: #667085;
  font-size: 12px;
}

.version-list {
  max-height: calc(100vh - 255px);
  overflow: auto;
}

.version-row {
  display: grid;
  width: 100%;
  min-height: 104px;
  padding: 13px 16px;
  border: 0;
  border-bottom: 1px solid #e7ecf3;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.version-row:hover {
  background: #f3f6fa;
}

.version-row--selected {
  box-shadow: inset 3px 0 #2563eb;
  background: #eff6ff;
}

.version-row-top {
  justify-content: space-between;
}

.version-row-top strong {
  color: #172033;
  font-size: 15px;
}

.version-summary {
  align-self: center;
  overflow: hidden;
  color: #475467;
  font-size: 13px;
  line-height: 1.4;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.version-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  color: #8a94a6;
  font-size: 11px;
}

.version-meta code,
.selected-hash code,
.manifest-hash-cell code,
.target-title code,
.manifest-file-table code,
.fingerprint-grid code,
.run-diff-table code,
.diff-value {
  font-family: Consolas, "SFMono-Regular", monospace;
  letter-spacing: 0;
}

.detail-pane {
  background: #ffffff;
}

.selected-version-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  min-height: 112px;
  padding: 16px 20px;
  border-bottom: 1px solid #e7ecf3;
}

.selected-title-line h2 {
  font-size: 18px;
}

.selected-hash {
  margin-top: 9px;
  color: #667085;
  font-size: 12px;
}

.selected-hash code {
  max-width: min(560px, 48vw);
  overflow: hidden;
  color: #344054;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.selected-version-actions {
  display: flex;
  flex: 0 0 auto;
  gap: 10px;
}

.factory-tabs :deep(.el-tabs__header) {
  margin: 0;
  padding: 0 20px;
  border-bottom: 1px solid #e7ecf3;
}

.factory-tabs :deep(.el-tabs__nav-wrap::after) {
  display: none;
}

.tab-content {
  min-height: 460px;
  padding: 18px 20px 24px;
}

.tab-toolbar,
.section-heading,
.diff-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.tab-toolbar-title {
  display: grid;
  gap: 3px;
}

.tab-toolbar-title span,
.diff-direction {
  color: #667085;
  font-size: 12px;
}

.spec-code {
  min-height: 280px;
  max-height: 52vh;
  margin: 14px 0 20px;
  padding: 16px;
  overflow: auto;
  border: 1px solid #dfe5ee;
  border-radius: 6px;
  background: #101828;
  color: #d1e9ff;
  font: 12px/1.65 Consolas, "SFMono-Regular", monospace;
  letter-spacing: 0;
  tab-size: 2;
}

.validation-section {
  border-top: 1px solid #e7ecf3;
  padding-top: 18px;
}

.section-heading h3 {
  font-size: 15px;
}

.issue-table,
.diff-table {
  margin-top: 14px;
}

.compare-control {
  display: flex;
  align-items: center;
  gap: 10px;
  color: #475467;
  font-size: 13px;
}

.compare-control :deep(.el-select) {
  width: 220px;
}

.run-history-toolbar,
.run-identity-row,
.run-change-flags {
  display: flex;
  align-items: center;
  gap: 12px;
}

.run-history-toolbar {
  justify-content: space-between;
  padding-bottom: 16px;
  border-bottom: 1px solid #e7ecf3;
}

.run-identity-row {
  flex-wrap: wrap;
  padding: 16px 0;
}

.run-identity-row > div {
  display: grid;
  gap: 3px;
  min-width: 130px;
}

.run-identity-row span,
.fingerprint-grid span,
.run-file-size {
  color: #667085;
  font-size: 12px;
}

.run-identity-row strong {
  color: #172033;
  font-size: 14px;
}

.fingerprint-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border-top: 1px solid #dfe5ee;
  border-bottom: 1px solid #dfe5ee;
}

.fingerprint-grid > div {
  display: grid;
  gap: 6px;
  min-width: 0;
  padding: 12px 14px;
  border-right: 1px solid #e7ecf3;
  border-bottom: 1px solid #e7ecf3;
}

.fingerprint-grid > div:nth-child(2n) {
  border-right: 0;
}

.fingerprint-grid > div:nth-last-child(-n + 2) {
  border-bottom: 0;
}

.fingerprint-grid code {
  overflow: hidden;
  color: #344054;
  font-size: 11px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fingerprint-grid .capability-meta {
  overflow: hidden;
  color: #667085;
  font-size: 11px;
  line-height: 1.45;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.diff-summary,
.manifest-summary {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-top: 16px;
  border: 1px solid #dfe5ee;
  border-radius: 6px;
}

.diff-summary > div,
.manifest-summary > div {
  display: grid;
  gap: 4px;
  min-height: 72px;
  align-content: center;
  padding: 11px 14px;
  border-right: 1px solid #e7ecf3;
}

.diff-summary > div:last-child,
.manifest-summary > div:last-child {
  border-right: 0;
}

.diff-summary strong,
.manifest-summary strong {
  color: #172033;
  font-size: 18px;
}

.diff-summary span,
.manifest-summary span {
  color: #667085;
  font-size: 12px;
}

.diff-value {
  display: block;
  max-height: 74px;
  overflow: auto;
  color: #344054;
  font-size: 12px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.run-diff-section {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid #e7ecf3;
}

.run-diff-table {
  margin-top: 14px;
}

.run-diff-table code,
.run-file-size {
  display: block;
}

.run-diff-table code {
  color: #344054;
  font-size: 12px;
}

.run-file-size {
  margin-top: 3px;
}

.manifest-hash-cell {
  min-width: 0;
}

.manifest-hash-cell code {
  overflow: hidden;
  color: #344054;
  font-size: 11px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.manifest-targets {
  margin-top: 18px;
  border-top: 1px solid #dfe5ee;
}

.target-title {
  width: calc(100% - 24px);
  min-width: 0;
}

.target-title strong {
  min-width: 90px;
  color: #172033;
}

.target-title span {
  color: #667085;
  font-size: 12px;
}

.target-title code {
  margin-left: auto;
  color: #667085;
  font-size: 11px;
}

@media (max-width: 1100px) {
  .selected-version-header {
    align-items: stretch;
    flex-direction: column;
  }

  .selected-version-actions {
    justify-content: flex-start;
  }

  .selected-hash code {
    max-width: 62vw;
  }
}

@media (max-width: 900px) {
  .factory-page {
    width: min(100% - 24px, 1180px);
  }

  .factory-workspace {
    grid-template-columns: minmax(0, 1fr);
  }

  .version-pane {
    border-right: 0;
    border-bottom: 1px solid #e7ecf3;
  }

  .version-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-height: 280px;
  }

  .version-row:nth-child(odd) {
    border-right: 1px solid #e7ecf3;
  }

  .factory-workspace {
    min-height: auto;
  }
}

@media (max-width: 640px) {
  .factory-page-header,
  .selected-version-header,
  .tab-toolbar,
  .section-heading,
  .diff-toolbar,
  .run-history-toolbar {
    align-items: stretch;
    flex-direction: column;
  }

  .factory-header-actions,
  .selected-version-actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .factory-header-actions :deep(.el-button),
  .selected-version-actions :deep(.el-button) {
    width: 100%;
    margin-left: 0;
  }

  .version-list {
    grid-template-columns: minmax(0, 1fr);
  }

  .version-row:nth-child(odd) {
    border-right: 0;
  }

  .selected-hash {
    align-items: flex-start;
    flex-wrap: wrap;
  }

  .selected-hash code {
    max-width: calc(100vw - 92px);
    white-space: normal;
    overflow-wrap: anywhere;
  }

  .compare-control {
    align-items: stretch;
    flex-direction: column;
  }

  .compare-control :deep(.el-select) {
    width: 100%;
  }

  .run-identity-row {
    align-items: flex-start;
  }

  .fingerprint-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .fingerprint-grid > div,
  .fingerprint-grid > div:nth-child(2n),
  .fingerprint-grid > div:nth-last-child(-n + 2) {
    border-right: 0;
    border-bottom: 1px solid #e7ecf3;
  }

  .fingerprint-grid > div:last-child {
    border-bottom: 0;
  }

  .diff-summary,
  .manifest-summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .diff-summary > div:nth-child(2),
  .manifest-summary > div:nth-child(2) {
    border-right: 0;
  }

  .diff-summary > div:nth-child(-n + 2),
  .manifest-summary > div:nth-child(-n + 2) {
    border-bottom: 1px solid #e7ecf3;
  }
}
</style>
