import { describe, expect, it } from "vitest";
import type { Item, ThreadEvidenceRecord, ThreadHandoffSnapshot } from "@best-agent/protocol";
import {
  buildWorkflowJoinedPhaseContexts,
  buildWorkflowPhaseOutputTexts,
  extractWorkflowPhaseResultsFromEvidence,
  mergeWorkflowPhaseResultsFromReasoningItems,
  renderWorkflowJoinedPhaseMessage,
  resolveWorkflowJoinContext,
} from "./workflow-join.js";

function createJoinRecord(
  payload: Record<string, unknown>,
  summary = "fallback summary",
): ThreadEvidenceRecord {
  return {
    confidence: 0.8,
    createdAt: new Date().toISOString(),
    evidenceId: "evidence-1",
    freshness: new Date().toISOString(),
    kind: "handoff-fact",
    payload,
    summary,
    threadId: "thread-1" as any,
    turnId: "turn-1" as any,
  };
}

describe("resolveWorkflowJoinContext", () => {
  it("prefers child evidence summary over output and handoff payloads", () => {
    const record = createJoinRecord({
      childEvidence: [{ evidenceId: "child-evidence-1", summary: "child evidence summary" }],
      handoff: { currentObjective: "Build objective." } satisfies Partial<ThreadHandoffSnapshot>,
      output: "build output",
      phaseName: "build",
      workflowName: "build-verify",
    }, "Child build evidence: child evidence summary");

    expect(resolveWorkflowJoinContext(record)).toEqual({
      phaseName: "build",
      text: "Child build evidence: child evidence summary",
      type: "evidence",
      workflowName: "build-verify",
    });
  });

  it("falls back to rendered handoff and then summary only when explicitly requested", () => {
    const handoffRecord = createJoinRecord({
      handoff: {
        currentObjective: "Workflow phase: build.",
        summaryText: "Current objective: Workflow phase: build.",
      } satisfies Partial<ThreadHandoffSnapshot>,
      phaseName: "build",
      workflowName: "build-verify",
    });
    expect(resolveWorkflowJoinContext(handoffRecord)).toEqual({
      phaseName: "build",
      text: expect.stringContaining("Current objective: Workflow phase: build."),
      type: "handoff",
      workflowName: "build-verify",
    });

    const summaryOnlyRecord = createJoinRecord({
      phaseName: "build",
      workflowName: "build-verify",
    }, "Child build output: fallback summary");
    expect(resolveWorkflowJoinContext(summaryOnlyRecord)).toBeUndefined();
    expect(resolveWorkflowJoinContext(summaryOnlyRecord, { fallbackToSummary: true })).toEqual({
      phaseName: "build",
      text: "Child build output: fallback summary",
      type: "output",
      workflowName: "build-verify",
    });
  });

  it("rejects payloads that are not workflow join records", () => {
    expect(resolveWorkflowJoinContext(createJoinRecord({
      phaseName: "summarize",
      workflowName: "build-verify",
    }))).toBeUndefined();
    expect(resolveWorkflowJoinContext(createJoinRecord({
      phaseName: "build",
      workflowName: "unknown-workflow",
    }))).toBeUndefined();
  });
});

describe("workflow joined phase helpers", () => {
  it("builds joined contexts and phase output texts for one workflow only, preferring the latest phase record", () => {
    const records = [
      createJoinRecord({
        output: "plan output",
        phaseName: "plan",
        workflowName: "plan-build-verify",
      }),
      createJoinRecord({
        output: "stale build output",
        phaseName: "build",
        workflowName: "plan-build-verify",
      }),
      createJoinRecord({
        childEvidence: [{ evidenceId: "child-evidence-1", summary: "build evidence" }],
        phaseName: "build",
        workflowName: "plan-build-verify",
      }, "Build evidence summary"),
      createJoinRecord({
        output: "ignored",
        phaseName: "build",
        workflowName: "build-verify",
      }),
    ];

    const joinedContexts = buildWorkflowJoinedPhaseContexts(records, "plan-build-verify");

    expect(joinedContexts).toEqual(new Map([
      ["plan", { text: "plan output", type: "output" }],
      ["build", { text: "Build evidence summary", type: "evidence" }],
    ]));
    expect(buildWorkflowPhaseOutputTexts(joinedContexts)).toEqual(new Map([
      ["plan", "plan output"],
      ["build", "Build evidence summary"],
    ]));
  });

  it("renders joined phase messages from context type", () => {
    expect(renderWorkflowJoinedPhaseMessage("build", {
      text: "child evidence summary",
      type: "evidence",
    })).toBe("Workflow phase build joined evidence:\nchild evidence summary");
    expect(renderWorkflowJoinedPhaseMessage("build", {
      text: "handoff text",
      type: "handoff",
    })).toBe("Workflow phase build joined handoff:\nhandoff text");
    expect(renderWorkflowJoinedPhaseMessage("build", {
      text: "build output",
      type: "output",
    })).toBe("Workflow phase build output:\nbuild output");
  });

  it("extracts workflow phase results from evidence with filters and summary fallback", () => {
    const records = [
      createJoinRecord({
        output: "old build output",
        phaseName: "build",
        workflowName: "build-verify",
      }),
      {
        ...createJoinRecord({
          phaseName: "plan",
          workflowName: "plan-build-verify",
        }, "plan summary fallback"),
        turnId: "turn-2" as any,
      },
      {
        ...createJoinRecord({
          output: "build output",
          phaseName: "build",
          workflowName: "plan-build-verify",
        }),
        turnId: "turn-2" as any,
      },
      {
        ...createJoinRecord({
          output: "verify output",
          phaseName: "verify",
          workflowName: "plan-build-verify",
        }),
        turnId: "turn-2" as any,
      },
    ];

    expect(extractWorkflowPhaseResultsFromEvidence(records, {
      allowedPhaseNames: new Set<"plan" | "build">(["plan", "build"]),
      fallbackToSummary: true,
      latestWorkflowTurnId: "turn-2" as any,
      workflowName: "plan-build-verify",
    })).toEqual({
      phaseResults: {
        build: "build output",
        plan: "plan summary fallback",
      },
      workflowName: "plan-build-verify",
    });
  });

  it("merges workflow phase results from reasoning items without overriding existing evidence results", () => {
    const items: Item[] = [
      {
        createdAt: new Date().toISOString(),
        id: "item-1" as any,
        kind: "reasoning",
        summary: "Workflow phase build result: build summary from reasoning",
        threadId: "thread-1" as any,
        turnId: "turn-1" as any,
      },
      {
        createdAt: new Date().toISOString(),
        id: "item-2" as any,
        kind: "reasoning",
        summary: "Workflow phase verify result: verify summary from reasoning",
        threadId: "thread-1" as any,
        turnId: "turn-1" as any,
      },
      {
        createdAt: new Date().toISOString(),
        id: "item-3" as any,
        kind: "reasoning",
        summary: "Workflow phase plan result: stale other turn",
        threadId: "thread-1" as any,
        turnId: "turn-0" as any,
      },
    ];

    expect(mergeWorkflowPhaseResultsFromReasoningItems(items, {
      build: "build output from evidence",
      plan: "plan output from evidence",
    }, {
      latestWorkflowTurnId: "turn-1" as any,
    })).toEqual({
      build: "build output from evidence",
      plan: "plan output from evidence",
      verify: "verify summary from reasoning",
    });
  });
});
