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 { buildDynamicFieldWhereClauses } from "./custom-field-filters.js";
import { projectCostReadProcedures } from "./project-cost-read.js";
import { projectCoverProcedures } from "./project-cover.js";
import { projectIdentifierReadProcedures } from "./project-identifier-read.js";
import { createProjectLifecycleProcedures } from "./project-lifecycle.js";
import { createProjectMutationProcedures } from "./project-mutations.js";
import { loadProjectPlanningReadModel } from "./project-planning-read-model.js";
import { createProjectBackgroundEffects } from "./project-background-effects.js";
import { getProjectShoringRatio } from "./project-shoring-ratio.js";
import { controllerProcedure, createTRPCRouter } from "../trpc.js";

const projectBackgroundEffects = createProjectBackgroundEffects();

export const projectRouter = createTRPCRouter({
  ...projectCostReadProcedures,
  ...projectCoverProcedures,
  ...projectIdentifierReadProcedures,
  ...createProjectLifecycleProcedures({
    invalidateDashboardCacheInBackground: projectBackgroundEffects.invalidateDashboardCacheInBackground,
    dispatchProjectWebhookInBackground: projectBackgroundEffects.dispatchProjectWebhookInBackground,
  }),
  ...createProjectMutationProcedures(projectBackgroundEffects),

  list: controllerProcedure
    .input(
      PaginationInputSchema.extend({
        status: z.nativeEnum(ProjectStatus).optional(),
        search: z.string().optional(),
        // Custom field JSONB filters
        customFieldFilters: z.array(z.object({
          key: z.string(),
          value: z.string(),
          type: z.nativeEnum(FieldType),
        })).optional(),
      }),
    )
    .query(async ({ ctx, input }) => {
      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,
      };
    }),

  getById: controllerProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      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,
      };
    }),

  getShoringRatio: controllerProcedure
    .input(z.object({ projectId: z.string() }))
    .query(async ({ ctx, input }) => getProjectShoringRatio(ctx.db, input.projectId)),

});
