/**
 * Unit tests for automatic lifecycle maintenance.
 *
 * Run with: bun test packages/mcp-server/src/__tests__/lifecycle-maintenance.test.ts
 */

import { describe, expect, mock, test } from "bun:test";
import { runLifecycleMaintenance } from "../lifecycle-maintenance.js";

function daysAgo(days: number): string {
  return new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
}

function makeMockClient(entities: unknown[]) {
  const deletedIds: string[] = [];
  const updatedEntities: Array<{
    id: string;
    updates: Record<string, unknown>;
  }> = [];

  return {
    client: {
      listMemoryEntities: mock(async () => ({
        entities,
        count: entities.length,
      })),
      deleteMemoryEntity: mock(async (id: string) => {
        deletedIds.push(id);
        return { success: true };
      }),
      updateMemoryEntity: mock(
        async (id: string, updates: Record<string, unknown>) => {
          updatedEntities.push({ id, updates });
          return { entity: { id, ...updates } };
        },
      ),
    } as any,
    deletedIds,
    updatedEntities,
  };
}

describe("runLifecycleMaintenance", () => {
  test("archives low-confidence entities", async () => {
    const { client, deletedIds } = makeMockClient([
      {
        id: "low-conf-1",
        title: "Bad memory",
        memory_tier: "draft",
        confidence: 0.1,
        access_count: 0,
        last_accessed_at: daysAgo(5),
        created_at: daysAgo(10),
      },
    ]);

    const result = await runLifecycleMaintenance(client, "ws-1");
    expect(result.archived).toBe(1);
    expect(deletedIds).toContain("low-conf-1");
  });

  test("prunes stale drafts older than 30 days with low decay", async () => {
    const { client, deletedIds } = makeMockClient([
      {
        id: "stale-draft-1",
        title: "Old draft",
        memory_tier: "draft",
        confidence: 0.5,
        access_count: 0,
        last_accessed_at: daysAgo(35),
        created_at: daysAgo(35),
      },
    ]);

    const result = await runLifecycleMaintenance(client, "ws-1");
    expect(result.pruned).toBe(1);
    expect(deletedIds).toContain("stale-draft-1");
  });

  test("does NOT prune recent drafts", async () => {
    const { client, deletedIds } = makeMockClient([
      {
        id: "fresh-draft",
        title: "New draft",
        memory_tier: "draft",
        confidence: 0.5,
        access_count: 1,
        last_accessed_at: daysAgo(2),
        created_at: daysAgo(5),
      },
    ]);

    const result = await runLifecycleMaintenance(client, "ws-1");
    expect(result.pruned).toBe(0);
    expect(result.archived).toBe(0);
    expect(deletedIds).toHaveLength(0);
  });

  test("auto-promotes eligible draft to episode", async () => {
    const { client, updatedEntities } = makeMockClient([
      {
        id: "promote-me",
        title: "Well-used draft",
        memory_tier: "draft",
        confidence: 0.85,
        access_count: 6,
        last_accessed_at: daysAgo(0),
        created_at: daysAgo(3),
      },
    ]);

    const result = await runLifecycleMaintenance(client, "ws-1");
    expect(result.promoted).toBe(1);
    expect(updatedEntities[0].id).toBe("promote-me");
    expect(updatedEntities[0].updates.memory_tier).toBe("episode");
  });

  test("auto-promotes eligible episode to reference", async () => {
    const { client, updatedEntities } = makeMockClient([
      {
        id: "promote-ep",
        title: "Proven episode",
        memory_tier: "episode",
        confidence: 0.95,
        access_count: 12,
        last_accessed_at: daysAgo(1),
        created_at: daysAgo(10),
      },
    ]);

    const result = await runLifecycleMaintenance(client, "ws-1");
    expect(result.promoted).toBe(1);
    expect(updatedEntities[0].updates.memory_tier).toBe("reference");
  });

  test("flags stale entities for review", async () => {
    const { client, updatedEntities } = makeMockClient([
      {
        id: "stale-ep",
        title: "Forgotten episode",
        memory_tier: "episode",
        confidence: 0.6,
        access_count: 2,
        last_accessed_at: daysAgo(100),
        created_at: daysAgo(120),
      },
    ]);

    const result = await runLifecycleMaintenance(client, "ws-1");
    expect(result.reviewed).toBe(1);
    expect(updatedEntities[0].id).toBe("stale-ep");
    expect(updatedEntities[0].updates.metadata).toHaveProperty(
      "needs_review",
      true,
    );
  });

  test("does not flag frequently accessed stale entities", async () => {
    const { client, updatedEntities } = makeMockClient([
      {
        id: "old-but-used",
        title: "Still relevant",
        memory_tier: "episode",
        confidence: 0.7,
        access_count: 10,
        last_accessed_at: daysAgo(100),
        created_at: daysAgo(120),
      },
    ]);

    const result = await runLifecycleMaintenance(client, "ws-1");
    expect(result.reviewed).toBe(0);
    expect(updatedEntities).toHaveLength(0);
  });

  test("handles empty entity list", async () => {
    const { client } = makeMockClient([]);
    const result = await runLifecycleMaintenance(client, "ws-1");
    expect(result.archived).toBe(0);
    expect(result.pruned).toBe(0);
    expect(result.promoted).toBe(0);
    expect(result.reviewed).toBe(0);
  });

  test("handles API errors gracefully", async () => {
    const client = {
      listMemoryEntities: mock(async () => {
        throw new Error("API down");
      }),
    } as any;

    const result = await runLifecycleMaintenance(client, "ws-1");
    expect(result.errors).toBe(0); // Early return, no entity-level errors
    expect(result.archived).toBe(0);
  });

  test("processes mixed entities correctly", async () => {
    const { client, deletedIds, updatedEntities } = makeMockClient([
      // Should be archived (low confidence)
      {
        id: "archive-me",
        title: "Low conf",
        memory_tier: "draft",
        confidence: 0.2,
        access_count: 1,
        last_accessed_at: daysAgo(5),
        created_at: daysAgo(10),
      },
      // Should be promoted (draft→episode)
      {
        id: "promote-me",
        title: "Good draft",
        memory_tier: "draft",
        confidence: 0.85,
        access_count: 7,
        last_accessed_at: daysAgo(0),
        created_at: daysAgo(5),
      },
      // Should be left alone (healthy reference)
      {
        id: "leave-me",
        title: "Healthy ref",
        memory_tier: "reference",
        confidence: 0.95,
        access_count: 20,
        last_accessed_at: daysAgo(1),
        created_at: daysAgo(60),
      },
    ]);

    const result = await runLifecycleMaintenance(client, "ws-1");
    expect(result.archived).toBe(1);
    expect(result.promoted).toBe(1);
    expect(deletedIds).toContain("archive-me");
    expect(updatedEntities[0].id).toBe("promote-me");
    expect(updatedEntities[0].updates.memory_tier).toBe("episode");
  });
});
