// SPEC: PJ2026-01040409 MDTODO前端重写; draft-2026-06-26-p0-mdtodo-web-rewrite.
// Responsibility: file list, Rxx task window, task detail and workbench links loading via public API.

import { computed, ref } from "vue";
import { projectManagementAPI, type MdtodoFileRecord, type MdtodoTaskRecord, type MdtodoTaskDetailAuthority, type MdtodoTaskDetailRecord, type MdtodoTaskPage, type MdtodoTaskStatus, type ProjectWorkbenchLinkRecord } from "@/api";

const TASK_WINDOW_LIMIT = 100;

export interface MdtodoTaskWindowQuery {
  search?: string | null;
  status?: MdtodoTaskStatus | null;
}

/**
 * Loads MDTODO files, the Rxx task window for a selected file, task detail body
 * and workbench links through the public Project Management API. The Rxx tree
 * hierarchy (R1/R1.1/R1.1.1) is derived from taskRef prefixes, not from Markdown
 * heading counts or checkbox indentation.
 */
export function useMdtodoTaskData() {
  const files = ref<MdtodoFileRecord[]>([]);
  const tasks = ref<MdtodoTaskRecord[]>([]);
  const taskPage = ref<MdtodoTaskPage | null>(null);
  const taskDetail = ref<MdtodoTaskDetailRecord | null>(null);
  const taskDetailAuthority = ref<MdtodoTaskDetailAuthority | null>(null);
  const links = ref<ProjectWorkbenchLinkRecord[]>([]);
  const taskLoading = ref(false);
  const taskDetailLoading = ref(false);
  const linkLoading = ref(false);
  const taskDetailError = ref<string | null>(null);

  let filesRequestSeq = 0;
  let taskWindowRequestSeq = 0;
  let taskDetailRequestSeq = 0;
  let linksRequestSeq = 0;
  let taskWindowContext: { sourceId: string; fileRef: string | null; query: MdtodoTaskWindowQuery; loadedCount: number } | null = null;

  const taskByRef = computed(() => new Map(tasks.value.map((task) => [task.taskRef, task])));

  async function loadFiles(sourceId: string): Promise<MdtodoFileRecord[]> {
    const requestSeq = ++filesRequestSeq;
    const fileResponse = await projectManagementAPI.files(sourceId);
    if (!fileResponse.ok) throw fileResponse;
    if (requestSeq !== filesRequestSeq) return files.value;
    files.value = fileResponse.data?.files ?? [];
    return files.value;
  }

  async function loadTaskWindow(sourceId: string, fileRef: string | null, preferredTaskRef?: string | null, query: MdtodoTaskWindowQuery = {}): Promise<MdtodoTaskRecord[]> {
    const requestSeq = ++taskWindowRequestSeq;
    const taskResponse = await projectManagementAPI.tasks({
      sourceId,
      fileRef,
      search: query.search,
      status: query.status,
      limit: TASK_WINDOW_LIMIT,
      offset: 0
    });
    if (!taskResponse.ok) throw taskResponse;
    if (requestSeq !== taskWindowRequestSeq) return tasks.value;
    const windowTasks = taskResponse.data?.tasks ?? [];
    const preferred = preferredTaskRef ? resolveTask(windowTasks, preferredTaskRef) : null;
    const exactTaskRef = preferredTaskRef ? normalizedTaskRef(sourceId, fileRef, preferredTaskRef) : null;
    let targetedTask: MdtodoTaskRecord | null = null;
    if (exactTaskRef && !preferred) {
      const targetedResponse = await projectManagementAPI.tasks({ taskRef: exactTaskRef, sourceId, fileRef, limit: 1 });
      if (!targetedResponse.ok && targetedResponse.status !== 404) throw targetedResponse;
      if (requestSeq !== taskWindowRequestSeq) return tasks.value;
      targetedTask = targetedResponse.data?.tasks?.[0] ?? null;
    }
    tasks.value = mergeTasks(windowTasks, targetedTask ? [targetedTask] : []);
    taskPage.value = taskResponse.data?.page ?? null;
    taskWindowContext = { sourceId, fileRef, query: { ...query }, loadedCount: windowTasks.length };
    return tasks.value;
  }

  async function loadMoreTaskWindow(): Promise<MdtodoTaskRecord[]> {
    const context = taskWindowContext;
    if (!context || taskPage.value?.hasMore !== true) return tasks.value;
    const requestSeq = ++taskWindowRequestSeq;
    const taskResponse = await projectManagementAPI.tasks({
      sourceId: context.sourceId,
      fileRef: context.fileRef,
      search: context.query.search,
      status: context.query.status,
      limit: TASK_WINDOW_LIMIT,
      offset: context.loadedCount
    });
    if (!taskResponse.ok) throw taskResponse;
    if (requestSeq !== taskWindowRequestSeq) return tasks.value;
    const nextTasks = taskResponse.data?.tasks ?? [];
    context.loadedCount += nextTasks.length;
    tasks.value = mergeTasks(tasks.value, nextTasks);
    taskPage.value = taskResponse.data?.page
      ? { ...taskResponse.data.page, offset: 0, limit: context.loadedCount }
      : null;
    return tasks.value;
  }

  function resolvePreferredTask(preferredTaskRef?: string | null): MdtodoTaskRecord | null {
    if (!preferredTaskRef) return null;
    return resolveTask(tasks.value, preferredTaskRef);
  }

  async function loadTaskDetail(taskRef: string | null): Promise<void> {
    const requestSeq = ++taskDetailRequestSeq;
    taskDetailError.value = null;
    taskDetailAuthority.value = null;
    if (!taskRef) {
      taskDetail.value = null;
      taskDetailLoading.value = false;
      return;
    }
    taskDetailLoading.value = true;
    try {
      const response = await projectManagementAPI.taskDetail(taskRef);
      if (!response.ok) throw response;
      if (requestSeq !== taskDetailRequestSeq) return;
      taskDetail.value = response.data?.detail ?? null;
      taskDetailAuthority.value = response.data?.detailAuthority ?? null;
      if (taskDetailAuthority.value?.state === "unavailable") {
        taskDetailError.value = taskDetailAuthority.value.message || "任务详情 authority 当前不可用；继续展示最近一次投影。";
      }
    } catch (err) {
      if (requestSeq !== taskDetailRequestSeq) return;
      taskDetail.value = null;
      taskDetailAuthority.value = null;
      taskDetailError.value = err && typeof err === "object" && "error" in err ? String((err as { error?: string }).error || "任务详情加载失败") : "任务详情加载失败";
    } finally {
      if (requestSeq === taskDetailRequestSeq) taskDetailLoading.value = false;
    }
  }

  async function loadLinks(taskRef: string | null): Promise<void> {
    const requestSeq = ++linksRequestSeq;
    linkLoading.value = true;
    try {
      const response = await projectManagementAPI.workbenchLinks(taskRef ? { taskRef } : {});
      if (!response.ok) throw response;
      if (requestSeq !== linksRequestSeq) return;
      links.value = response.data?.links ?? [];
    } catch {
      if (requestSeq !== linksRequestSeq) return;
      links.value = [];
    } finally {
      if (requestSeq === linksRequestSeq) linkLoading.value = false;
    }
  }

  function clearTaskDetailAndReport(): void {
    taskDetail.value = null;
    taskDetailAuthority.value = null;
    taskDetailError.value = null;
  }

  return {
    files,
    tasks,
    taskPage,
    taskDetail,
    taskDetailAuthority,
    links,
    taskLoading,
    taskDetailLoading,
    linkLoading,
    taskDetailError,
    taskByRef,
    loadFiles,
    loadTaskWindow,
    loadMoreTaskWindow,
    resolvePreferredTask,
    loadTaskDetail,
    loadLinks,
    clearTaskDetailAndReport
  };
}

function normalizedTaskRef(sourceId: string, fileRef: string | null, preferredTaskRef: string): string | null {
  const value = preferredTaskRef.trim();
  if (!value) return null;
  if (value.startsWith("mdtodo:")) return value;
  return fileRef ? `mdtodo:${sourceId}:${fileRef}:${value}` : null;
}

function resolveTask(items: MdtodoTaskRecord[], preferredTaskRef: string): MdtodoTaskRecord | null {
  return items.find((task) => task.taskRef === preferredTaskRef || task.taskId === preferredTaskRef || task.rxxId === preferredTaskRef) ?? null;
}

function mergeTasks(current: MdtodoTaskRecord[], incoming: MdtodoTaskRecord[]): MdtodoTaskRecord[] {
  const byRef = new Map(current.map((task) => [task.taskRef, task]));
  for (const task of incoming) byRef.set(task.taskRef, task);
  return [...byRef.values()].sort((left, right) => Number(left.ordinal ?? 0) - Number(right.ordinal ?? 0) || left.taskRef.localeCompare(right.taskRef));
}
