<script setup lang="ts">
import { computed, nextTick, onMounted, ref, watch } from "vue";
import { ChevronLeft, ChevronRight, Maximize2, PanelRight, X } from "lucide-vue-next";
import type { TaskTreeReport, TaskTreeTask } from "@/api/tasktree";
import ContentViewer from "@/components/common/ContentViewer.vue";
import { displayTimeLabel, formatDisplayDateTime } from "@/config/runtime";

const props = defineProps<{
  task: TaskTreeTask;
  tasks: TaskTreeTask[];
  reports: TaskTreeReport[];
  mode: "dialog" | "docked";
}>();
const emit = defineEmits<{
  select: [task: TaskTreeTask];
  close: [];
  mode: [mode: "dialog" | "docked"];
}>();
const detailTreeElement = ref<HTMLElement | null>(null);

const taskIndex = computed(() => props.tasks.findIndex((task) => task.id === props.task.id));
const previousTask = computed(() => taskIndex.value > 0 ? props.tasks[taskIndex.value - 1] ?? null : null);
const nextTask = computed(() => taskIndex.value >= 0 ? props.tasks[taskIndex.value + 1] ?? null : null);
const parent = computed(() => props.task.parentId ? props.tasks.find((task) => task.id === props.task.parentId) ?? null : null);
const siblings = computed(() => props.tasks.filter((task) => task.parentId === props.task.parentId));
const children = computed(() => props.tasks.filter((task) => task.parentId === props.task.id));
const localTreeItems = computed(() => {
  const items: Array<{
    task: TaskTreeTask;
    relation: "parent" | "current" | "sibling" | "child";
    level: number;
  }> = [];
  if (parent.value) items.push({ task: parent.value, relation: "parent", level: 0 });
  for (const sibling of siblings.value) {
    const current = sibling.id === props.task.id;
    items.push({ task: sibling, relation: current ? "current" : "sibling", level: parent.value ? 1 : 0 });
    if (current) {
      for (const child of children.value) {
        items.push({ task: child, relation: "child", level: parent.value ? 2 : 1 });
      }
    }
  }
  return items;
});
const taskReports = computed(() => props.reports.filter((report) => report.taskId === props.task.id));
const combinedMarkdown = computed(() => {
  const description = props.task.description.trim() || "未填写任务说明。";
  const reports = taskReports.value.length
    ? taskReports.value.map((report) => [
      `### ${plainReportTitle(report)}`,
      `_${dateLabel(report.createdAt)}_`,
      reportMarkdown(report)
    ].join("\n\n")).join("\n\n---\n\n")
    : "尚无执行报告。";
  return ["## 任务说明", description, "## 执行报告", reports].join("\n\n");
});

function plainTitle(task: TaskTreeTask) {
  return task.title.replace(/^R\d+(?:\.\d+){0,2}\s+/u, "").replace(/\[([^\]]+)\]\([^)]+\)/gu, "$1").replace(/`([^`]+)`/gu, "$1").trim();
}
function plainReportTitle(report: TaskTreeReport) {
  return report.title.replace(/\[([^\]]+)\]\([^)]+\)/gu, "$1").replace(/`([^`]+)`/gu, "$1").trim();
}
function kindLabel(kind: TaskTreeTask["kind"]) { return ({ task: "Task", subtask: "Subtask", subsubtask: "Subsubtask" })[kind]; }
function relationLabel(relation: "parent" | "current" | "sibling" | "child") {
  return ({ parent: "父节点", current: "当前任务", sibling: "兄弟任务", child: "子节点" })[relation];
}
function dateLabel(value: string | null) { return value ? formatDisplayDateTime(value) : "未设置"; }
function reportMarkdown(report: TaskTreeReport) {
  const lines = report.body.replace(/\r\n?/gu, "\n").split("\n");
  const firstContent = lines.findIndex((line) => line.trim().length > 0);
  if (firstContent < 0) return report.body;
  const heading = lines[firstContent].match(/^#{1,6}\s+(.+?)\s*#*\s*$/u);
  if (!heading || normalizeHeading(heading[1]) !== normalizeHeading(report.title)) return report.body;
  lines.splice(firstContent, 1);
  return lines.join("\n").trimStart();
}
function normalizeHeading(value: string) { return value.replace(/\s+/gu, "").toLocaleLowerCase(); }
async function revealCurrentTask() {
  await nextTick();
  const tree = detailTreeElement.value;
  const row = tree?.querySelector<HTMLElement>("[data-current-task='true']");
  if (!tree || !row) return;
  const treeRect = tree.getBoundingClientRect();
  const rowRect = row.getBoundingClientRect();
  const target = tree.scrollTop + rowRect.top - treeRect.top - tree.clientHeight * 0.18;
  tree.scrollTop = Math.max(0, Math.min(tree.scrollHeight - tree.clientHeight, target));
}

onMounted(revealCurrentTask);
watch(() => props.task.id, revealCurrentTask);
</script>

<template>
  <div class="tasktree-detail-panel" :data-mode="mode">
    <header v-if="mode === 'docked'" class="tasktree-docked-header">
      <div><small>{{ task.outlineRef }} · {{ kindLabel(task.kind) }}</small><strong :title="plainTitle(task)">{{ plainTitle(task) }}</strong></div>
      <button type="button" title="切换到弹窗" aria-label="切换到弹窗" @click="emit('mode', 'dialog')"><Maximize2 :size="16" aria-hidden="true" /></button>
      <button type="button" title="关闭详情" aria-label="关闭详情" @click="emit('close')"><X :size="17" aria-hidden="true" /></button>
    </header>
    <div class="tasktree-detail-shell">
      <aside v-if="mode === 'dialog'" ref="detailTreeElement" class="tasktree-detail-tree" aria-label="局部任务层级">
        <h3>局部任务</h3>
        <ol class="tasktree-local-tree">
          <li
            v-for="item in localTreeItems"
            :key="item.task.id"
            :class="[`level-${item.level}`, `relation-${item.relation}`, { current: item.relation === 'current' }]"
            :data-current-task="item.relation === 'current' ? 'true' : undefined"
          >
            <button v-if="item.relation !== 'current'" type="button" @click="emit('select', item.task)"><small>{{ item.task.outlineRef }} · {{ relationLabel(item.relation) }} · {{ kindLabel(item.task.kind) }}</small><strong>{{ plainTitle(item.task) }}</strong></button>
            <button v-else class="current" type="button" aria-current="true"><small>{{ item.task.outlineRef }} · {{ relationLabel(item.relation) }} · {{ kindLabel(item.task.kind) }}</small><strong>{{ plainTitle(item.task) }}</strong></button>
          </li>
        </ol>
      </aside>
      <div class="tasktree-detail-main">
        <div class="tasktree-detail-content">
          <ContentViewer class="tasktree-detail-document" :content="combinedMarkdown" mode="markdown" title="任务说明与执行报告" :filename="`tasktree-${task.id}.md`" :line-numbers="false" />
        </div>
      </div>
    </div>
    <footer class="tasktree-detail-statusbar">
      <div class="tasktree-detail-pager" aria-label="前后任务导航">
        <button type="button" :disabled="!previousTask" title="上一个任务" aria-label="上一个任务" @click="previousTask && emit('select', previousTask)"><ChevronLeft :size="15" aria-hidden="true" /></button>
        <output>{{ taskIndex + 1 }} / {{ tasks.length }}</output>
        <button type="button" :disabled="!nextTask" title="下一个任务" aria-label="下一个任务" @click="nextTask && emit('select', nextTask)"><ChevronRight :size="15" aria-hidden="true" /></button>
      </div>
      <span>编号 <strong>{{ task.outlineRef }}</strong></span><span>开始 <strong>{{ dateLabel(task.startAt) }}</strong></span><span>截止 <strong>{{ dateLabel(task.dueAt) }}</strong></span><span>时区 <strong>{{ displayTimeLabel() }}</strong></span><span>状态 <strong>{{ task.status }}</strong></span>
      <button v-if="mode === 'dialog'" class="tasktree-detail-dock-button" type="button" title="吸附到右侧" aria-label="吸附到右侧" @click="emit('mode', 'docked')"><PanelRight :size="15" aria-hidden="true" /></button>
    </footer>
  </div>
</template>

<style scoped>
.tasktree-detail-panel { display: grid; grid-template-rows: minmax(0, 1fr) 32px; width: 100%; height: 100%; min-width: 0; min-height: 0; background: var(--surface-primary); }
.tasktree-detail-panel[data-mode="docked"] { grid-template-rows: 48px minmax(0, 1fr) 32px; }
.tasktree-docked-header { display: flex; min-width: 0; align-items: center; gap: 6px; padding: 7px 9px 7px 12px; border-bottom: 1px solid var(--border-color); }
.tasktree-docked-header > div { display: grid; min-width: 0; flex: 1 1 auto; gap: 1px; }
.tasktree-docked-header small { color: var(--text-secondary); font-size: 9px; }
.tasktree-docked-header strong { overflow: hidden; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
.tasktree-docked-header button { display: grid; width: 30px; height: 30px; flex: 0 0 auto; place-items: center; padding: 0; border: 1px solid var(--border-color); border-radius: 4px; background: var(--surface-primary); color: var(--text-secondary); cursor: pointer; }
.tasktree-detail-shell { display: grid; grid-template-columns: minmax(160px, 24%) minmax(0, 1fr); min-height: 0; }
.tasktree-detail-panel[data-mode="docked"] .tasktree-detail-shell { grid-template-columns: minmax(0, 1fr); }
.tasktree-detail-tree { min-width: 0; min-height: 0; padding: 14px 10px; overflow: auto; border-right: 1px solid var(--border-color); background: color-mix(in srgb, var(--surface-secondary) 72%, var(--surface-primary)); scrollbar-width: thin; }
.tasktree-detail-tree h3 { margin: 0 6px 14px; font-size: 12px; }
.tasktree-local-tree { display: grid; gap: 3px; margin: 0; padding: 0; list-style: none; }
.tasktree-local-tree li { position: relative; min-width: 0; }
.tasktree-local-tree li.level-1 { padding-left: 15px; }
.tasktree-local-tree li.level-2 { padding-left: 30px; }
.tasktree-local-tree li.level-1::before,
.tasktree-local-tree li.level-2::before { position: absolute; top: 0; bottom: 0; left: 6px; width: 1px; background: var(--border-color); content: ""; }
.tasktree-local-tree li.level-2::before { left: 21px; box-shadow: -15px 0 0 var(--border-subtle); }
.tasktree-local-tree li.relation-parent { margin-bottom: 5px; padding-bottom: 6px; border-bottom: 1px solid var(--border-subtle); }
.tasktree-detail-tree button { display: grid; width: 100%; min-width: 0; gap: 2px; padding: 8px; overflow: hidden; border: 1px solid transparent; border-radius: 4px; background: transparent; color: var(--text-primary); text-align: left; cursor: pointer; }
.tasktree-detail-tree button:hover { border-color: var(--border-color); background: var(--surface-primary); }
.tasktree-detail-tree button.current { border-color: #8eb7d7; background: #eaf4fb; cursor: default; }
.tasktree-detail-tree small { color: var(--text-secondary); font-family: var(--console-font-mono); font-size: 9px; }
.tasktree-detail-tree strong { overflow: hidden; font-size: 11px; line-height: 1.35; text-overflow: ellipsis; white-space: nowrap; }
.tasktree-detail-main { display: grid; grid-template-rows: minmax(0, 1fr); min-width: 0; min-height: 0; overflow: hidden; }
.tasktree-detail-content { display: grid; min-width: 0; min-height: 0; grid-template-rows: minmax(0, 1fr); padding: 10px; overflow: hidden; }
.tasktree-detail-document { width: 100%; height: 100%; max-height: 100%; min-height: 0; overflow: hidden; }
.tasktree-detail-document :deep(.content-viewer-toolbar) { display: grid; height: 34px; min-height: 34px; grid-template-columns: minmax(0, 1fr) minmax(90px, 170px) repeat(4, 26px); align-items: center; gap: 3px; box-sizing: border-box; padding: 3px 4px; }
.tasktree-detail-document :deep(.content-viewer-toolbar strong) { min-width: 0; max-width: none; margin-right: 0; overflow: hidden; font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
.tasktree-detail-document :deep(.content-viewer-search) { width: 100%; height: 26px; min-width: 0; min-height: 26px; padding-block: 2px; }
.tasktree-detail-document :deep(.content-viewer-toolbar .icon-button) { width: 26px; height: 26px; min-height: 26px; padding: 0; }
.tasktree-detail-document :deep(.content-viewer-body) { max-height: 100%; min-height: 0; overflow: auto; overscroll-behavior: contain; }
.tasktree-detail-content :deep(code),
.tasktree-detail-content :deep(a) { overflow-wrap: anywhere; word-break: break-word; }
.tasktree-detail-statusbar { display: flex; min-width: 0; align-items: center; gap: 0; padding: 0 6px; overflow-x: auto; border-top: 1px solid var(--border-color); background: var(--surface-secondary); color: var(--text-secondary); font-family: var(--console-font-mono); font-size: 9px; white-space: nowrap; }
.tasktree-detail-statusbar span + span::before { margin: 0 8px; color: var(--border-color); content: "|"; }
.tasktree-detail-statusbar strong { color: var(--text-primary); }
.tasktree-detail-pager { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 2px; margin-right: 9px; padding-right: 8px; border-right: 1px solid var(--border-color); }
.tasktree-detail-pager button,
.tasktree-detail-dock-button { display: grid; width: 24px; height: 24px; flex: 0 0 24px; place-items: center; padding: 0; border: 0; border-radius: 3px; background: transparent; color: var(--text-secondary); cursor: pointer; }
.tasktree-detail-pager button:hover:not(:disabled),
.tasktree-detail-dock-button:hover { background: var(--surface-primary); color: var(--text-primary); }
.tasktree-detail-pager button:disabled { opacity: 0.35; cursor: default; }
.tasktree-detail-pager output { min-width: 42px; text-align: center; }
.tasktree-detail-dock-button { margin-left: auto; }
@media (max-width: 720px) {
  .tasktree-detail-shell { grid-template-columns: 140px minmax(0, 1fr); }
}
</style>
