import { countPlanningEntries } from "@capakraken/application";
import { FieldType, ProjectStatus } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { paginate, PaginationInputSchema } from "../db/pagination.js";
import type { TRPCContext } from "../trpc.js";
import { buildDynamicFieldWhereClauses } from "./custom-field-filters.js";
import { loadProjectPlanningReadModel } from "./project-planning-read-model.js";
import { getProjectShoringRatio } from "./project-shoring-ratio.js";

export const ProjectListInputSchema = PaginationInputSchema.extend({
  status: z.nativeEnum(ProjectStatus).optional(),
  search: z.string().optional(),
  customFieldFilters: z.array(z.object({
    key: z.string(),
    value: z.string(),
    type: z.nativeEnum(FieldType),
  })).optional(),
});

export const ProjectIdInputSchema = z.object({
  id: z.string(),
});

export const ProjectShoringRatioInputSchema = z.object({
  projectId: z.string(),
});

type ProjectProcedureContext = Pick<TRPCContext, "db">;

export async function listProjects(
  ctx: ProjectProcedureContext,
  input: z.infer<typeof ProjectListInputSchema>,
) {
  const { status, search, cursor, customFieldFilters } = input;

  const cfConditions = buildDynamicFieldWhereClauses(customFieldFilters)
    .map((dynamicFields) => ({ dynamicFields }));

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const where: any = {
    ...(status ? { status } : {}),
    ...(search
      ? {
          OR: [
            { name: { contains: search, mode: "insensitive" as const } },
            { shortCode: { contains: search, mode: "insensitive" as const } },
          ],
        }
      : {}),
    ...(cfConditions.length > 0 ? { AND: cfConditions } : {}),
  };

  const whereWithCursor = cursor ? { ...where, id: { gt: cursor } } : where;

  const result = await paginate(
    ({ skip, take }) =>
      ctx.db.project.findMany({
        where: whereWithCursor,
        skip,
        take,
        orderBy: [{ startDate: "asc" }, { id: "asc" }],
      }),
    () => ctx.db.project.count({ where }),
    input,
  );

  const { countsByProjectId } = await countPlanningEntries(ctx.db, {
    projectIds: result.items.map((project) => project.id),
  });

  return {
    projects: result.items.map((project) => ({
      ...project,
      _count: {
        allocations: countsByProjectId.get(project.id) ?? 0,
      },
    })),
    total: result.total,
    page: result.page,
    limit: result.limit,
    nextCursor: result.nextCursor,
  };
}

export async function getProjectById(
  ctx: ProjectProcedureContext,
  input: z.infer<typeof ProjectIdInputSchema>,
) {
  const [project, planningRead] = await Promise.all([
    ctx.db.project.findUnique({
      where: { id: input.id },
      include: { blueprint: true },
    }),
    loadProjectPlanningReadModel(ctx.db, { projectId: input.id }),
  ]);

  if (!project) {
    throw new TRPCError({ code: "NOT_FOUND", message: "Project not found" });
  }

  return {
    ...project,
    allocations: planningRead.readModel.assignments,
    demands: planningRead.readModel.demands,
    assignments: planningRead.readModel.assignments,
  };
}

export async function getProjectShoringRatioData(
  ctx: ProjectProcedureContext,
  input: z.infer<typeof ProjectShoringRatioInputSchema>,
) {
  return getProjectShoringRatio(ctx.db, input.projectId);
}
