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

const TERMINAL_STATUSES = new Set<ProcessRunStatus>(['done', 'error', 'cancelled']);
const ACTIVE_STATUSES = new Set<ProcessRunStatus>(['queued', 'running', 'waiting']);
const ARTIFACT_TYPE_ORDER: ProcessArtifact['artifact_type'][] = [
  'text',
  'json',
  'file',
  'image',
  'link',
  'markdown',
];

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

export interface SessionProgressStepView {
  runId: string;
  title: string;
  actorName: string;
  status: ProcessRunStatus;
  description: string | null;
  startedAt: string;
  updatedAt: string;
  finishedAt: string | null;
  artifactCount: number;
  isRoot: boolean;
  isCurrent: boolean;
}

export interface SessionProgressArtifactView {
  artifactId: string;
  runId: string;
  title: string;
  type: ProcessArtifact['artifact_type'];
  typeLabel: string;
  actorName: string;
  preview: string;
  createdAt: string;
  url?: string;
}

export interface SessionProgressArtifactTypeSummary {
  type: ProcessArtifact['artifact_type'];
  count: number;
  label: string;
}

export interface SessionProgressView {
  rootRunId: string;
  title: string;
  status: ProcessRunStatus;
  summary: string | null;
  updatedAt: string;
  progress: SessionProgressValueView;
  steps: SessionProgressStepView[];
  artifacts: SessionProgressArtifactView[];
  artifactTypeSummaries: SessionProgressArtifactTypeSummary[];
}

export type BuildSessionProgressInput = {
  sessionId: string;
  processRuns: ProcessRun[];
  processEvents: ProcessEvent[];
  processArtifacts: ProcessArtifact[];
  locale?: AppLocale;
};

function toTime(value?: string | null): number | null {
  if (!value) return null;
  const parsed = new Date(value).getTime();
  return Number.isFinite(parsed) ? parsed : 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 compareIsoDesc(a?: string | null, b?: string | null): number {
  return (toTime(b) ?? 0) - (toTime(a) ?? 0);
}

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

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);
    } else {
      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 groupByRunId<T extends { run_id: string }>(items: T[]): Map<string, T[]> {
  const map = new Map<string, T[]>();
  for (const item of items) {
    const existing = map.get(item.run_id);
    if (existing) {
      existing.push(item);
    } else {
      map.set(item.run_id, [item]);
    }
  }
  return map;
}

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

function getTreeUpdatedAt(
  runs: ProcessRun[],
  eventsByRun: Map<string, ProcessEvent[]>,
  artifactsByRun: Map<string, ProcessArtifact[]>,
): string {
  return latestTimestamp(runs.map((run) => getRunUpdatedAt(run, eventsByRun, artifactsByRun))) ?? runs[0]?.started_at ?? '';
}

function latestEventText(events: ProcessEvent[]): string | null {
  const event = [...events]
    .filter((item) => item.text?.trim())
    .sort((a, b) => compareIsoDesc(a.created_at, b.created_at))[0];
  return event?.text?.trim() || null;
}

function percent(value: number, max: number): number {
  return Math.max(0, Math.min(100, Math.round((value / max) * 100)));
}

function explicitProgress(
  rootRun: ProcessRun,
  treeEvents: ProcessEvent[],
  locale: AppLocale,
): SessionProgressValueView | null {
  const metadataSources = [
    rootRun.metadata,
    ...[...treeEvents]
      .sort((a, b) => compareIsoDesc(a.created_at, b.created_at))
      .map((event) => event.metadata),
  ];

  for (const metadata of metadataSources) {
    const stepValue = firstNumber(metadata, ['step_index']);
    const stepMax = firstNumber(metadata, ['step_total']);
    if (stepValue !== null && stepMax !== null && stepMax > 0) {
      const safeValue = Math.min(stepValue, stepMax);
      return {
        label: pickAppText(locale, `运行中：${safeValue} / ${stepMax} 步`, `Running: ${safeValue} / ${stepMax} steps`),
        value: safeValue,
        max: stepMax,
        percent: percent(safeValue, stepMax),
      };
    }

    const stageValue = firstNumber(metadata, ['stage_index', 'phase_index']);
    const stageMax = firstNumber(metadata, ['stage_total', 'phase_total']);
    if (stageValue !== null && stageMax !== null && stageMax > 0) {
      const safeValue = Math.min(stageValue, stageMax);
      return {
        label: pickAppText(locale, `运行中：${safeValue} / ${stageMax} 阶段`, `Running: ${safeValue} / ${stageMax} stages`),
        value: safeValue,
        max: stageMax,
        percent: percent(safeValue, stageMax),
      };
    }
  }

  return null;
}

function fallbackProgress(taskRuns: ProcessRun[], locale: AppLocale): SessionProgressValueView {
  const childRuns = taskRuns.filter((run) => run.parent_run_id);
  const runsForProgress = childRuns.length > 0 ? childRuns : taskRuns;
  const doneRuns = runsForProgress.filter((run) => run.status === 'done').length;
  const totalRuns = runsForProgress.length;

  if (totalRuns > 0) {
    return {
      label: pickAppText(locale, `已完成 ${doneRuns} / ${totalRuns} 步`, `Completed ${doneRuns} / ${totalRuns} steps`),
      value: doneRuns,
      max: totalRuns,
      percent: percent(doneRuns, totalRuns),
    };
  }

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

function artifactTypeLabel(type: ProcessArtifact['artifact_type'], locale: AppLocale): string {
  if (type === 'text') return pickAppText(locale, '文本', 'Text');
  if (type === 'json') return 'JSON';
  if (type === 'file') return pickAppText(locale, '文件', 'File');
  if (type === 'image') return pickAppText(locale, '图片', 'Image');
  if (type === 'link') return pickAppText(locale, '链接', 'Link');
  return 'Markdown';
}

function artifactPreview(artifact: ProcessArtifact, locale: AppLocale): string {
  if (artifact.content?.trim()) {
    return artifact.content.trim().replace(/\s+/g, ' ').slice(0, 120);
  }
  if (artifact.url?.trim()) return artifact.url.trim();
  if (artifact.data !== undefined) {
    return JSON.stringify(artifact.data).slice(0, 120);
  }
  return pickAppText(locale, '暂无预览', 'No preview');
}

function buildArtifactSummaries(
  artifacts: ProcessArtifact[],
  locale: AppLocale,
): SessionProgressArtifactTypeSummary[] {
  const counts = new Map<ProcessArtifact['artifact_type'], number>();
  for (const artifact of artifacts) {
    counts.set(artifact.artifact_type, (counts.get(artifact.artifact_type) ?? 0) + 1);
  }
  return ARTIFACT_TYPE_ORDER
    .filter((type) => counts.has(type))
    .map((type) => ({
      type,
      count: counts.get(type) ?? 0,
      label: artifactTypeLabel(type, locale),
    }));
}

function buildArtifactViews(
  artifacts: ProcessArtifact[],
  locale: AppLocale,
): SessionProgressArtifactView[] {
  return [...artifacts]
    .sort((a, b) => compareIsoDesc(a.created_at, b.created_at))
    .map((artifact) => ({
      artifactId: artifact.artifact_id,
      runId: artifact.run_id,
      title: artifact.title,
      type: artifact.artifact_type,
      typeLabel: artifactTypeLabel(artifact.artifact_type, locale),
      actorName: artifact.actor_name || artifact.actor_id,
      preview: artifactPreview(artifact, locale),
      createdAt: artifact.created_at,
      url: artifact.url,
    }));
}

function buildSteps(
  rootRun: ProcessRun,
  taskRuns: ProcessRun[],
  eventsByRun: Map<string, ProcessEvent[]>,
  artifactsByRun: Map<string, ProcessArtifact[]>,
): SessionProgressStepView[] {
  return [...taskRuns]
    .sort((a, b) => {
      if (a.run_id === rootRun.run_id) return 1;
      if (b.run_id === rootRun.run_id) return -1;
      return (toTime(a.started_at) ?? 0) - (toTime(b.started_at) ?? 0);
    })
    .map((run) => {
      const runEvents = eventsByRun.get(run.run_id) ?? [];
      const runArtifacts = artifactsByRun.get(run.run_id) ?? [];
      return {
        runId: run.run_id,
        title: run.title,
        actorName: run.actor_name,
        status: run.status,
        description: latestEventText(runEvents) || run.summary?.trim() || null,
        startedAt: run.started_at,
        updatedAt: getRunUpdatedAt(run, eventsByRun, artifactsByRun),
        finishedAt: run.finished_at ?? null,
        artifactCount: runArtifacts.length,
        isRoot: run.run_id === rootRun.run_id,
        isCurrent: !TERMINAL_STATUSES.has(run.status),
      };
    });
}

export function buildSessionProgressView({
  sessionId,
  processRuns,
  processEvents,
  processArtifacts,
  locale = getCurrentAppLocale(),
}: BuildSessionProgressInput): SessionProgressView | null {
  const sessionRuns = processRuns.filter((run) => run.session_id === sessionId);
  const rootRuns = sessionRuns.filter((run) => !run.parent_run_id);
  if (rootRuns.length === 0) return null;

  const allChildrenMap = buildChildrenMap(processRuns);
  const runTreeCache = new Map<string, ProcessRun[]>();
  const treeForRoot = (root: ProcessRun) => {
    const cached = runTreeCache.get(root.run_id);
    if (cached) return cached;
    const tree = collectRunTree(root, allChildrenMap).filter(
      (run) => run.session_id === sessionId || run.run_id === root.run_id
    );
    runTreeCache.set(root.run_id, tree);
    return tree;
  };

  const allEventsByRun = groupByRunId(processEvents);
  const allArtifactsByRun = groupByRunId(processArtifacts);
  const selectedRoot = [...rootRuns].sort((a, b) => {
    const aActive = ACTIVE_STATUSES.has(a.status);
    const bActive = ACTIVE_STATUSES.has(b.status);
    if (aActive !== bActive) return aActive ? -1 : 1;
    return compareIsoDesc(
      getTreeUpdatedAt(treeForRoot(a), allEventsByRun, allArtifactsByRun),
      getTreeUpdatedAt(treeForRoot(b), allEventsByRun, allArtifactsByRun)
    );
  })[0];

  if (!selectedRoot) return null;

  const taskRuns = treeForRoot(selectedRoot);
  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 updatedAt = getTreeUpdatedAt(taskRuns, eventsByRun, artifactsByRun);
  const progress = explicitProgress(selectedRoot, taskEvents, locale) ?? fallbackProgress(taskRuns, locale);

  return {
    rootRunId: selectedRoot.run_id,
    title: selectedRoot.title,
    status: selectedRoot.status,
    summary: selectedRoot.summary?.trim() || latestEventText(eventsByRun.get(selectedRoot.run_id) ?? []) || null,
    updatedAt,
    progress,
    steps: buildSteps(selectedRoot, taskRuns, eventsByRun, artifactsByRun),
    artifacts: buildArtifactViews(taskArtifacts, locale),
    artifactTypeSummaries: buildArtifactSummaries(taskArtifacts, locale),
  };
}
