import type {
  ProcessArtifact,
  ProcessEvent,
  ProcessRun,
  ProcessRunStatus,
  Session,
} from '@/types';
import { getCurrentAppLocale, pickAppText, type AppLocale } from '@/lib/i18n/core';

const TERMINAL_STATUSES = new Set<TaskRuntimeStatus>(['done', 'error', 'cancelled']);
const STALE_WAITING_MS = 2 * 60 * 1000;

export type TaskRuntimeStatus = ProcessRunStatus | 'blocked';

export interface TaskRuntimeProgressView {
  label: string;
  value: number | null;
  max: number | null;
}

export interface TaskRuntimeStatsView {
  totalRuns: number;
  activeRuns: number;
  artifactCount: number;
  alertCount: number;
}

export interface TaskRuntimeNodeView {
  taskId: string;
  runId: string;
  actorName: string;
  title: string;
  status: TaskRuntimeStatus;
  stageLabel: string | null;
  summary: string | null;
  updatedAt: string;
  childTaskIds: string[];
  isRoot: boolean;
}

export interface TaskRuntimeView {
  taskId: string;
  sessionId: string | null;
  title: string;
  status: TaskRuntimeStatus;
  createdAt: string;
  updatedAt: string;
  finishedAt: string | null;
  durationMs: number | null;
  sourceSessionLabel: string;
  rootRunId: string;
  rootActorName: string;
  progress: TaskRuntimeProgressView;
  stats: TaskRuntimeStatsView;
  tasks: TaskRuntimeNodeView[];
}

type BuildTaskRuntimeInput = {
  sessions: Session[];
  processRuns: ProcessRun[];
  processEvents: ProcessEvent[];
  processArtifacts: ProcessArtifact[];
};

function toTime(value?: string | null): number | null {
  if (!value) return null;
  const parsed = new Date(value).getTime();
  return Number.isFinite(parsed) ? parsed : null;
}

function compareIsoDesc(a?: string | null, b?: string | null): number {
  return (toTime(b) ?? 0) - (toTime(a) ?? 0);
}

function firstString(value: unknown): string | null {
  return typeof value === 'string' && value.trim() ? value.trim() : null;
}

function firstNumber(value: unknown): number | null {
  return typeof value === 'number' && Number.isFinite(value) ? value : null;
}

function readMetadataString(metadata: Record<string, unknown> | undefined, keys: string[]): string | null {
  for (const key of keys) {
    const value = firstString(metadata?.[key]);
    if (value) return value;
  }
  return null;
}

function readMetadataNumber(metadata: Record<string, unknown> | undefined, keys: string[]): number | null {
  for (const key of keys) {
    const value = firstNumber(metadata?.[key]);
    if (value !== null) return value;
  }
  return null;
}

function latestTimestamp(values: Array<string | null | undefined>): string | null {
  let selected: string | null = null;
  let selectedTime = -1;
  for (const value of values) {
    const time = toTime(value);
    if (time === null || time <= selectedTime) continue;
    selected = value ?? null;
    selectedTime = time;
  }
  return selected;
}

function getSessionLabel(sessions: Session[], sessionId: string | null, locale: AppLocale): string {
  if (!sessionId) return pickAppText(locale, '未关联会话', 'No session linked');
  const session = sessions.find((item) => item.key === sessionId);
  if (!session) return sessionId;
  return session.path?.trim() || session.key;
}

function groupByRunId<T extends { run_id: string }>(items: T[]): Map<string, T[]> {
  const map = new Map<string, T[]>();
  for (const item of items) {
    const collection = map.get(item.run_id);
    if (collection) {
      collection.push(item);
      continue;
    }
    map.set(item.run_id, [item]);
  }
  return map;
}

function buildChildrenMap(processRuns: ProcessRun[]): Map<string, ProcessRun[]> {
  const map = new Map<string, ProcessRun[]>();
  for (const run of processRuns) {
    if (!run.parent_run_id) continue;
    const children = map.get(run.parent_run_id);
    if (children) {
      children.push(run);
      continue;
    }
    map.set(run.parent_run_id, [run]);
  }
  return map;
}

function collectRunTree(rootRun: ProcessRun, childrenMap: Map<string, ProcessRun[]>): ProcessRun[] {
  const collected: ProcessRun[] = [];
  const stack = [rootRun];
  const seen = new Set<string>();

  while (stack.length > 0) {
    const current = stack.pop();
    if (!current || seen.has(current.run_id)) continue;
    seen.add(current.run_id);
    collected.push(current);
    const children = childrenMap.get(current.run_id) ?? [];
    for (let index = children.length - 1; index >= 0; index -= 1) {
      stack.push(children[index]);
    }
  }

  return collected;
}

function getRunUpdatedAt(
  run: ProcessRun,
  eventsByRun: Map<string, ProcessEvent[]>,
  artifactsByRun: Map<string, ProcessArtifact[]>,
): string {
  const eventTimes = (eventsByRun.get(run.run_id) ?? []).map((event) => event.created_at);
  const artifactTimes = (artifactsByRun.get(run.run_id) ?? []).map((artifact) => artifact.created_at);
  return (
    latestTimestamp([
      ...eventTimes,
      ...artifactTimes,
      run.finished_at,
      run.started_at,
    ]) ?? run.started_at
  );
}

function deriveStageLabel(
  run: ProcessRun,
  runEvents: ProcessEvent[],
  fallbackStatus: TaskRuntimeStatus,
  locale: AppLocale,
): string | null {
  const runMetadataLabel = readMetadataString(run.metadata, [
    'stage_label',
    'stage',
    'phase_label',
    'step_label',
  ]);
  if (runMetadataLabel) return runMetadataLabel;

  const sortedEvents = [...runEvents].sort((a, b) => compareIsoDesc(a.created_at, b.created_at));
  for (const event of sortedEvents) {
    const label = readMetadataString(event.metadata, [
      'stage_label',
      'stage',
      'phase_label',
      'step_label',
    ]);
    if (label) return label;
  }

  if (fallbackStatus === 'running') return pickAppText(locale, '执行中', 'Running');
  if (fallbackStatus === 'waiting') return pickAppText(locale, '等待中', 'Waiting');
  if (fallbackStatus === 'queued') return pickAppText(locale, '排队中', 'Queued');
  if (fallbackStatus === 'done') return pickAppText(locale, '已完成', 'Done');
  if (fallbackStatus === 'error') return pickAppText(locale, '失败', 'Error');
  if (fallbackStatus === 'cancelled') return pickAppText(locale, '已取消', 'Cancelled');
  if (fallbackStatus === 'blocked') return pickAppText(locale, '阻塞', 'Blocked');
  return null;
}

function deriveRunStatus(
  run: ProcessRun,
  updatedAt: string,
  now: number,
): TaskRuntimeStatus {
  if (run.status !== 'waiting') return run.status;
  const updatedTime = toTime(updatedAt);
  if (updatedTime !== null && now - updatedTime > STALE_WAITING_MS) {
    return 'blocked';
  }
  return 'waiting';
}

function deriveProgress(
  rootRun: ProcessRun,
  taskRuns: ProcessRun[],
  locale: AppLocale,
): TaskRuntimeProgressView {
  const stageValue = readMetadataNumber(rootRun.metadata, ['stage_index', 'step_index', 'phase_index']);
  const stageMax = readMetadataNumber(rootRun.metadata, ['stage_total', 'step_total', 'phase_total']);

  if (stageValue !== null && stageMax !== null && stageMax > 0) {
    return {
      label: pickAppText(
        locale,
        `阶段 ${Math.min(stageValue, stageMax)} / ${stageMax}`,
        `Stage ${Math.min(stageValue, stageMax)} / ${stageMax}`
      ),
      value: stageValue,
      max: stageMax,
    };
  }

  const doneRuns = taskRuns.filter((run) => run.status === 'done').length;
  if (taskRuns.length > 0) {
    return {
      label: pickAppText(
        locale,
        `已完成子任务 ${doneRuns} / ${taskRuns.length}`,
        `Subtasks completed ${doneRuns} / ${taskRuns.length}`
      ),
      value: doneRuns,
      max: taskRuns.length,
    };
  }

  return {
    label: pickAppText(locale, '等待任务数据', 'Waiting for task data'),
    value: null,
    max: null,
  };
}

function countAlerts(taskViews: TaskRuntimeNodeView[]): number {
  return taskViews.filter((task) => task.status === 'error' || task.status === 'blocked').length;
}

export function isTaskRuntimeTerminal(status: TaskRuntimeStatus): boolean {
  return TERMINAL_STATUSES.has(status);
}

export function taskRuntimeStatusLabel(status: TaskRuntimeStatus, locale: AppLocale = getCurrentAppLocale()): string {
  if (status === 'queued') return pickAppText(locale, '排队中', 'Queued');
  if (status === 'running') return pickAppText(locale, '进行中', 'In Progress');
  if (status === 'waiting') return pickAppText(locale, '等待中', 'Waiting');
  if (status === 'blocked') return pickAppText(locale, '阻塞', 'Blocked');
  if (status === 'done') return pickAppText(locale, '已完成', 'Done');
  if (status === 'error') return pickAppText(locale, '失败', 'Error');
  return pickAppText(locale, '已取消', 'Cancelled');
}

export function buildTaskRuntimeView(
  taskId: string,
  input: BuildTaskRuntimeInput,
  locale: AppLocale = getCurrentAppLocale(),
): TaskRuntimeView | null {
  const { sessions, processRuns, processEvents, processArtifacts } = input;
  const runById = new Map(processRuns.map((run) => [run.run_id, run]));
  const rootRun = runById.get(taskId);
  if (!rootRun) return null;

  const childrenMap = buildChildrenMap(processRuns);
  const taskRuns = collectRunTree(rootRun, childrenMap);
  const taskRunIds = new Set(taskRuns.map((run) => run.run_id));
  const taskEvents = processEvents.filter((event) => taskRunIds.has(event.run_id));
  const taskArtifacts = processArtifacts.filter((artifact) => taskRunIds.has(artifact.run_id));
  const eventsByRun = groupByRunId(taskEvents);
  const artifactsByRun = groupByRunId(taskArtifacts);
  const now = Date.now();

  const taskViews: TaskRuntimeNodeView[] = taskRuns
    .map((run) => {
      const runEvents = eventsByRun.get(run.run_id) ?? [];
      const updatedAt = getRunUpdatedAt(run, eventsByRun, artifactsByRun);
      const status = deriveRunStatus(run, updatedAt, now);
      const stageLabel = deriveStageLabel(run, runEvents, status, locale);
      const childTaskIds = (childrenMap.get(run.run_id) ?? [])
        .filter((child) => taskRunIds.has(child.run_id))
        .map((child) => child.run_id);

      return {
        taskId: run.run_id,
        runId: run.run_id,
        actorName: run.actor_name,
        title: run.title,
        status,
        stageLabel,
        summary: firstString(run.summary),
        updatedAt,
        childTaskIds,
        isRoot: run.run_id === rootRun.run_id,
      };
    })
    .sort((a, b) => {
      if (a.isRoot !== b.isRoot) return a.isRoot ? -1 : 1;
      if (isTaskRuntimeTerminal(a.status) !== isTaskRuntimeTerminal(b.status)) {
        return isTaskRuntimeTerminal(a.status) ? 1 : -1;
      }
      return compareIsoDesc(a.updatedAt, b.updatedAt);
    });

  const sessionId = rootRun.session_id ?? taskRuns.find((run) => run.session_id)?.session_id ?? null;
  const updatedAt = latestTimestamp([
    ...taskViews.map((task) => task.updatedAt),
    rootRun.finished_at,
    rootRun.started_at,
  ]) ?? rootRun.started_at;
  const derivedRootStatus = deriveRunStatus(rootRun, updatedAt, now);
  const alertCount = countAlerts(taskViews);
  const progress = deriveProgress(rootRun, taskRuns, locale);
  const sourceSessionLabel = getSessionLabel(sessions, sessionId, locale);
  const createdAt = rootRun.started_at;
  const finishedAt = rootRun.finished_at ?? null;
  const durationStart = toTime(createdAt);
  const durationEnd = toTime(finishedAt ?? updatedAt);
  const durationMs =
    durationStart !== null && durationEnd !== null && durationEnd >= durationStart
      ? durationEnd - durationStart
      : null;

  return {
    taskId: rootRun.run_id,
    sessionId,
    title: rootRun.title || pickAppText(locale, `任务 ${rootRun.run_id.slice(0, 8)}`, `Task ${rootRun.run_id.slice(0, 8)}`),
    status: derivedRootStatus,
    createdAt,
    updatedAt,
    finishedAt,
    durationMs,
    sourceSessionLabel,
    rootRunId: rootRun.run_id,
    rootActorName: rootRun.actor_name,
    progress,
    stats: {
      totalRuns: taskRuns.length,
      activeRuns: taskViews.filter((task) => !isTaskRuntimeTerminal(task.status)).length,
      artifactCount: taskArtifacts.length,
      alertCount,
    },
    tasks: taskViews,
  };
}
