/**
 * TerminalSessionService Tests
 *
 * Unit tests for database-backed terminal session CRUD:
 * create, findByWorkspace, close, and findById.
 * PrismaService is mocked to isolate the service logic.
 */

import { describe, it, expect, beforeEach, vi } from "vitest";
import { NotFoundException } from "@nestjs/common";
import { TerminalSessionStatus } from "@prisma/client";
import type { TerminalSession } from "@prisma/client";
import { TerminalSessionService } from "./terminal-session.service";

// ==========================================
// Helpers
// ==========================================

function makeSession(overrides: Partial<TerminalSession> = {}): TerminalSession {
  return {
    id: "session-uuid-1",
    workspaceId: "workspace-uuid-1",
    name: "Terminal",
    status: TerminalSessionStatus.ACTIVE,
    createdAt: new Date("2026-02-25T00:00:00Z"),
    closedAt: null,
    ...overrides,
  };
}

// ==========================================
// Mock PrismaService
// ==========================================

function makeMockPrisma() {
  return {
    terminalSession: {
      create: vi.fn(),
      findMany: vi.fn(),
      findUnique: vi.fn(),
      update: vi.fn(),
    },
  };
}

// ==========================================
// Tests
// ==========================================

describe("TerminalSessionService", () => {
  let service: TerminalSessionService;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  let mockPrisma: any;

  beforeEach(() => {
    vi.clearAllMocks();
    mockPrisma = makeMockPrisma();
    service = new TerminalSessionService(mockPrisma);
  });

  // ==========================================
  // create
  // ==========================================
  describe("create", () => {
    it("should call prisma.terminalSession.create with workspaceId only when no name provided", async () => {
      const session = makeSession();
      mockPrisma.terminalSession.create.mockResolvedValueOnce(session);

      const result = await service.create("workspace-uuid-1");

      expect(mockPrisma.terminalSession.create).toHaveBeenCalledWith({
        data: { workspaceId: "workspace-uuid-1" },
      });
      expect(result).toEqual(session);
    });

    it("should include name in create data when name is provided", async () => {
      const session = makeSession({ name: "My Terminal" });
      mockPrisma.terminalSession.create.mockResolvedValueOnce(session);

      const result = await service.create("workspace-uuid-1", "My Terminal");

      expect(mockPrisma.terminalSession.create).toHaveBeenCalledWith({
        data: { workspaceId: "workspace-uuid-1", name: "My Terminal" },
      });
      expect(result).toEqual(session);
    });

    it("should return the created session", async () => {
      const session = makeSession();
      mockPrisma.terminalSession.create.mockResolvedValueOnce(session);

      const result = await service.create("workspace-uuid-1");

      expect(result.id).toBe("session-uuid-1");
      expect(result.status).toBe(TerminalSessionStatus.ACTIVE);
    });
  });

  // ==========================================
  // findByWorkspace
  // ==========================================
  describe("findByWorkspace", () => {
    it("should query for ACTIVE sessions in the given workspace, ordered by createdAt desc", async () => {
      const sessions = [makeSession(), makeSession({ id: "session-uuid-2" })];
      mockPrisma.terminalSession.findMany.mockResolvedValueOnce(sessions);

      const result = await service.findByWorkspace("workspace-uuid-1");

      expect(mockPrisma.terminalSession.findMany).toHaveBeenCalledWith({
        where: {
          workspaceId: "workspace-uuid-1",
          status: TerminalSessionStatus.ACTIVE,
        },
        orderBy: { createdAt: "desc" },
      });
      expect(result).toHaveLength(2);
    });

    it("should return an empty array when no active sessions exist", async () => {
      mockPrisma.terminalSession.findMany.mockResolvedValueOnce([]);

      const result = await service.findByWorkspace("workspace-uuid-empty");

      expect(result).toEqual([]);
    });

    it("should not include CLOSED sessions", async () => {
      // The where clause enforces ACTIVE status — verify it is present
      mockPrisma.terminalSession.findMany.mockResolvedValueOnce([]);

      await service.findByWorkspace("workspace-uuid-1");

      const callArgs = mockPrisma.terminalSession.findMany.mock.calls[0][0] as {
        where: { status: TerminalSessionStatus };
      };
      expect(callArgs.where.status).toBe(TerminalSessionStatus.ACTIVE);
    });
  });

  // ==========================================
  // close
  // ==========================================
  describe("close", () => {
    it("should set status to CLOSED and set closedAt when session exists", async () => {
      const existingSession = makeSession();
      const closedSession = makeSession({
        status: TerminalSessionStatus.CLOSED,
        closedAt: new Date("2026-02-25T01:00:00Z"),
      });

      mockPrisma.terminalSession.findUnique.mockResolvedValueOnce(existingSession);
      mockPrisma.terminalSession.update.mockResolvedValueOnce(closedSession);

      const result = await service.close("session-uuid-1");

      expect(mockPrisma.terminalSession.findUnique).toHaveBeenCalledWith({
        where: { id: "session-uuid-1" },
      });
      expect(mockPrisma.terminalSession.update).toHaveBeenCalledWith({
        where: { id: "session-uuid-1" },
        data: {
          status: TerminalSessionStatus.CLOSED,
          closedAt: expect.any(Date),
        },
      });
      expect(result.status).toBe(TerminalSessionStatus.CLOSED);
    });

    it("should throw NotFoundException when session does not exist", async () => {
      mockPrisma.terminalSession.findUnique.mockResolvedValueOnce(null);

      await expect(service.close("nonexistent-id")).rejects.toThrow(NotFoundException);
      expect(mockPrisma.terminalSession.update).not.toHaveBeenCalled();
    });

    it("should include a non-null closedAt timestamp on close", async () => {
      const existingSession = makeSession();
      const closedSession = makeSession({
        status: TerminalSessionStatus.CLOSED,
        closedAt: new Date(),
      });

      mockPrisma.terminalSession.findUnique.mockResolvedValueOnce(existingSession);
      mockPrisma.terminalSession.update.mockResolvedValueOnce(closedSession);

      const result = await service.close("session-uuid-1");

      expect(result.closedAt).not.toBeNull();
    });
  });

  // ==========================================
  // findById
  // ==========================================
  describe("findById", () => {
    it("should return the session when it exists", async () => {
      const session = makeSession();
      mockPrisma.terminalSession.findUnique.mockResolvedValueOnce(session);

      const result = await service.findById("session-uuid-1");

      expect(mockPrisma.terminalSession.findUnique).toHaveBeenCalledWith({
        where: { id: "session-uuid-1" },
      });
      expect(result).toEqual(session);
    });

    it("should return null when session does not exist", async () => {
      mockPrisma.terminalSession.findUnique.mockResolvedValueOnce(null);

      const result = await service.findById("no-such-id");

      expect(result).toBeNull();
    });

    it("should find CLOSED sessions as well as ACTIVE ones", async () => {
      const closedSession = makeSession({
        status: TerminalSessionStatus.CLOSED,
        closedAt: new Date(),
      });
      mockPrisma.terminalSession.findUnique.mockResolvedValueOnce(closedSession);

      const result = await service.findById("session-uuid-1");

      expect(result?.status).toBe(TerminalSessionStatus.CLOSED);
    });
  });
});
