<template>
  <div class="project-detail">
    <!-- 加载态 -->
    <div v-if="loading" class="loading-state">{{ $t('common.loading') }}</div>
    <!-- 项目不存在 -->
    <div v-else-if="!currentProject" class="empty-state">
      <p>{{ $t('projectDetail.notFound') }}</p>
      <router-link to="/projects" class="btn btn-primary">{{ $t('projectDetail.backToList') }}</router-link>
    </div>
    <!-- 项目详情 -->
    <template v-else>
    <!-- 页面头部 -->
    <header class="page-header">
      <div class="header-left">
        <router-link to="/projects" class="back-link">{{ $t('projectDetail.backToList') }}</router-link>
        <h1>{{ currentProject.name }}</h1>
        <span v-if="statusLabel" class="stage-badge" :class="'stage-' + stageKey">{{ $t(statusLabel) }}</span>
      </div>
      <div class="header-actions">
        <button class="btn btn-ghost" @click="handleSync">{{ $t('projectDetail.sync') }}</button>
        <button class="btn btn-ghost" type="button" @click="handleImportDir">{{ $t('projectDetail.importDir') }}</button>
        <button class="btn btn-danger" @click="handleDelete">{{ $t('projectDetail.delete') }}</button>
        <button class="btn btn-primary" @click="showNewTaskModal = true">{{ $t('projectDetail.newTask') }}</button>
      </div>
    </header>

    <!-- 新建任务模态框 -->
    <div v-if="showNewTaskModal" class="modal-overlay" @click.self="showNewTaskModal = false">
      <div class="modal-box">
        <h3 class="modal-title">{{ $t('projectDetail.newTaskTitle') }}</h3>
        <div class="modal-field">
          <label>{{ $t('projectDetail.taskTitleLabel') }}</label>
          <input v-model="newTaskTitle" :placeholder="$t('projectDetail.taskTitlePlaceholder')" @keyup.enter="submitNewTask" />
        </div>
        <div class="modal-field">
          <label>{{ $t('projectDetail.descLabel') }}</label>
          <textarea v-model="newTaskDesc" :placeholder="$t('projectDetail.descPlaceholder')" rows="3"></textarea>
        </div>
        <div class="modal-field">
          <label>{{ $t('projectDetail.branchLabel') }}</label>
          <input v-model="newTaskBranch" placeholder="feature/xxx" />
        </div>
        <div class="modal-actions">
          <button class="btn btn-ghost" @click="showNewTaskModal = false">{{ $t('common.cancel') }}</button>
          <button class="btn btn-primary" @click="submitNewTask" :disabled="submitting || !newTaskTitle.trim()">{{ $t('projectDetail.confirmCreate') }}</button>
        </div>
      </div>
    </div>

    <!-- 阶段进度条已移除(阶段流转逻辑未接通,后续接入真实状态机后重启) -->

    <!-- Tab 导航(概览 / 文件浏览器) -->
    <nav class="detail-tabs">
      <button
        class="tab-btn"
        :class="{ 'tab-active': activeTab === 'overview' }"
        type="button"
        @click="setActiveTab('overview')"
      >
        {{ $t('projectDetail.tabOverview') }}
      </button>
      <button
        class="tab-btn"
        :class="{ 'tab-active': activeTab === 'files' }"
        type="button"
        @click="setActiveTab('files')"
      >
        {{ $t('fileExplorer.tabTitle') }}
      </button>
      <button
        class="tab-btn"
        :class="{ 'tab-active': activeTab === 'graph' }"
        type="button"
        @click="setActiveTab('graph')"
      >
        {{ $t('dependencyGraph.tabTitle') }}
      </button>
    </nav>

    <!-- 文件浏览器 Tab -->
    <section v-if="activeTab === 'files'" class="file-explorer-wrap">
      <FileExplorer :project-id="projectId" />
    </section>

    <!-- 依赖图 Tab -->
    <section v-else-if="activeTab === 'graph'" class="file-explorer-wrap">
      <DependencyGraph :project-id="projectId" />
    </section>

    <!-- 概览 Tab:两栏(项目信息 + 任务列表),工作流日志移至下方(P1-g+ 问题10 三栏拥挤) -->
    <div v-else class="detail-overview">
      <div class="detail-grid">
      <!-- 左栏：项目信息 -->
      <section class="panel">
        <div class="panel-header">
          <h2>{{ $t('projectDetail.infoTitle') }}</h2>
        </div>
        <div class="project-info" v-if="currentProject">
          <!-- 代码目录(绑定 + 重定位) -->
          <div class="info-item">
            <span class="label">{{ $t('projectDetail.codeDirLabel') }}</span>
            <div v-if="currentProject.path" class="path-row">
              <span class="path-text" :title="currentProject.path">{{ currentProject.path }}</span>
              <button class="btn btn-ghost btn-sm" type="button" @click="relocateDir">{{ $t('projectDetail.relocateDir') }}</button>
            </div>
            <div v-else class="path-row">
              <span class="no-idea">{{ $t('projectDetail.noDirBound') }}</span>
              <button class="btn btn-ghost btn-sm" type="button" @click="relocateDir">{{ $t('projectDetail.bindDir') }}</button>
            </div>
          </div>

          <!-- 目录状态(仅绑定时显示) -->
          <div class="info-item" v-if="currentProject.path">
            <span class="label">{{ $t('projectDetail.dirStatusLabel') }}</span>
            <span :class="pathExists === false ? 'path-missing' : 'path-ok'">
              {{ pathExists === null ? $t('projectDetail.dirChecking') : pathExists ? $t('projectDetail.dirExists') : $t('projectDetail.dirMissing') }}
            </span>
          </div>

          <!-- 技术栈 -->
          <div class="info-item" v-if="parseStack(currentProject.stack).length">
            <span class="label">{{ $t('projectDetail.techStackLabel') }}</span>
            <div class="info-tags">
              <span class="tech-tag" v-for="t in parseStack(currentProject.stack)" :key="t">{{ t }}</span>
            </div>
          </div>
          <div class="info-item">
            <span class="label">{{ $t('projectDetail.createdAt') }}</span>
            <span>{{ formatDate(currentProject.created_at) }}</span>
          </div>
          <div class="info-item">
            <span class="label">{{ $t('projectDetail.updatedAt') }}</span>
            <span>{{ formatDate(currentProject.updated_at) }}</span>
          </div>
          <div class="info-item info-block">
            <span class="label">{{ $t('projectDetail.description') }}</span>
            <!-- B-260615-25:项目描述 Markdown 渲染,复用 useMarkdown composable(同 B-24 TaskDetail)
                 P1-g+ 问题10③:长描述折叠(对齐 TaskDetail 长内容),默认折叠截 4 行,展开看全文 -->
            <div v-if="currentProject.description" class="description-wrap" :class="{ 'is-collapsed': !descExpanded }">
              <span
                class="value description ai-md"
                v-html="renderedDesc"
              ></span>
              <button
                v-if="descCollapsible"
                class="btn btn-ghost btn-sm desc-toggle"
                type="button"
                @click="descExpanded = !descExpanded"
              >
                {{ descExpanded ? $t('common.collapse') : $t('common.expand') }}
              </button>
            </div>
            <span v-else>—</span>
          </div>
          <div class="info-item">
            <span class="label">{{ $t('projectDetail.projectStatus') }}</span>
            <span class="status-badge" :class="currentProject.status">{{ $t(statusLabel) }}</span>
          </div>

          <!-- 来源灵感卡片(晋升携带评估结论回溯):仅 idea_id 有值时显示 -->
          <div v-if="currentProject.idea_id" class="source-idea-card">
            <div class="source-idea-header">
              <span class="source-idea-title">{{ $t('ideas.sourceIdea') }}</span>
              <router-link
                v-if="sourceIdea"
                class="idea-link"
                :to="`/ideas/${currentProject.idea_id}`"
              >
                {{ sourceIdea.title }} →
              </router-link>
              <router-link
                v-else
                class="idea-link idea-link-dim"
                :to="`/ideas/${currentProject.idea_id}`"
              >
                #{{ currentProject.idea_id }} →
              </router-link>
            </div>
            <!-- sourceIdea 未找到(灵感被删/未 load):提示跳转 -->
            <p v-if="!sourceIdea" class="source-idea-hint">{{ $t('projectDetail.ideaDeleted') }}</p>
            <!-- 已评估:显示评估结论回溯 -->
            <template v-else-if="sourceAiAnalysis">
              <div class="source-idea-body">
                <span
                  class="assessment-badge"
                  :class="assessmentClass(sourceAiAnalysis.recommendation)"
                >
                  {{ assessmentLabel(sourceAiAnalysis.recommendation) }}
                </span>
                <span class="source-final-score">
                  {{ $t('ideas.finalScore', { score: sourceAiAnalysis.final_score.toFixed(1) }) }}
                </span>
              </div>
              <p v-if="sourceAiAnalysis.summary" class="source-summary">{{ sourceAiAnalysis.summary }}</p>
              <!-- 多维评分条(借鉴 IdeaDetail parseScores 渲染) -->
              <div v-if="sourceScores.length" class="source-scores">
                <div class="score-bar-row" v-for="dim in sourceScores" :key="dim.name">
                  <span class="score-bar-label">{{ dim.name }}</span>
                  <div class="score-bar-track">
                    <div
                      class="score-bar-fill"
                      :class="'fill-' + scoreTier(dim.score)"
                      :style="{ width: dim.score + '%' }"
                    ></div>
                  </div>
                  <span class="score-bar-value">{{ dim.score }}</span>
                </div>
              </div>
            </template>
            <!-- 灵感未评估:鼓励去评估 -->
            <p v-else class="source-idea-hint">
              <router-link class="idea-link" :to="`/ideas/${currentProject.idea_id}`">
                {{ $t('ideas.sourceIdeaNotEvaluated') }} →
              </router-link>
            </p>
          </div>

        </div>
      </section>

      <!-- 右栏：任务列表(本项目独立 fetch,不读 store.tasks,避免 Tasks 分页污染) -->
      <section class="panel">
        <div class="panel-header">
          <h2>{{ $t('projectDetail.taskListTitle') }}</h2>
          <span class="task-count">{{ $t('projectDetail.taskCount', { n: projectTasks.length }) }}</span>
        </div>
        <div class="task-list">
          <div class="task-card" v-for="task in projectTasks" :key="task.id">
            <div class="task-top">
              <span class="task-title">{{ task.title }}</span>
              <span class="task-status" :class="taskStatusClass(task.status)">{{ $t(taskStatusLabel(task.status)) }}</span>
            </div>
            <div class="task-branch" v-if="task.branch_name">
              <span class="branch-icon">⑂</span>
              <span class="branch-name">{{ task.branch_name }}</span>
            </div>
            <div class="task-meta">
              <span>{{ task.assignee ?? '—' }}</span>
              <span>{{ formatDate(task.created_at) }}</span>
              <span style="opacity: 0.7">{{ formatDate(task.updated_at) }}</span>
            </div>
          </div>
        </div>
        <div v-if="projectTasks.length === 0" class="empty-hint">{{ $t('projectDetail.emptyTasks') }}</div>
      </section>
      </div>

      <!-- 工作流日志:无记录时折叠为摘要(标题 + 计数),有记录才展开列表 -->
      <section v-if="formattedEvents.length > 0 || !workflowLogCollapsed" class="panel workflow-log-panel">
        <div class="panel-header">
          <h2>{{ $t('projectDetail.workflowLogTitle') }}</h2>
          <span v-if="formattedEvents.length === 0" class="task-count">{{ $t('projectDetail.emptyWorkflowLog') }}</span>
          <button
            v-else
            class="btn btn-ghost btn-sm"
            type="button"
            @click="workflowLogCollapsed = !workflowLogCollapsed"
          >
            {{ workflowLogCollapsed ? $t('common.expand') : $t('common.collapse') }}
          </button>
        </div>
        <div v-if="formattedEvents.length > 0 && !workflowLogCollapsed" class="log-list" ref="logListRef">
          <div class="log-item" v-for="(evt, idx) in formattedEvents" :key="idx" :class="'log-' + evt.level">
            <span class="log-time">{{ evt.time }}</span>
            <span class="log-level">{{ evt.level }}</span>
            <span class="log-msg">{{ evt.message }}</span>
          </div>
        </div>
      </section>
    </div>

    <!-- 人工审批对话框(抽离至 components/project/ApprovalDialog.vue)-->
    <ApprovalDialog
      :visible="showApprovalDialog"
      :store="store"
      @cancel="handleCancelApproval"
      @later="showApprovalDialog = false"
    />

    <!-- 确认弹层（删除/重定位确认，替代原生 window.confirm/alert） -->
    <ConfirmDialog :visible="confirmState.visible" :msg="confirmState.msg" @result="answerConfirm" />
    </template>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { Message } from '@arco-design/web-vue'
import { open } from '@tauri-apps/plugin-dialog'
import { useProjectStore } from '@/stores/project'
import { projectApi, taskApi } from '@/api'
import { formatDate } from '@/utils/time'
import { parseStack } from '@/utils/project'
import { parseScores as parseScoresJson, assessmentClass, assessmentLabel as assessmentLabelI18n, scoreTier } from '@/utils/ideaEval'
import { projectStatusLabel, taskStatusLabel, taskStatusClass } from '../constants/project'
import ConfirmDialog from '@/components/ConfirmDialog.vue'
import ApprovalDialog from '@/components/project/ApprovalDialog.vue'
import FileExplorer from '@/components/project/FileExplorer.vue'
import DependencyGraph from '@/components/project/DependencyGraph.vue'
import { useConfirm } from '@/composables/useConfirm'
import { useRendered } from '@/composables/useMarkdown'
import type { ProjectId, TaskRecord } from '@/api/types'

const route = useRoute()
const router = useRouter()
const store = useProjectStore()
const { t, locale } = useI18n()
const logListRef = ref<HTMLElement | null>(null)
const showApprovalDialog = ref(false)
let unlisten: (() => void) | null = null

// Tab 导航:概览(项目信息/任务/工作流日志) vs 文件浏览器(Batch 10)。
// P1-g+ 问题10④:activeTab 持久化 localStorage(按项目隔离,切换项目不串扰)。
const TAB_STORAGE_PREFIX = 'df-project-tab-'
const validTabs = ['overview', 'files', 'graph'] as const
type DetailTab = typeof validTabs[number]
function readStoredTab(projectId: string): DetailTab {
  try {
    const v = localStorage.getItem(TAB_STORAGE_PREFIX + projectId)
    return (v && (validTabs as readonly string[]).includes(v)) ? (v as DetailTab) : 'overview'
  } catch { return 'overview' }
}
const activeTab = ref<DetailTab>('overview')
function setActiveTab(tab: DetailTab) {
  activeTab.value = tab
  try { localStorage.setItem(TAB_STORAGE_PREFIX + projectId.value, tab) } catch { /* ignore */ }
}

// 描述折叠(P1-g+ 问题10③):长描述默认折叠截 4 行,展开看全文。
// descCollapsible 由描述长度阈值决定(渲染后行数不可靠,用源字符数近似 ≥120 字符)。
const DESC_COLLAPSE_THRESHOLD = 120
const descExpanded = ref(false)
const descCollapsible = computed(() => (currentProject.value?.description?.length ?? 0) >= DESC_COLLAPSE_THRESHOLD)

// 工作流日志折叠(P1-g+ 问题10②):无记录时折叠为摘要(标题 + emptyWorkflowLog 提示),
// 有记录默认展开。collapse 由用户主动操作,避免空态占整栏。
const workflowLogCollapsed = ref(false)

// 确认弹层状态机抽至 composables/useConfirm(原 4 视图重复:Projects/ProjectDetail/Ideas/Settings)
const { confirmState, confirmDialog, answerConfirm } = useConfirm()



// ── 当前项目 ──
// 状态文案/阶段进度统一走 ../constants/project(与 Projects/Tasks/Dashboard 一致,
// 根治此前 stageMap 含不存在的 testing 状态、三套映射互相矛盾)
const projectId = computed(() => route.params.id as string)
const currentProject = computed(() =>
  store.projects.find(p => p.id === projectId.value)
)
const loading = computed(() => store.projects.length === 0 && !store.error)

// 状态文案/阶段进度统一走 ../constants/project
// B-260615-25:项目描述 Markdown 渲染(复用 AiChat/TaskDetail 同款渲染器,模块级单例),
// useRendered 封装 computed(读 mdReady 触发响应式 + renderMd)+ ensureLoaded(幂等预热)
const { rendered: renderedDesc, ensureLoaded } = useRendered(
  () => currentProject.value?.description ?? '',
)
const stageKey = computed(() => currentProject.value?.status ?? 'planning')
const statusLabel = computed(() => projectStatusLabel(stageKey.value))
// const currentStageIndex = computed(() => projectStageInfo(stageKey.value).stepIndex)

// ── 来源灵感(晋升携带评估结论回溯)──
// ProjectRecord.idea_id 存灵感 id(promote_idea 写入)。
// sourceIdea: 从 store.ideas 反查;store 未 load 时由 onMounted 守卫补 load。
const sourceIdea = computed(() =>
  store.ideas.find(i => i.id === currentProject.value?.idea_id) ?? null,
)

// ai_analysis / scores 是 JSON 字符串(对齐 IdeaDetail 现有解析模式,try/catch 容错)
interface SourceAnalysis {
  recommendation: string
  final_score: number
  summary: string
}
const sourceAiAnalysis = computed<SourceAnalysis | null>(() => {
  const idea = sourceIdea.value
  if (!idea?.ai_analysis) return null
  try {
    const parsed = JSON.parse(idea.ai_analysis) as Record<string, unknown>
    if (typeof parsed !== 'object' || parsed === null) return null
    const recommendation = typeof parsed.recommendation === 'string' ? parsed.recommendation : ''
    const final_score = typeof parsed.final_score === 'number' ? parsed.final_score : 0
    // summary 优先顶层,回退 analyst.summary(对齐 IdeaDetail historySummary 模式)
    const summary = typeof parsed.summary === 'string' ? parsed.summary
      : (typeof parsed.analyst === 'object' && parsed.analyst !== null
          && typeof (parsed.analyst as Record<string, unknown>).summary === 'string'
            ? ((parsed.analyst as Record<string, unknown>).summary as string)
            : '')
    if (!recommendation && !final_score && !summary) return null
    return { recommendation, final_score, summary }
  } catch {
    return null
  }
})

// 项1 DRY:ScoreDimension/parseScores/assessmentClass/assessmentLabel 抽到 @/utils/ideaEval,
// 与 IdeaDetail 共用。sourceScores 仅保留 computed(读 sourceIdea.scores → 复用纯函数解析)。
const sourceScores = computed(() => parseScoresJson(sourceIdea.value?.scores))

// assessmentClass 直接复用 ideaEval(模板直调);assessmentLabel 需注入 t,留薄包装消除重复。
const assessmentLabel = (recommendation: string): string =>
  assessmentLabelI18n(t, recommendation)

// ── 任务 ──
// P1-g+ 问题10①:projectTasks 不再读 store.tasks(被 Tasks 视图分页/筛选污染),
// 改为按 project_id 走后端 list_tasks {project_id} 独立拉取,存本地 ref。
// TaskQuery.project_id 命中 idx_tasks_project_id(见 api/types.ts:191)。
const projectTasks = ref<TaskRecord[]>([])
async function loadProjectTasks() {
  try {
    projectTasks.value = await taskApi.list({ project_id: projectId.value })
  } catch (e) {
    console.error('加载项目任务失败:', e)
    projectTasks.value = []
  }
}
// taskStatusLabel / taskStatusClass 由 ../constants/project 提供

// ── 工作流 ──
// demoDag + runDemoWorkflow 已下线（R-PD-2）："script" 节点不再注册到 NodeRegistry，
// 前端构造含 script 节点的 DagDef 会在后端 build_dag 失败报错。
// 工作流执行日志面板保留，实时事件展示能力不依赖 demoDag。
// 待真实工作流需求落地（独立 BuildNode + 审批链）时重建演示入口。

// ── 实时日志格式化 ──
interface LogEntry {
  time: string
  level: string
  message: string
}

const formattedEvents = computed<LogEntry[]>(() => {
  return store.liveEvents.map((evt) => {
    const ev = evt.event
    const type = ev.type ?? 'unknown'
    const level = type.includes('error') || type.includes('fail') ? 'error'
      : type.includes('warn') ? 'warn' : 'info'
    // 用事件入数组时固化的时间戳(FR-C1),非 computed 重算的当前时刻
    const time = new Date(evt._ts).toLocaleTimeString(locale.value, { hour: '2-digit', minute: '2-digit', second: '2-digit' })
    const message = ev.label
      ? `[${ev.label}] ${type}: ${ev.output ?? ev.code ?? ev.message ?? JSON.stringify(ev)}`
      : `${type}: ${ev.output ?? ev.code ?? ev.message ?? JSON.stringify(ev)}`
    return { time, level, message }
  })
})

// ── 新建任务 ──
const showNewTaskModal = ref(false)
const newTaskTitle = ref('')
const newTaskDesc = ref('')
const newTaskBranch = ref('')

// 异步操作禁用态(IPC 期间防双击重复提交):submitNewTask 创建任务期间禁用确认按钮
const submitting = ref(false)

async function submitNewTask() {
  if (!newTaskTitle.value.trim()) return
  submitting.value = true
  try {
    const r = await store.createTask({
      project_id: projectId.value as ProjectId,
      title: newTaskTitle.value.trim(),
      description: newTaskDesc.value.trim(),
      branch_name: newTaskBranch.value.trim() || undefined,
    })
    if (!r) return // 失败已 toast,保持弹窗不关
    // 同步到本地 ref(store.createTask 已 push 全局 tasks,但本视图不再读 store.tasks)
    projectTasks.value = [r, ...projectTasks.value]
    showNewTaskModal.value = false
    newTaskTitle.value = ''
    newTaskDesc.value = ''
    newTaskBranch.value = ''
  } finally {
    submitting.value = false
  }
}

// ── 同步 ──
async function handleSync() {
  await store.loadProjects()
  await loadProjectTasks() // 本项目任务独立拉取,不依赖 store.loadTasks
  await checkPath()
}

// ── 代码目录绑定 ──
const pathExists = ref<boolean | null>(null)

// 检查绑定目录是否存在(详情页「目录是否还在」)
async function checkPath() {
  const p = currentProject.value
  if (!p?.path) { pathExists.value = null; return }
  try {
    pathExists.value = await projectApi.checkPathExists(p.path)
  } catch {
    pathExists.value = null
  }
}

// 重定位/绑定目录(选目录 → 查重 → 确认 → 后端重探测 stack)
async function relocateDir() {
  const p = currentProject.value
  if (!p) return
  try {
    const selected = await open({ directory: true, multiple: false })
    if (!selected || Array.isArray(selected)) return
    const dir = selected as string
    // 查重(排除自身)
    try {
      const conflict = await projectApi.checkBinding(dir, p.id)
      if (conflict) {
        Message.warning(t('projectDetail.dirConflict', { name: conflict.name }))
        return
      }
    } catch { /* ignore */ }
    const confirmMsg = p.path
      ? t('projectDetail.relocateConfirmRelocate', { dir })
      : t('projectDetail.relocateConfirmBind', { dir })
    if (!await confirmDialog(confirmMsg)) return
    await store.relocateProjectPath(p.id, dir)
    await checkPath()
  } catch (e: any) {
    console.error('重定位失败:', e)
    Message.error(t('projectDetail.relocateFailed', { msg: e?.toString() ?? t('common.unknownError') }))
  }
}

// 导入历史项目(选已存在目录 → 后端创建实体+绑定+探测栈+读 README 首段一步完成)
async function handleImportDir() {
  try {
    const selected = await open({ directory: true, multiple: false })
    if (!selected || Array.isArray(selected)) return
    const dir = selected as string
    if (!await confirmDialog(t('projectDetail.importConfirm', { dir }))) return
    const record = await store.importProject({ path: dir })
    if (!record) {
      // store 已 toast 错误
      if (store.error) Message.error(store.error)
      return
    }
    Message.success(t('projectDetail.importSuccess', { name: record.name }))
    // 跳转到新导入项目的详情页
    router.push(`/projects/${record.id}`)
  } catch (e: any) {
    console.error('导入失败:', e)
    Message.error(t('projectDetail.importFailed', { msg: e?.toString() ?? t('common.unknownError') }))
  }
}

// ── 审批处理 ──
// 审批对话框 UI + 单/多选决策逻辑抽离至 components/project/ApprovalDialog.vue
// handleApproval/handleApprovalMulti/isMultipleSelect/multiDecisions 已迁移;
// submitting(UX-260618-17)在子组件自治,父级 submitting 仅用于 submitNewTask

// ── 取消审批（调 cancel_workflow_node IPC 置节点 Cancelled）──
async function handleCancelApproval() {
  await store.cancelHumanApproval()
  showApprovalDialog.value = false
}

// ── 删除项目(软删 → 回收站,可恢复)──
async function handleDelete() {
  const p = currentProject.value
  if (!p) return
  if (!await confirmDialog(t('projectDetail.confirmDelete', { name: p.name }))) return
  await store.deleteProject(p.id)
  router.push('/projects')
}

// 日志自动滚到底部(让 logListRef 不再是死 ref)
watch(formattedEvents, () => {
  if (logListRef.value) logListRef.value.scrollTop = logListRef.value.scrollHeight
})

// 监听审批请求(pendingApproval → 开弹窗;多选清空已在 ApprovalDialog 内 watch)
watch(() => store.pendingApproval, (newApproval) => {
  if (newApproval) {
    showApprovalDialog.value = true
  }
}, { immediate: true })

// ── 工具函数 ──
// formatDate 由 ../utils/time 提供（统一毫秒字符串解析，根治 Invalid Date）

// ── 生命周期 ──
onMounted(async () => {
  ensureLoaded() // 后台预热 Markdown 渲染器(模块单例,与 AiChat/TaskDetail 共享),不阻塞
  // 恢复持久化的 Tab 选择(按项目隔离)
  activeTab.value = readStoredTab(projectId.value)
  await store.loadProjects()
  // 本项目任务独立拉取(不调 store.loadTasks,避免被 Tasks 视图分页/筛选污染)
  await loadProjectTasks()
  // 来源灵感回溯:项目有 idea_id 但 store.ideas 为空(本页未 load 过)时补拉,
  // 否则 sourceIdea computed 永远找不到对应灵感记录
  await store.loadIdeas()
  unlisten = await store.startEventListener()
  await checkPath()
})

// 路由切换到不同项目(URL /projects/:id 变化)时,重新拉取本项目任务 + 恢复 Tab + 检测目录
watch(projectId, async (newId) => {
  if (!newId) return
  activeTab.value = readStoredTab(newId)
  await loadProjectTasks()
})

// 目录变更(重定位后)重新检测存在性
watch(() => currentProject.value?.path, () => { checkPath() })

onUnmounted(() => {
  if (unlisten) {
    unlisten()
    unlisten = null
  }
})
</script>

<style scoped>
/* ... 现有样式 ... */

/* 加载/空态(模板顶部使用) */
.loading-state { text-align: center; padding: 40px; color: var(--df-text-dim); }
/* 覆盖全局 .empty-state:带图标+文字垂直排列,缩短 padding */
.empty-state { padding: 40px; display: flex; flex-direction: column; align-items: center; gap: 12px; }

/* ===== 项目信息样式 ===== */
.project-info {
  display: flex;
  flex-direction: column;
  gap: var(--df-gap-grid);
}

/* B-260615-31:字段同行布局(label 固定宽 + 值占余),描述字段 info-block 保持块状)
   基础 .info-item/.label/.value 已收敛至全局 components.css(DRY 收口 B-260619),
   此处仅保留本组件特有覆盖。 */
.info-item a.idea-link {
  color: var(--df-accent);
  text-decoration: none;
  font-weight: 500;
}

.info-item a.idea-link:hover {
  text-decoration: underline;
}

.info-item .no-idea {
  color: var(--df-text-dim);
  font-style: italic;
}

.status-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--df-radius-lg);
  font-size: 11px;
  font-weight: 500;
  background: rgba(108,99,255,0.1);
  color: var(--df-accent);
}

/* ===== 代码目录 / 技术栈 ===== */
.path-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.path-text {
  font-size: 12px; font-family: 'SF Mono', 'Fira Code', monospace;
  color: var(--df-text-secondary); word-break: break-all;
}
.path-ok { color: var(--df-success); font-size: 12px; }
.path-missing { color: var(--df-danger); font-size: 12px; }
.info-tags { display: flex; flex-wrap: wrap; gap: 6px; }
/* .info-tags .tech-tag 与全局 .tech-tag(components.css)逐字一致,移除局部冗余,
   标签样式由全局提供。 */

/* ===== 项目描述 Markdown 渲染(B-260615-25,基础样式收敛至全局 ai-md.css) ===== */
.description.ai-md { font-size: 14px; color: var(--df-text); line-height: 1.6; }
/* P1-g+ 问题10③:描述折叠(对齐 TaskDetail 长内容)— 折叠时 max-height 截断 + 渐变遮罩 */
.description-wrap { position: relative; }
.description-wrap.is-collapsed .description {
  max-height: 6em; /* 约 4 行(line-height 1.5) */
  overflow: hidden;
}
.desc-toggle { margin-top: 6px; }

/* ===== 来源灵感卡片(晋升携带评估结论回溯)===== */
.source-idea-card {
  margin-top: 8px;
  padding: 12px 14px;
  background: var(--df-bg-raised);
  border: 0.5px solid var(--df-border);
  border-left: 3px solid var(--df-accent);
  border-radius: var(--df-radius);
}
.source-idea-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}
.source-idea-title { font-size: 13px; font-weight: 500; color: var(--df-text); }
.info-item a.idea-link.idea-link-dim,
.source-idea-card a.idea-link-dim { color: var(--df-text-dim); }
.source-idea-card .source-idea-hint {
  font-size: 12px;
  color: var(--df-text-dim);
  margin: 4px 0 0;
}
.source-idea-body {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 6px 0;
}
.source-final-score {
  font-size: 13px;
  font-weight: 500;
  color: var(--df-text);
}
.source-summary {
  font-size: 12px;
  color: var(--df-text-secondary);
  line-height: 1.5;
  margin: 6px 0;
}

/* assessment-badge 系列(基础 + immediate/soon/conditional/revised/defer/cancel)
   已提取到 styles/components.css 全局 */

/* 多维评分条(.score-bar-* 系列)已提取到 styles/components.css 全局 */
.source-scores {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 8px;
}

</style>

<style scoped>
.project-detail { padding: 16px 20px 20px; display: flex; flex-direction: column; min-height: 0; flex: 1; max-height: 100%; overflow: hidden; }

/* ===== Tab 导航(Batch 10 文件浏览器) ===== */
.detail-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 0.5px solid var(--df-border);
  margin-bottom: 16px;
}
.tab-btn {
  padding: 8px 16px;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--df-text-dim);
  font-size: 13px;
  cursor: pointer;
  transition: all 0.15s;
  margin-bottom: -0.5px;
}
.tab-btn:hover {
  color: var(--df-text);
}
.tab-btn.tab-active {
  color: var(--df-text);
  border-bottom-color: var(--df-accent);
}

/* 文件浏览器容器(弹性填充,内部 FileExplorer 自管布局) */
.file-explorer-wrap {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

/* .page-header / .page-header h1 已提取到 styles/global.css 全局 */
.header-left { display: flex; align-items: center; gap: 12px; }
/* .back-link / .back-link:hover 已提取到 styles/global.css 全局 */

.stage-badge {
  font-size: 12px;
  padding: 3px 10px;
  border-radius: var(--df-radius-lg);
  font-weight: 500;
}
.stage-planning { background: rgba(100,181,246,0.15); color: #64b5f6; }
.stage-in_progress { background: rgba(108,99,255,0.15); color: #6c63ff; }
.stage-testing { background: rgba(255,193,7,0.15); color: #ffc107; }
.stage-releasing { background: rgba(156,39,176,0.15); color: #ce93d8; }
.stage-completed { background: rgba(100,200,100,0.15); color: #64c864; }
.stage-paused { background: rgba(158,158,158,0.15); color: #9e9e9e; }
.stage-cancelled { background: rgba(244,67,54,0.15); color: #f44336; }

/* .header-actions 已提取到 styles/global.css 全局 */

/* .btn 系列(btn/btn-primary/btn-ghost/btn-sm/btn-danger)已提取到 styles/global.css 全局 */

/* ===== 概览布局(P1-g+ 问题10②:三栏拥挤 → 两栏 + 日志下方)=====
   .detail-overview 整体滚动;内部 .detail-grid 两栏(项目信息 + 任务列表),
   .workflow-log-panel 跨整宽放下方。无日志记录时折叠为摘要不占整栏。 */
.detail-overview {
  display: flex;
  flex-direction: column;
  gap: var(--df-gap-page);
  overflow-y: auto;
  min-height: 0;
  max-height: 100%;
}
/* 两栏布局 — 项目信息 + 任务列表 */
.detail-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--df-gap-page);
  align-content: start;
}
.workflow-log-panel { flex: 0 0 auto; }

/* ===== 面板 ===== */
/* .panel / .panel-header 基础样式已收敛至全局 components.css(DRY 收口 B-260619),
   此处仅保留本组件特有 .task-count。 */
.task-count { font-size: 12px; color: var(--df-text-dim); }

/* ===== 任务卡片 ===== */
.task-list { display: flex; flex-direction: column; }
.task-card {
  padding: 14px 0;
  border-bottom: 0.5px solid var(--df-border);
}
.task-card:last-child { border-bottom: none; }
.task-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
.task-title { font-size: 14px; font-weight: 500; color: var(--df-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.task-status { font-size: 11px; padding: 2px 8px; border-radius: var(--df-radius-xs); }
.status-done { background: rgba(100,255,218,0.15); color: var(--df-success); }
.status-progress { background: rgba(108,99,255,0.15); color: var(--df-accent); }
.status-review { background: rgba(255,217,61,0.15); color: var(--df-warning); }
.status-todo { background: rgba(90,99,128,0.2); color: var(--df-text-dim); }

.task-branch {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-bottom: 6px;
}
.branch-icon { color: var(--df-accent); font-size: 13px; }
.branch-name {
  font-size: 12px;
  font-family: 'SF Mono', 'Fira Code', monospace;
  color: var(--df-text-secondary);
  background: rgba(108,99,255,0.1);
  padding: 1px 6px;
  border-radius: var(--df-radius-sm);
}
.task-meta {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--df-text-dim);
}

/* ===== 日志 ===== */
.log-list {
  font-family: 'SF Mono', 'Fira Code', monospace;
  font-size: 12px;
  max-height: 280px;
  overflow-y: auto;
}
.log-item {
  display: flex;
  gap: 8px;
  padding: 5px 0;
  border-bottom: 0.5px solid rgba(42,42,74,0.5);
}
.log-item:last-child { border-bottom: none; }
.log-time { color: var(--df-text-dim); min-width: 64px; }
.log-level {
  min-width: 42px;
  font-weight: 500;
  text-transform: uppercase;
  font-size: 10px;
  padding: 1px 4px;
  border-radius: var(--df-radius-sm);
  text-align: center;
}
.log-info .log-level { color: var(--df-info); }
.log-warn .log-level { color: var(--df-warning); }
.log-error .log-level { color: var(--df-danger); }
.log-msg { color: var(--df-text-secondary); flex: 1; }

/* ===== 项目信息(左栏 .project-info/.info-item,右栏重复面板已删)===== */

/* .modal-* 系列(modal-overlay/modal-box/modal-title/modal-field/modal-actions)
   及 .btn:disabled 已提取到 styles/global.css 全局 */

.empty-hint {
  text-align: center; padding: 24px 12px;
  font-size: 13px; color: var(--df-text-dim);
}

/* ===== 响应式 ===== */
@media (max-width: 768px) {
  .project-detail { padding: 16px; }
  .detail-grid {
    grid-template-columns: 1fr;
  }
  .log-list { max-height: 200px; }
}
</style>
