import {
  renderThreadHandoffText,
  type Item,
  type ThreadEvidenceRecord,
  type ThreadHandoffSnapshot,
  type TurnId,
  type WorkflowPhaseName,
  type WorkflowName,
} from "@best-agent/protocol";

export type WorkflowJoinContext = {
  phaseName: WorkflowPhaseName;
  text: string;
  type: "evidence" | "handoff" | "output";
  workflowName: WorkflowName;
};

export type WorkflowJoinedPhaseContext = {
  text: string;
  type: "evidence" | "handoff" | "output";
};

export type WorkflowPhaseResults = {
  build?: string;
  explore?: string;
  plan?: string;
  verify?: string;
};

export function resolveWorkflowJoinContext(
  record: ThreadEvidenceRecord,
  options: { fallbackToSummary?: boolean } = {},
): WorkflowJoinContext | undefined {
  if (typeof record.payload !== "object" || record.payload === null) {
    return undefined;
  }
  const payload = record.payload as Record<string, unknown>;
  const phaseName = payload.phaseName;
  const workflowName = payload.workflowName;
  const childEvidence = payload.childEvidence;
  const output = typeof payload.output === "string" && payload.output.trim().length > 0 ? payload.output : undefined;
  const handoff =
    typeof payload.handoff === "object" && payload.handoff !== null
      ? renderThreadHandoffText(payload.handoff as ThreadHandoffSnapshot)
      : undefined;
  if (
    (phaseName !== "explore" && phaseName !== "plan" && phaseName !== "build" && phaseName !== "verify")
    || (workflowName !== "explore-build"
      && workflowName !== "build-verify"
      && workflowName !== "plan-build-verify"
      && workflowName !== "plan-explore-build-verify")
  ) {
    return undefined;
  }
  if (Array.isArray(childEvidence) && childEvidence.length > 0) {
    return {
      phaseName,
      text: record.summary,
      type: "evidence",
      workflowName,
    };
  }
  if (output) {
    return {
      phaseName,
      text: output,
      type: "output",
      workflowName,
    };
  }
  if (handoff) {
    return {
      phaseName,
      text: handoff,
      type: "handoff",
      workflowName,
    };
  }
  if (options.fallbackToSummary && record.summary.trim().length > 0) {
    return {
      phaseName,
      text: record.summary,
      type: "output",
      workflowName,
    };
  }
  return undefined;
}

export function buildWorkflowJoinedPhaseContexts(
  joinedEvidence: ThreadEvidenceRecord[],
  workflowName: WorkflowName,
): Map<WorkflowPhaseName, WorkflowJoinedPhaseContext> {
  return collectWorkflowJoinedPhaseContexts(joinedEvidence, { workflowName }).joinedContexts;
}

export function buildWorkflowPhaseOutputTexts(
  joinedContexts: Map<WorkflowPhaseName, WorkflowJoinedPhaseContext>,
): Map<WorkflowPhaseName, string> {
  return new Map(
    Array.from(joinedContexts.entries()).map(([phaseName, joinedContext]) => [phaseName, joinedContext.text]),
  );
}

export function renderWorkflowJoinedPhaseMessage(
  phaseName: WorkflowPhaseName,
  joinedContext: WorkflowJoinedPhaseContext,
): string {
  if (joinedContext.type === "evidence") {
    return `Workflow phase ${phaseName} joined evidence:\n${joinedContext.text}`;
  }
  if (joinedContext.type === "handoff") {
    return `Workflow phase ${phaseName} joined handoff:\n${joinedContext.text}`;
  }
  return `Workflow phase ${phaseName} output:\n${joinedContext.text}`;
}

export function extractWorkflowPhaseResultsFromEvidence(
  joinedEvidence: ThreadEvidenceRecord[],
  options: {
    allowedPhaseNames?: ReadonlySet<WorkflowPhaseName>;
    fallbackToSummary?: boolean;
    latestWorkflowTurnId?: TurnId;
    workflowName?: WorkflowName;
  } = {},
): {
  phaseResults: WorkflowPhaseResults;
  workflowName?: WorkflowName;
} {
  const { joinedContexts, workflowName } = collectWorkflowJoinedPhaseContexts(joinedEvidence, options);
  const phaseResults: WorkflowPhaseResults = {};
  for (const [phaseName, joinedContext] of joinedContexts.entries()) {
    phaseResults[phaseName] = joinedContext.text;
  }
  return { phaseResults, workflowName };
}

export function mergeWorkflowPhaseResultsFromReasoningItems(
  items: Item[],
  phaseResults: WorkflowPhaseResults,
  options: {
    latestWorkflowTurnId?: TurnId;
  } = {},
): WorkflowPhaseResults {
  const mergedPhaseResults: WorkflowPhaseResults = { ...phaseResults };
  for (const item of items) {
    if (item.kind !== "reasoning") {
      continue;
    }
    if (options.latestWorkflowTurnId && item.turnId !== options.latestWorkflowTurnId) {
      continue;
    }

    const match = item.summary.match(/^Workflow phase (explore|plan|build|verify) result: ([\s\S]+)$/);
    if (!match) {
      continue;
    }

    const phase = match[1] as WorkflowPhaseName;
    mergedPhaseResults[phase] ??= match[2];
  }
  return mergedPhaseResults;
}

function collectWorkflowJoinedPhaseContexts(
  joinedEvidence: ThreadEvidenceRecord[],
  options: {
    allowedPhaseNames?: ReadonlySet<WorkflowPhaseName>;
    fallbackToSummary?: boolean;
    latestWorkflowTurnId?: TurnId;
    workflowName?: WorkflowName;
  } = {},
): {
  joinedContexts: Map<WorkflowPhaseName, WorkflowJoinedPhaseContext>;
  workflowName?: WorkflowName;
} {
  const joinedContexts = new Map<WorkflowPhaseName, WorkflowJoinedPhaseContext>();
  let workflowNameFromEvidence: WorkflowName | undefined;
  for (const record of [...joinedEvidence].reverse()) {
    const joinedContext = resolveWorkflowJoinContext(record, { fallbackToSummary: options.fallbackToSummary });
    if (!joinedContext) {
      continue;
    }
    if (options.latestWorkflowTurnId && record.turnId !== options.latestWorkflowTurnId) {
      continue;
    }
    if (options.workflowName && joinedContext.workflowName !== options.workflowName) {
      continue;
    }
    if (options.allowedPhaseNames && !options.allowedPhaseNames.has(joinedContext.phaseName)) {
      continue;
    }
    if (joinedContexts.has(joinedContext.phaseName)) {
      continue;
    }
    joinedContexts.set(joinedContext.phaseName, {
      text: joinedContext.text,
      type: joinedContext.type,
    });
    workflowNameFromEvidence = joinedContext.workflowName;
  }
  return {
    joinedContexts,
    workflowName: workflowNameFromEvidence,
  };
}
