import {
  AllowedAction,
  AttachmentType,
  DocumentStatus,
  DocumentType,
  SecurityLevel,
  VisibilityType,
  type AttachmentType as AttachmentTypeCode,
  type DocumentStatus as DocumentStatusCode,
  type DocumentType as DocumentTypeCode,
  type SecurityLevel as SecurityLevelCode,
  type VisibilityType as VisibilityTypeCode,
} from "../../shared/api/enums";
import type { Id, PageResult } from "../../shared/api/types";
import type { DocumentSummary } from "../documents/documentTypes";

export interface DeletedBySnapshot {
  userId: Id | null;
  username: string | null;
  realName: string | null;
  organizationName: string | null;
}

export interface RecycleBinDocumentSummary {
  id: Id;
  documentName: string;
  documentType: DocumentTypeCode;
  originalFileName: string;
  categoryId: Id | null;
  categoryName: string | null;
  parentDocumentId: Id | null;
  securityLevel: SecurityLevelCode;
  documentStatus: DocumentStatusCode;
  deletedAt: string;
  updatedBy: Id | null;
  updatedByName: string | null;
  deletedBy: DeletedBySnapshot | null;
  rowVersion: number;
}

export interface RecycleBinQuery {
  keyword?: string;
  documentType?: DocumentTypeCode;
  categoryId?: Id;
  deletedFrom?: string;
  deletedTo?: string;
  page?: number;
  pageSize?: number;
  sortField?: "deletedAt" | "documentName" | "documentType" | "updatedAt";
  sortDirection?: "asc" | "desc";
}

export interface RestoreDocumentRequest {
  rowVersion: number;
}

export type RestoredDocument = Omit<
  DocumentSummary,
  "mountedPlanCount"
> & {
  originalFileName: string;
  mimeType: string | null;
  fileSize: number;
  permissionSummary: {
    organizationCount: number;
    userCount: number;
    inheritedFromMainPlan: boolean;
  };
  mountedPlanCount?: number;
};

export interface RestoreDocumentResult {
  document: RestoredDocument;
  restoredPermissionCount: number;
  skippedPermissionCount: number;
  restoredBindingCount: number;
  skippedBindingCount: number;
}

type RecordValue = Record<string, unknown>;
const ID_PATTERN = /^[0-9]+$/;
const UTC_PATTERN =
  /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;

const SUMMARY_KEYS = [
  "id",
  "documentName",
  "documentType",
  "originalFileName",
  "categoryId",
  "categoryName",
  "parentDocumentId",
  "securityLevel",
  "documentStatus",
  "deletedAt",
  "updatedBy",
  "updatedByName",
  "deletedBy",
  "rowVersion",
] as const;

const RESTORED_DOCUMENT_KEYS = [
  "id",
  "documentName",
  "summary",
  "documentType",
  "status",
  "securityLevel",
  "visibilityType",
  "visibilitySummary",
  "categoryId",
  "categoryName",
  "categoryPath",
  "parentDocumentId",
  "rootDocumentId",
  "attachmentType",
  "fileExtension",
  "tags",
  "childCount",
  "attachmentCount",
  "viewCount",
  "downloadCount",
  "createdByName",
  "createdAt",
  "updatedAt",
  "rowVersion",
  "allowedActions",
  "originalFileName",
  "mimeType",
  "fileSize",
  "permissionSummary",
] as const;

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 isId(value: unknown): value is Id {
  return typeof value === "string" && ID_PATTERN.test(value);
}

function isNullableString(value: unknown): value is string | null {
  return value === null || typeof value === "string";
}

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

function isEnum<T extends Record<string, string>>(
  values: T,
  value: unknown,
): boolean {
  return (
    typeof value === "string" &&
    (Object.values(values) as string[]).includes(value)
  );
}

export function isUtcDateTime(value: unknown): value is string {
  return typeof value === "string" && UTC_PATTERN.test(value);
}

export function isDeletedBySnapshot(
  value: unknown,
): value is DeletedBySnapshot {
  return (
    isRecord(value) &&
    hasExactKeys(value, [
      "userId",
      "username",
      "realName",
      "organizationName",
    ]) &&
    (value.userId === null || isId(value.userId)) &&
    isNullableString(value.username) &&
    isNullableString(value.realName) &&
    isNullableString(value.organizationName)
  );
}

export function isRecycleBinDocumentSummary(
  value: unknown,
): value is RecycleBinDocumentSummary {
  if (!isRecord(value) || !hasExactKeys(value, SUMMARY_KEYS)) return false;
  return (
    isId(value.id) &&
    typeof value.documentName === "string" &&
    isEnum(DocumentType, value.documentType) &&
    typeof value.originalFileName === "string" &&
    (value.categoryId === null || isId(value.categoryId)) &&
    isNullableString(value.categoryName) &&
    (value.parentDocumentId === null || isId(value.parentDocumentId)) &&
    isEnum(SecurityLevel, value.securityLevel) &&
    isEnum(DocumentStatus, value.documentStatus) &&
    isUtcDateTime(value.deletedAt) &&
    (value.updatedBy === null || isId(value.updatedBy)) &&
    isNullableString(value.updatedByName) &&
    (value.deletedBy === null || isDeletedBySnapshot(value.deletedBy)) &&
    isCount(value.rowVersion)
  );
}

export function isRecycleBinPage(
  value: unknown,
): value is PageResult<RecycleBinDocumentSummary> {
  if (
    !isRecord(value) ||
    !hasExactKeys(value, [
      "items",
      "page",
      "pageSize",
      "total",
      "totalPages",
    ])
  ) {
    return false;
  }
  return (
    Array.isArray(value.items) &&
    value.items.every(isRecycleBinDocumentSummary) &&
    isCount(value.page) &&
    value.page >= 1 &&
    isCount(value.pageSize) &&
    value.pageSize >= 1 &&
    value.pageSize <= 100 &&
    isCount(value.total) &&
    isCount(value.totalPages)
  );
}

export function isRestoreDocumentRequest(
  value: unknown,
): value is RestoreDocumentRequest {
  return (
    isRecord(value) &&
    hasExactKeys(value, ["rowVersion"]) &&
    isCount(value.rowVersion)
  );
}

export function isRestoredDocument(
  value: unknown,
): value is RestoredDocument {
  if (!isRecord(value)) return false;
  const keys = "mountedPlanCount" in value
    ? [...RESTORED_DOCUMENT_KEYS, "mountedPlanCount"]
    : RESTORED_DOCUMENT_KEYS;
  if (!hasExactKeys(value, keys)) return false;
  const permission = value.permissionSummary;
  return (
    isId(value.id) &&
    typeof value.documentName === "string" &&
    isNullableString(value.summary) &&
    isEnum(DocumentType, value.documentType) &&
    isEnum(DocumentStatus, value.status) &&
    isEnum(SecurityLevel, value.securityLevel) &&
    isEnum(VisibilityType, value.visibilityType) &&
    typeof value.visibilitySummary === "string" &&
    (value.categoryId === null || isId(value.categoryId)) &&
    isNullableString(value.categoryName) &&
    isNullableString(value.categoryPath) &&
    (value.parentDocumentId === null || isId(value.parentDocumentId)) &&
    (value.rootDocumentId === null || isId(value.rootDocumentId)) &&
    (value.attachmentType === null ||
      isEnum(AttachmentType, value.attachmentType)) &&
    typeof value.fileExtension === "string" &&
    Array.isArray(value.tags) &&
    value.tags.every((tag) => typeof tag === "string") &&
    isCount(value.childCount) &&
    isCount(value.attachmentCount) &&
    isCount(value.viewCount) &&
    isCount(value.downloadCount) &&
    isNullableString(value.createdByName) &&
    isUtcDateTime(value.createdAt) &&
    isUtcDateTime(value.updatedAt) &&
    isCount(value.rowVersion) &&
    Array.isArray(value.allowedActions) &&
    value.allowedActions.every((action) => isEnum(AllowedAction, action)) &&
    typeof value.originalFileName === "string" &&
    isNullableString(value.mimeType) &&
    isCount(value.fileSize) &&
    isRecord(permission) &&
    hasExactKeys(permission, [
      "organizationCount",
      "userCount",
      "inheritedFromMainPlan",
    ]) &&
    isCount(permission.organizationCount) &&
    isCount(permission.userCount) &&
    typeof permission.inheritedFromMainPlan === "boolean" &&
    (!("mountedPlanCount" in value) || isCount(value.mountedPlanCount))
  );
}

export function isRestoreDocumentResult(
  value: unknown,
): value is RestoreDocumentResult {
  return (
    isRecord(value) &&
    hasExactKeys(value, [
      "document",
      "restoredPermissionCount",
      "skippedPermissionCount",
      "restoredBindingCount",
      "skippedBindingCount",
    ]) &&
    isRestoredDocument(value.document) &&
    isCount(value.restoredPermissionCount) &&
    isCount(value.skippedPermissionCount) &&
    isCount(value.restoredBindingCount) &&
    isCount(value.skippedBindingCount)
  );
}

