import { describe, expect, it, vi } from "vitest";
import { appRouter } from "./routers";
import type { TrpcContext } from "./_core/context";

type AuthenticatedUser = NonNullable<TrpcContext["user"]>;

/* ─── Context helpers ─── */
function createContext(role: "admin" | "agent" | "user" | null): TrpcContext {
  if (!role) {
    return {
      user: null,
      req: { protocol: "https", headers: {} } as TrpcContext["req"],
      res: { clearCookie: vi.fn() } as unknown as TrpcContext["res"],
    };
  }

  const user: AuthenticatedUser = {
    id: role === "admin" ? 1 : role === "agent" ? 2 : 3,
    openId: `${role}-openid`,
    email: `${role}@homelegance.com`,
    name: `Test ${role.charAt(0).toUpperCase() + role.slice(1)}`,
    loginMethod: "manus",
    role,
    createdAt: new Date(),
    updatedAt: new Date(),
    lastSignedIn: new Date(),
  };

  return {
    user,
    req: { protocol: "https", headers: {} } as TrpcContext["req"],
    res: { clearCookie: vi.fn() } as unknown as TrpcContext["res"],
  };
}

/* ─── Mock database ─── */
const mockConversations = [
  { id: 1, sessionId: "s1", visitorName: "Alice", visitorEmail: "alice@test.com", status: "active", assignedAgentId: 2, metadata: {}, createdAt: new Date(), updatedAt: new Date() },
  { id: 2, sessionId: "s2", visitorName: "Bob", visitorEmail: "bob@test.com", status: "escalated", assignedAgentId: null, metadata: {}, createdAt: new Date(), updatedAt: new Date() },
  { id: 3, sessionId: "s3", visitorName: "Charlie", visitorEmail: null, status: "resolved", assignedAgentId: 1, metadata: {}, createdAt: new Date(), updatedAt: new Date() },
];

const mockAgents = [
  { id: 1, name: "Admin User", email: "admin@homelegance.com", role: "admin" },
  { id: 2, name: "Agent User", email: "agent@homelegance.com", role: "agent" },
];

vi.mock("./db", () => {
  const users = [
    { id: 1, openId: "admin-openid", name: "Test Admin", email: "admin@homelegance.com", role: "admin", lastSignedIn: new Date(), createdAt: new Date(), updatedAt: new Date() },
    { id: 2, openId: "agent-openid", name: "Test Agent", email: "agent@homelegance.com", role: "agent", lastSignedIn: new Date(), createdAt: new Date(), updatedAt: new Date() },
    { id: 3, openId: "user-openid", name: "Test User", email: "user@homelegance.com", role: "user", lastSignedIn: new Date(), createdAt: new Date(), updatedAt: new Date() },
  ];

  return {
    createConversation: vi.fn(async (data: any) => ({ id: 1, ...data, createdAt: new Date(), updatedAt: new Date() })),
    getConversations: vi.fn(async () => []),
    getConversationsAdvanced: vi.fn(async (params: any) => ({
      conversations: [
        { id: 1, sessionId: "s1", visitorName: "Alice", status: "active", assignedAgentId: 2, customerId: "CUST-001", salesRep: "Jane Smith", agentName: "Test Agent", createdAt: new Date(), updatedAt: new Date() },
        { id: 2, sessionId: "s2", visitorName: "Bob", status: "escalated", assignedAgentId: null, customerId: null, salesRep: null, agentName: null, createdAt: new Date(), updatedAt: new Date() },
      ],
      total: 25,
      page: params?.page || 1,
      pageSize: params?.pageSize || 20,
      totalPages: 2,
    })),
    getConversationById: vi.fn(async (id: number) => ({ id, sessionId: "test", status: "active", createdAt: new Date(), updatedAt: new Date() })),
    getConversationBySessionId: vi.fn(async () => null),
    updateConversationStatus: vi.fn(async (id: number, status: string) => ({ id, status })),
    getConversationStats: vi.fn(async () => ({ total: 25, active: 10, escalated: 5, resolved: 7, closed: 3 })),
    addMessage: vi.fn(async (data: any) => ({ id: 1, ...data, createdAt: new Date() })),
    getMessagesByConversation: vi.fn(async () => []),
    getConversationMessageCounts: vi.fn(async (ids: number[]) => {
      const counts: Record<number, number> = {};
      ids.forEach((id, i) => { counts[id] = (i + 1) * 3; });
      return counts;
    }),
    getAgentUsers: vi.fn(async () => [
      { id: 1, name: "Admin User", email: "admin@homelegance.com", role: "admin" },
      { id: 2, name: "Agent User", email: "agent@homelegance.com", role: "agent" },
    ]),
    bulkUpdateConversationStatus: vi.fn(async (ids: number[], status: string) => ({ updated: ids.length })),
    deleteConversations: vi.fn(async (ids: number[]) => ({ deleted: ids.length })),
    saveWorkflow: vi.fn(async (wid: string, nodes: any[], edges: any[]) => ({ workflowId: wid, nodeCount: nodes.length, edgeCount: edges.length })),
    getWorkflow: vi.fn(async () => ({ nodes: [], edges: [] })),
    getAllUsers: vi.fn(async () => users),
    updateUserRole: vi.fn(async (userId: number, role: string) => {
      const u = users.find(u => u.id === userId);
      if (!u) return null;
      return { ...u, role };
    }),
    getUserById: vi.fn(async (userId: number) => users.find(u => u.id === userId)),
    getUserByEmail: vi.fn(async (email: string) => users.find(u => u.email === email) || null),
    deleteUser: vi.fn(async (userId: number) => users.find(u => u.id === userId) || null),
    getUserByEmailWithPassword: vi.fn(async () => null),
    createUserWithPassword: vi.fn(async (data: any) => ({ id: 10, ...data })),
    updateUserPassword: vi.fn(async () => {}),
    createPasswordResetToken: vi.fn(async (data: any) => ({ id: 1, ...data })),
    getPasswordResetToken: vi.fn(async () => null),
    markPasswordResetTokenUsed: vi.fn(async () => {}),
    createInvitation: vi.fn(async (data: any) => ({ id: 1, ...data, createdAt: new Date() })),
    getAllInvitations: vi.fn(async () => []),
    getInvitationByToken: vi.fn(async () => null),
    getInvitationByEmail: vi.fn(async () => []),
    updateInvitationStatus: vi.fn(async (id: number, status: string) => ({ id, status })),
    expireOldInvitations: vi.fn(async () => {}),
    createAuditLog: vi.fn(async (data: any) => ({ id: 1, ...data, createdAt: new Date() })),
    getAuditLogs: vi.fn(async () => []),
    upsertUser: vi.fn(),
    getUserByOpenId: vi.fn(),
  };
});

vi.mock("./_core/llm", () => ({
  invokeLLM: vi.fn(async () => ({
    id: "test",
    created: Date.now(),
    model: "test",
    choices: [{ index: 0, message: { role: "assistant", content: "Test response from Ellie" }, finish_reason: "stop" }],
  })),
}));

/* ═══════════════════════════════════════════════════════════════
   ADVANCED CONVERSATION QUERIES
   ═══════════════════════════════════════════════════════════════ */

describe("Advanced conversation queries", () => {
  it("returns paginated conversations with message counts for admin", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const result = await caller.agent.conversationsAdvanced({
      page: 1,
      pageSize: 20,
      sortBy: "updated",
      sortOrder: "desc",
    });
    expect(result.total).toBe(25);
    expect(result.page).toBe(1);
    expect(result.totalPages).toBe(2);
    expect(result.conversations).toHaveLength(2);
    // Message counts should be enriched
    expect(result.conversations[0]).toHaveProperty("messageCount");
    expect(typeof result.conversations[0].messageCount).toBe("number");
  });

  it("returns paginated conversations for agent role", async () => {
    const caller = appRouter.createCaller(createContext("agent"));
    const result = await caller.agent.conversationsAdvanced({
      page: 1,
      pageSize: 20,
      sortBy: "updated",
      sortOrder: "desc",
    });
    expect(result.total).toBe(25);
    expect(result.conversations).toHaveLength(2);
  });

  it("rejects advanced query for regular user", async () => {
    const caller = appRouter.createCaller(createContext("user"));
    await expect(
      caller.agent.conversationsAdvanced({
        page: 1,
        pageSize: 20,
        sortBy: "updated",
        sortOrder: "desc",
      })
    ).rejects.toThrow("Agent or admin access required");
  });

  it("rejects advanced query for unauthenticated user", async () => {
    const caller = appRouter.createCaller(createContext(null));
    await expect(
      caller.agent.conversationsAdvanced({
        page: 1,
        pageSize: 20,
        sortBy: "updated",
        sortOrder: "desc",
      })
    ).rejects.toThrow();
  });

  it("supports filtering by status", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const result = await caller.agent.conversationsAdvanced({
      page: 1,
      pageSize: 20,
      status: "escalated",
      sortBy: "updated",
      sortOrder: "desc",
    });
    expect(result).toBeDefined();
    expect(result.conversations).toBeDefined();
  });

  it("supports filtering by agent", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const result = await caller.agent.conversationsAdvanced({
      page: 1,
      pageSize: 20,
      agentId: 2,
      sortBy: "updated",
      sortOrder: "desc",
    });
    expect(result).toBeDefined();
  });

  it("supports search query", async () => {
    const caller = appRouter.createCaller(createContext("agent"));
    const result = await caller.agent.conversationsAdvanced({
      page: 1,
      pageSize: 20,
      search: "Alice",
      sortBy: "updated",
      sortOrder: "desc",
    });
    expect(result).toBeDefined();
  });

  it("supports date range filtering", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const result = await caller.agent.conversationsAdvanced({
      page: 1,
      pageSize: 20,
      dateFrom: "2025-01-01",
      dateTo: "2026-12-31",
      sortBy: "created",
      sortOrder: "asc",
    });
    expect(result).toBeDefined();
  });

  it("supports page 2 navigation", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const result = await caller.agent.conversationsAdvanced({
      page: 2,
      pageSize: 20,
      sortBy: "updated",
      sortOrder: "desc",
    });
    expect(result.page).toBe(2);
  });

  it("supports sorting by customerId", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const result = await caller.agent.conversationsAdvanced({
      page: 1,
      pageSize: 20,
      sortBy: "customerId",
      sortOrder: "asc",
    });
    expect(result).toBeDefined();
    expect(result.conversations).toBeDefined();
  });

  it("supports sorting by salesRep", async () => {
    const caller = appRouter.createCaller(createContext("agent"));
    const result = await caller.agent.conversationsAdvanced({
      page: 1,
      pageSize: 20,
      sortBy: "salesRep",
      sortOrder: "desc",
    });
    expect(result).toBeDefined();
    expect(result.conversations).toBeDefined();
  });

  it("supports sorting by agent name", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const result = await caller.agent.conversationsAdvanced({
      page: 1,
      pageSize: 20,
      sortBy: "agent",
      sortOrder: "asc",
    });
    expect(result).toBeDefined();
    expect(result.conversations).toBeDefined();
  });

  it("returns agentName field in conversation data", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const result = await caller.agent.conversationsAdvanced({
      page: 1,
      pageSize: 20,
      sortBy: "updated",
      sortOrder: "desc",
    });
    // First conversation has an assigned agent
    expect(result.conversations[0]).toHaveProperty("agentName");
    expect(result.conversations[0].agentName).toBe("Test Agent");
    // Second conversation has no assigned agent
    expect(result.conversations[1]).toHaveProperty("agentName");
    expect(result.conversations[1].agentName).toBeNull();
  });

  it("returns customerId and salesRep fields in conversation data", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const result = await caller.agent.conversationsAdvanced({
      page: 1,
      pageSize: 20,
      sortBy: "updated",
      sortOrder: "desc",
    });
    expect(result.conversations[0]).toHaveProperty("customerId");
    expect(result.conversations[0].customerId).toBe("CUST-001");
    expect(result.conversations[0]).toHaveProperty("salesRep");
    expect(result.conversations[0].salesRep).toBe("Jane Smith");
  });
});

/* ═══════════════════════════════════════════════════════════════
   AGENT LIST
   ═══════════════════════════════════════════════════════════════ */

describe("Agent list for filter dropdown", () => {
  it("returns agent list for admin", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const agents = await caller.agent.agents();
    expect(agents).toHaveLength(2);
    expect(agents[0]).toHaveProperty("name");
    expect(agents[0]).toHaveProperty("role");
  });

  it("returns agent list for agent role", async () => {
    const caller = appRouter.createCaller(createContext("agent"));
    const agents = await caller.agent.agents();
    expect(agents).toHaveLength(2);
  });

  it("rejects agent list for regular user", async () => {
    const caller = appRouter.createCaller(createContext("user"));
    await expect(caller.agent.agents()).rejects.toThrow("Agent or admin access required");
  });
});

/* ═══════════════════════════════════════════════════════════════
   BULK ACTIONS
   ═══════════════════════════════════════════════════════════════ */

describe("Bulk conversation actions", () => {
  it("allows agent to bulk resolve conversations", async () => {
    const caller = appRouter.createCaller(createContext("agent"));
    const result = await caller.agent.bulkUpdateStatus({
      conversationIds: [1, 2, 3],
      status: "resolved",
    });
    expect(result.updated).toBe(3);
  });

  it("allows admin to bulk close conversations", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const result = await caller.agent.bulkUpdateStatus({
      conversationIds: [1, 2],
      status: "closed",
    });
    expect(result.updated).toBe(2);
  });

  it("rejects bulk action for regular user", async () => {
    const caller = appRouter.createCaller(createContext("user"));
    await expect(
      caller.agent.bulkUpdateStatus({ conversationIds: [1], status: "resolved" })
    ).rejects.toThrow("Agent or admin access required");
  });

  it("rejects bulk action for unauthenticated user", async () => {
    const caller = appRouter.createCaller(createContext(null));
    await expect(
      caller.agent.bulkUpdateStatus({ conversationIds: [1], status: "resolved" })
    ).rejects.toThrow();
  });
});

/* ═══════════════════════════════════════════════════════════════
   DELETE CONVERSATIONS (admin only)
   ═══════════════════════════════════════════════════════════════ */

describe("Delete conversations (admin only)", () => {
  it("allows admin to delete conversations", async () => {
    const caller = appRouter.createCaller(createContext("admin"));
    const result = await caller.agent.deleteConversations({
      conversationIds: [1, 2],
    });
    expect(result.deleted).toBe(2);
  });

  it("rejects delete for agent role", async () => {
    const caller = appRouter.createCaller(createContext("agent"));
    await expect(
      caller.agent.deleteConversations({ conversationIds: [1] })
    ).rejects.toThrow();
  });

  it("rejects delete for regular user", async () => {
    const caller = appRouter.createCaller(createContext("user"));
    await expect(
      caller.agent.deleteConversations({ conversationIds: [1] })
    ).rejects.toThrow();
  });

  it("rejects delete for unauthenticated user", async () => {
    const caller = appRouter.createCaller(createContext(null));
    await expect(
      caller.agent.deleteConversations({ conversationIds: [1] })
    ).rejects.toThrow();
  });
});
