import { FieldType, ProjectStatus } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { beforeEach, describe, expect, it, vi } from "vitest";

const {
  countPlanningEntries,
  loadProjectPlanningReadModel,
  getProjectShoringRatio,
} = vi.hoisted(() => ({
  countPlanningEntries: vi.fn(),
  loadProjectPlanningReadModel: vi.fn(),
  getProjectShoringRatio: vi.fn(),
}));

vi.mock("@capakraken/application", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@capakraken/application")>();
  return {
    ...actual,
    countPlanningEntries,
  };
});

vi.mock("../router/project-planning-read-model.js", () => ({
  loadProjectPlanningReadModel,
}));

vi.mock("../router/project-shoring-ratio.js", () => ({
  getProjectShoringRatio,
}));

import {
  getProjectById,
  getProjectShoringRatioData,
  listProjects,
} from "../router/project-procedure-support.js";

function createContext(db: Record<string, unknown>) {
  return { db: db as never };
}

describe("project-procedure-support", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("lists projects with planning counts and dynamic field filters", async () => {
    const findMany = vi.fn().mockResolvedValue([
      {
        id: "project_1",
        name: "Platform Refresh",
        shortCode: "PRJ-1",
        status: ProjectStatus.ACTIVE,
        startDate: new Date("2026-01-01T00:00:00.000Z"),
      },
    ]);
    const count = vi.fn().mockResolvedValue(1);
    countPlanningEntries.mockResolvedValue({
      countsByProjectId: new Map([["project_1", 3]]),
    });

    const result = await listProjects(
      createContext({
        project: {
          findMany,
          count,
        },
      }),
      {
        limit: 50,
        page: 1,
        status: ProjectStatus.ACTIVE,
        search: "Platform",
        customFieldFilters: [
          { key: "market", value: "de", type: FieldType.TEXT },
        ],
      },
    );

    expect(result.total).toBe(1);
    expect(result.projects).toEqual([
      expect.objectContaining({
        id: "project_1",
        _count: { allocations: 3 },
      }),
    ]);
    expect(findMany).toHaveBeenCalledWith({
      where: {
        status: ProjectStatus.ACTIVE,
        OR: [
          { name: { contains: "Platform", mode: "insensitive" } },
          { shortCode: { contains: "Platform", mode: "insensitive" } },
        ],
        AND: [
          {
            dynamicFields: {
              path: ["market"],
              string_contains: "de",
            },
          },
        ],
      },
      skip: 0,
      take: 51,
      orderBy: [{ startDate: "asc" }, { id: "asc" }],
    });
    expect(count).toHaveBeenCalledWith(expect.objectContaining({
      where: expect.objectContaining({
        status: ProjectStatus.ACTIVE,
      }),
    }));
    expect(countPlanningEntries).toHaveBeenCalledWith(
      expect.objectContaining({
        project: expect.objectContaining({
          findMany,
          count,
        }),
      }),
      { projectIds: ["project_1"] },
    );
  });

  it("returns a project detail enriched with planning read model data", async () => {
    const findUnique = vi.fn().mockResolvedValue({
      id: "project_1",
      name: "Platform Refresh",
      blueprint: null,
    });
    loadProjectPlanningReadModel.mockResolvedValue({
      readModel: {
        assignments: [{ id: "assignment_1" }],
        demands: [{ id: "demand_1" }],
      },
    });

    const result = await getProjectById(
      createContext({
        project: { findUnique },
      }),
      { id: "project_1" },
    );

    expect(result).toEqual({
      id: "project_1",
      name: "Platform Refresh",
      blueprint: null,
      allocations: [{ id: "assignment_1" }],
      demands: [{ id: "demand_1" }],
      assignments: [{ id: "assignment_1" }],
    });
    expect(findUnique).toHaveBeenCalledWith({
      where: { id: "project_1" },
      include: { blueprint: true },
    });
    expect(loadProjectPlanningReadModel).toHaveBeenCalledWith(
      expect.objectContaining({
        project: expect.objectContaining({ findUnique }),
      }),
      { projectId: "project_1" },
    );
  });

  it("throws not found when the requested project is missing", async () => {
    loadProjectPlanningReadModel.mockResolvedValue({
      readModel: { assignments: [], demands: [] },
    });

    await expect(
      getProjectById(
        createContext({
          project: { findUnique: vi.fn().mockResolvedValue(null) },
        }),
        { id: "missing" },
      ),
    ).rejects.toThrowError(new TRPCError({
      code: "NOT_FOUND",
      message: "Project not found",
    }));
  });

  it("delegates shoring ratio reads to the dedicated shoring helper", async () => {
    getProjectShoringRatio.mockResolvedValue({
      totalHours: 40,
      onshoreRatio: 60,
      offshoreRatio: 40,
    });

    const result = await getProjectShoringRatioData(
      createContext({
        project: { findUnique: vi.fn() },
      }),
      { projectId: "project_1" },
    );

    expect(result).toEqual({
      totalHours: 40,
      onshoreRatio: 60,
      offshoreRatio: 40,
    });
    expect(getProjectShoringRatio).toHaveBeenCalledWith(
      expect.any(Object),
      "project_1",
    );
  });
});
