import type { Id } from "../../shared/api/types";

export interface BindAttachmentsRequest {
  attachmentIds: Id[];
  mainPlanRowVersion: number;
}

export interface BindAttachmentsResult {
  createdCount: number;
  existingCount: number;
  attachmentCount: number;
  mainPlanRowVersion: number;
}

export interface UnbindAttachmentResult {
  attachmentCount: number;
  mainPlanRowVersion: number;
}

type RecordValue = Record<string, unknown>;
const idPattern = /^[0-9]+$/;

function isRecord(value: unknown): value is RecordValue {
  return !!value && typeof value === "object" && !Array.isArray(value);
}

function hasExactKeys(value: RecordValue, keys: readonly string[]): boolean {
  const actual = Object.keys(value).sort();
  const expected = [...keys].sort();
  return actual.length === expected.length &&
    actual.every((key, index) => key === expected[index]);
}

function isNonNegativeInteger(value: unknown): value is number {
  return typeof value === "number" && Number.isInteger(value) && value >= 0;
}

export function isStringId(value: unknown): value is Id {
  return typeof value === "string" && idPattern.test(value);
}

export function isBindAttachmentsRequest(
  value: unknown,
): value is BindAttachmentsRequest {
  if (!isRecord(value) ||
      !hasExactKeys(value, ["attachmentIds", "mainPlanRowVersion"])) {
    return false;
  }
  return (
    Array.isArray(value.attachmentIds) &&
    value.attachmentIds.length > 0 &&
    value.attachmentIds.every(isStringId) &&
    isNonNegativeInteger(value.mainPlanRowVersion)
  );
}

export function isBindAttachmentsResult(
  value: unknown,
): value is BindAttachmentsResult {
  if (!isRecord(value) || !hasExactKeys(value, [
    "createdCount",
    "existingCount",
    "attachmentCount",
    "mainPlanRowVersion",
  ])) {
    return false;
  }
  return (
    isNonNegativeInteger(value.createdCount) &&
    isNonNegativeInteger(value.existingCount) &&
    isNonNegativeInteger(value.attachmentCount) &&
    isNonNegativeInteger(value.mainPlanRowVersion)
  );
}

export function isUnbindAttachmentResult(
  value: unknown,
): value is UnbindAttachmentResult {
  if (!isRecord(value) || !hasExactKeys(value, [
    "attachmentCount",
    "mainPlanRowVersion",
  ])) {
    return false;
  }
  return (
    isNonNegativeInteger(value.attachmentCount) &&
    isNonNegativeInteger(value.mainPlanRowVersion)
  );
}

