// @vitest-environment jsdom
import { describe, it, expect } from "vitest";
import type { Agent, Approval, ActivityEvent, Project } from "@paperclipai/shared";
import { composeHomeStatus } from "./useAssistantHomeStatus";

// ─── Fixture factories ──────────────────────────────────────────────────────

const NOW_MS = Date.parse("2026-04-11T12:00:00Z");

function makeAgent(overrides: Partial<Agent> = {}): Agent {
  return {
    id: "agent-" + Math.random().toString(36).slice(2, 8),
    companyId: "co-1",
    name: "Agent",
    urlKey: "agent",
    role: "engineer",
    title: null,
    icon: null,
    status: "idle",
    reportsTo: null,
    capabilities: null,
    adapterType: "process",
    adapterConfig: {},
    runtimeConfig: {},
    budgetMonthlyCents: 0,
    spentMonthlyCents: 0,
    pauseReason: null,
    pausedAt: null,
    permissions: { canCreateAgents: false },
    lastHeartbeatAt: null,
    metadata: null,
    createdAt: new Date(NOW_MS),
    updatedAt: new Date(NOW_MS),
    ...overrides,
  } as Agent;
}

function makeApproval(overrides: Partial<Approval> = {}): Approval {
  return {
    id: "gate-" + Math.random().toString(36).slice(2, 8),
    companyId: "co-1",
    type: "generic",
    requestedByAgentId: null,
    requestedByUserId: null,
    status: "pending",
    payload: {},
    decisionNote: null,
    decidedByUserId: null,
    decidedAt: null,
    createdAt: new Date(NOW_MS),
    updatedAt: new Date(NOW_MS),
    ...overrides,
  } as Approval;
}

function makeActivity(overrides: Partial<ActivityEvent> = {}): ActivityEvent {
  return {
    id: "act-" + Math.random().toString(36).slice(2, 8),
    companyId: "co-1",
    actorType: "agent",
    actorId: "agent-1",
    action: "project.completed",
    entityType: "project",
    entityId: "proj-a",
    agentId: null,
    runId: null,
    details: null,
    createdAt: new Date(NOW_MS - 60 * 60 * 1000), // 1h ago
    ...overrides,
  } as ActivityEvent;
}

function makeProject(overrides: Partial<Project> = {}): Project {
  return {
    id: "proj-" + Math.random().toString(36).slice(2, 8),
    companyId: "co-1",
    urlKey: "nexus-demo",
    goalId: null,
    goalIds: [],
    goals: [],
    name: "Demo",
    description: null,
    status: "active",
    leadAgentId: null,
    targetDate: null,
    color: null,
    pauseReason: null,
    pausedAt: null,
    executionWorkspacePolicy: null,
    codebase: { kind: "none" } as Project["codebase"],
    workspaces: [],
    primaryWorkspace: null,
    archivedAt: null,
    createdAt: new Date(NOW_MS),
    updatedAt: new Date(NOW_MS),
    ...overrides,
  } as Project;
}

function baseArgs() {
  return {
    nowMs: NOW_MS,
    companyPrefix: "NEX",
    maxStaleProjects: 3,
    maxRecentCompletions: 3,
    staleThresholdMs: 3 * 24 * 60 * 60 * 1000,
    recentWindowMs: 24 * 60 * 60 * 1000,
  };
}

// ─── Tests ──────────────────────────────────────────────────────────────────

describe("composeHomeStatus", () => {
  it("counts agents whose status is 'running' as active", () => {
    const agents = [
      makeAgent({ status: "running" }),
      makeAgent({ status: "running" }),
      makeAgent({ status: "idle" }),
      makeAgent({ status: "paused" }),
    ];
    const result = composeHomeStatus({
      agents,
      approvals: [],
      activity: [],
      projects: [],
      ...baseArgs(),
    });
    expect(result.activeAgents).toBe(2);
  });

  it("includes only pending approvals as pending gates and resolves project names", () => {
    const proj = makeProject({ id: "proj-a", name: "nexus-design-migration" });
    const approved = makeApproval({ status: "approved", payload: { projectId: "proj-a" } });
    const pending1 = makeApproval({
      status: "pending",
      payload: { projectId: "proj-a", title: "Phase 4 audit" },
    });
    const pending2 = makeApproval({ status: "pending", payload: {} });

    const result = composeHomeStatus({
      agents: [],
      approvals: [approved, pending1, pending2],
      activity: [],
      projects: [proj],
      ...baseArgs(),
    });

    expect(result.pendingGates).toHaveLength(2);
    const first = result.pendingGates[0]!;
    expect(first.gateName).toBe("Phase 4 audit");
    expect(first.projectName).toBe("nexus-design-migration");
    expect(first.href).toBe(`/NEX/approvals/${pending1.id}`);
    // Second gate has no projectId → empty projectName, gateName falls back to type.
    expect(result.pendingGates[1]!.projectName).toBe("");
    expect(result.pendingGates[1]!.gateName).toBe("generic");
  });

  it("surfaces recent completions within the recent window, newest first, capped", () => {
    const proj = makeProject({ id: "proj-a", name: "nexus-demo" });
    const completedRecent = makeActivity({
      id: "e1",
      action: "project.completed",
      entityId: "proj-a",
      details: { summary: "Phase 4 shipped" },
      createdAt: new Date(NOW_MS - 2 * 60 * 60 * 1000), // 2h ago
    });
    const completedOlder = makeActivity({
      id: "e2",
      action: "task.finished",
      entityId: "proj-a",
      details: { summary: "Older one" },
      createdAt: new Date(NOW_MS - 6 * 60 * 60 * 1000), // 6h ago
    });
    const completedAncient = makeActivity({
      id: "e3",
      action: "task.done",
      entityId: "proj-a",
      details: null,
      createdAt: new Date(NOW_MS - 48 * 60 * 60 * 1000), // 48h ago, outside window
    });
    const notACompletion = makeActivity({
      id: "e4",
      action: "project.started",
      entityId: "proj-a",
      createdAt: new Date(NOW_MS - 30 * 60 * 1000),
    });

    const result = composeHomeStatus({
      agents: [],
      approvals: [],
      activity: [completedRecent, completedOlder, completedAncient, notACompletion],
      projects: [proj],
      ...baseArgs(),
    });

    expect(result.recentCompletions.map((c) => c.id)).toEqual(["e1", "e2"]);
    expect(result.recentCompletions[0]!.summary).toBe("Phase 4 shipped");
    expect(result.recentCompletions[0]!.projectName).toBe("nexus-demo");
    expect(result.recentCompletions[0]!.when).toBe("2h ago");
  });

  it("respects maxRecentCompletions cap", () => {
    const completions = Array.from({ length: 5 }).map((_, i) =>
      makeActivity({
        id: `c${i}`,
        action: "completed",
        createdAt: new Date(NOW_MS - (i + 1) * 60 * 1000),
      }),
    );
    const result = composeHomeStatus({
      agents: [],
      approvals: [],
      activity: completions,
      projects: [],
      ...baseArgs(),
      maxRecentCompletions: 2,
    });
    expect(result.recentCompletions).toHaveLength(2);
  });

  it("marks projects with no activity in staleThresholdMs as stale, oldest first", () => {
    const fresh = makeProject({
      id: "fresh",
      name: "fresh",
      updatedAt: new Date(NOW_MS - 60 * 60 * 1000), // 1h ago
    });
    const stale = makeProject({
      id: "stale",
      name: "personal-finance",
      urlKey: "personal-finance",
      updatedAt: new Date(NOW_MS - 5 * 24 * 60 * 60 * 1000), // 5d ago
    });
    const ancient = makeProject({
      id: "ancient",
      name: "ancient",
      urlKey: "ancient",
      updatedAt: new Date(NOW_MS - 30 * 24 * 60 * 60 * 1000), // 30d ago
    });
    const archived = makeProject({
      id: "arch",
      name: "archived-project",
      status: "archived",
      updatedAt: new Date(NOW_MS - 60 * 24 * 60 * 60 * 1000),
    });

    const result = composeHomeStatus({
      agents: [],
      approvals: [],
      activity: [],
      projects: [fresh, stale, ancient, archived],
      ...baseArgs(),
    });

    // Ancient is stalest (oldest), so it should come first.
    expect(result.staleProjects.map((p) => p.id)).toEqual(["ancient", "stale"]);
    expect(result.staleProjects[0]!.lastActivity).toBe("30d ago");
    expect(result.staleProjects[1]!.href).toBe("/NEX/projects/personal-finance");
  });

  it("falls back to absolute paths when companyPrefix is null", () => {
    const stale = makeProject({
      id: "stale",
      name: "stale",
      urlKey: "stale-url",
      updatedAt: new Date(NOW_MS - 10 * 24 * 60 * 60 * 1000),
    });
    const gate = makeApproval({ status: "pending", payload: {} });

    const result = composeHomeStatus({
      agents: [],
      approvals: [gate],
      activity: [],
      projects: [stale],
      ...baseArgs(),
      companyPrefix: null,
    });

    expect(result.staleProjects[0]!.href).toBe("/projects/stale-url");
    expect(result.pendingGates[0]!.href).toBe(`/approvals/${gate.id}`);
    expect(result.companyPrefix).toBeNull();
  });

  it("returns zero/empty values when no data is present", () => {
    const result = composeHomeStatus({
      agents: [],
      approvals: [],
      activity: [],
      projects: [],
      ...baseArgs(),
    });
    expect(result.activeAgents).toBe(0);
    expect(result.pendingGates).toEqual([]);
    expect(result.recentCompletions).toEqual([]);
    expect(result.staleProjects).toEqual([]);
  });
});
