import {
  ImportBatchStatus,
  StagedRecordStatus,
} from "@capakraken/db";
import { TRPCError } from "@trpc/server";

type PaginationInput = {
  cursor?: string | undefined;
  limit: number;
};

function toPaginatedResult<T extends { id: string }>(items: T[], limit: number) {
  let nextCursor: string | undefined;
  if (items.length > limit) {
    const next = items.pop();
    nextCursor = next?.id;
  }

  return { items, nextCursor };
}

function toCursorPagination(input: PaginationInput) {
  return {
    take: input.limit + 1,
    ...(input.cursor ? { cursor: { id: input.cursor }, skip: 1 } : {}),
  };
}

export async function listImportBatches(
  db: { importBatch: { findMany: Function } },
  input: PaginationInput & { status?: ImportBatchStatus | undefined },
) {
  const items = await db.importBatch.findMany({
    where: {
      ...(input.status !== undefined ? { status: input.status } : {}),
    },
    orderBy: { createdAt: "desc" },
    ...toCursorPagination(input),
  });

  return toPaginatedResult(items, input.limit);
}

export async function getImportBatch(
  db: {
    importBatch: { findUnique: Function };
    stagedResource: { count: Function };
    stagedClient: { count: Function };
    stagedProject: { count: Function };
    stagedAssignment: { count: Function };
    stagedVacation: { count: Function };
    stagedAvailabilityRule: { count: Function };
    stagedUnresolvedRecord: { count: Function };
  },
  id: string,
) {
  const batch = await db.importBatch.findUnique({
    where: { id },
  });

  if (!batch) {
    throw new TRPCError({ code: "NOT_FOUND", message: `Import batch "${id}" not found` });
  }

  const [
    resourceCount,
    clientCount,
    projectCount,
    assignmentCount,
    vacationCount,
    availabilityRuleCount,
    unresolvedCount,
  ] = await Promise.all([
    db.stagedResource.count({ where: { importBatchId: id } }),
    db.stagedClient.count({ where: { importBatchId: id } }),
    db.stagedProject.count({ where: { importBatchId: id } }),
    db.stagedAssignment.count({ where: { importBatchId: id } }),
    db.stagedVacation.count({ where: { importBatchId: id } }),
    db.stagedAvailabilityRule.count({ where: { importBatchId: id } }),
    db.stagedUnresolvedRecord.count({ where: { importBatchId: id } }),
  ]);

  return {
    ...batch,
    counts: {
      assignmentCount,
      availabilityRuleCount,
      clientCount,
      projectCount,
      resourceCount,
      unresolvedCount,
      vacationCount,
    },
  };
}

export async function listStagedResources(
  db: { stagedResource: { findMany: Function } },
  input: PaginationInput & {
    importBatchId: string;
    status?: StagedRecordStatus | undefined;
  },
) {
  const items = await db.stagedResource.findMany({
    where: {
      importBatchId: input.importBatchId,
      ...(input.status !== undefined ? { status: input.status } : {}),
    },
    orderBy: { canonicalExternalId: "asc" },
    ...toCursorPagination(input),
  });

  return toPaginatedResult(items, input.limit);
}

export async function listStagedProjects(
  db: { stagedProject: { findMany: Function } },
  input: PaginationInput & {
    importBatchId: string;
    isTbd?: boolean | undefined;
    status?: StagedRecordStatus | undefined;
  },
) {
  const items = await db.stagedProject.findMany({
    where: {
      importBatchId: input.importBatchId,
      ...(input.status !== undefined ? { status: input.status } : {}),
      ...(input.isTbd !== undefined ? { isTbd: input.isTbd } : {}),
    },
    orderBy: { projectKey: "asc" },
    ...toCursorPagination(input),
  });

  return toPaginatedResult(items, input.limit);
}

export async function listStagedAssignments(
  db: { stagedAssignment: { findMany: Function } },
  input: PaginationInput & {
    importBatchId: string;
    resourceExternalId?: string | undefined;
    status?: StagedRecordStatus | undefined;
  },
) {
  const items = await db.stagedAssignment.findMany({
    where: {
      importBatchId: input.importBatchId,
      ...(input.status !== undefined ? { status: input.status } : {}),
      ...(input.resourceExternalId !== undefined ? { resourceExternalId: input.resourceExternalId } : {}),
    },
    orderBy: { createdAt: "asc" },
    ...toCursorPagination(input),
  });

  return toPaginatedResult(items, input.limit);
}

export async function listStagedVacations(
  db: { stagedVacation: { findMany: Function } },
  input: PaginationInput & {
    importBatchId: string;
    resourceExternalId?: string | undefined;
  },
) {
  const items = await db.stagedVacation.findMany({
    where: {
      importBatchId: input.importBatchId,
      ...(input.resourceExternalId !== undefined ? { resourceExternalId: input.resourceExternalId } : {}),
    },
    orderBy: { startDate: "asc" },
    ...toCursorPagination(input),
  });

  return toPaginatedResult(items, input.limit);
}

export async function listStagedUnresolvedRecords(
  db: { stagedUnresolvedRecord: { findMany: Function } },
  input: PaginationInput & {
    importBatchId: string;
    recordType?: string | undefined;
  },
) {
  const items = await db.stagedUnresolvedRecord.findMany({
    where: {
      importBatchId: input.importBatchId,
      ...(input.recordType !== undefined ? { recordType: input.recordType } : {}),
    },
    orderBy: { createdAt: "asc" },
    ...toCursorPagination(input),
  });

  return toPaginatedResult(items, input.limit);
}
