import {
  AllowedAction,
  AttachmentType,
  DocumentStatus,
  DocumentType,
  SecurityLevel,
  VisibilityType,
  type AllowedAction as AllowedActionCode,
  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";

export interface DocumentSummary {
  id: Id;
  documentName: string;
  summary: string | null;
  documentType: DocumentTypeCode;
  status: DocumentStatusCode;
  securityLevel: SecurityLevelCode;
  visibilityType: VisibilityTypeCode;
  visibilitySummary: string;
  categoryId: Id | null;
  categoryName: string | null;
  categoryPath: string | null;
  parentDocumentId: Id | null;
  rootDocumentId: Id | null;
  attachmentType: AttachmentTypeCode | null;
  fileExtension: string;
  tags: string[];
  childCount: number;
  attachmentCount: number;
  viewCount: number;
  downloadCount: number;
  createdByName: string | null;
  createdAt: string;
  updatedAt: string;
  rowVersion: number;
  allowedActions: AllowedActionCode[];
  mountedPlanCount?: number;
  contentSnippet?: string | null;
}

export interface DocumentDetail extends DocumentSummary {
  originalFileName: string;
  mimeType: string | null;
  fileSize: number;
  fileHash: string;
  permissionSummary: {
    organizationCount: number;
    userCount: number;
    inheritedFromMainPlan: boolean;
  };
  contentText?: string | null;
}

export interface MainPlanBrief {
  id: Id;
  documentName: string;
  categoryName: string | null;
  securityLevel: SecurityLevelCode;
}

export interface MainPlanBriefResult {
  items: MainPlanBrief[];
  total: number;
}

export interface DocumentListQuery {
  documentType?: string;
  categoryId?: Id;
  includeDescendants?: boolean;
  keyword?: string;
  tags?: string[];
  tagsMatch?: "any" | "all";
  securityLevel?: SecurityLevelCode;
  visibilityType?: VisibilityTypeCode;
  status?: DocumentStatusCode;
  updatedFrom?: string;
  updatedTo?: string;
  sortBy?: "documentName" | "createdAt" | "updatedAt" | "viewCount" | "downloadCount";
  sortDirection?: "asc" | "desc";
  page?: number;
  pageSize?: number;
}

export interface SubPlanListQuery {
  keyword?: string;
  status?: DocumentStatusCode;
  updatedFrom?: string;
  updatedTo?: string;
  sortBy?: "documentName" | "createdAt" | "updatedAt" | "viewCount" | "downloadCount";
  sortDirection?: "asc" | "desc";
  page?: number;
  pageSize?: number;
}

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

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

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

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

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

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

function hasOnlyAllowedActions(value: unknown): value is AllowedActionCode[] {
  return Array.isArray(value) && value.every((item) => isEnum(AllowedAction, item));
}

export function isDocumentSummary(value: unknown): value is DocumentSummary {
  if (!isRecord(value)) return false;
  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") &&
    isInteger(value.childCount) &&
    isInteger(value.attachmentCount) &&
    isInteger(value.viewCount) &&
    isInteger(value.downloadCount) &&
    isNullableString(value.createdByName) &&
    typeof value.createdAt === "string" &&
    utcPattern.test(value.createdAt) &&
    typeof value.updatedAt === "string" &&
    utcPattern.test(value.updatedAt) &&
    isInteger(value.rowVersion) &&
    hasOnlyAllowedActions(value.allowedActions) &&
    (!("mountedPlanCount" in value) || isInteger(value.mountedPlanCount)) &&
    (!("contentSnippet" in value) || value.contentSnippet === null || value.contentSnippet === undefined || typeof value.contentSnippet === "string")
  );
}

export function isDocumentDetail(value: unknown): value is DocumentDetail {
  if (!isDocumentSummary(value) || !isRecord(value)) return false;
  const permission = value.permissionSummary;
  return (
    typeof value.originalFileName === "string" &&
    isNullableString(value.mimeType) &&
    isInteger(value.fileSize) &&
    typeof value.fileHash === "string" &&
    /^[a-fA-F0-9]{64}$/.test(value.fileHash) &&
    isRecord(permission) &&
    isInteger(permission.organizationCount) &&
    isInteger(permission.userCount) &&
    typeof permission.inheritedFromMainPlan === "boolean" &&
    (!("contentText" in value) || value.contentText === null || value.contentText === undefined || typeof value.contentText === "string")
  );
}

export function isMainPlanBrief(value: unknown): value is MainPlanBrief {
  if (!isRecord(value)) return false;
  return (
    isId(value.id) &&
    typeof value.documentName === "string" &&
    isNullableString(value.categoryName) &&
    isEnum(SecurityLevel, value.securityLevel)
  );
}

export function isPageResult<T>(
  value: unknown,
  itemGuard: (item: unknown) => item is T,
): value is PageResult<T> {
  if (!isRecord(value)) return false;
  return (
    Array.isArray(value.items) &&
    value.items.every(itemGuard) &&
    typeof value.page === "number" &&
    Number.isInteger(value.page) &&
    value.page >= 1 &&
    typeof value.pageSize === "number" &&
    Number.isInteger(value.pageSize) &&
    value.pageSize >= 1 &&
    value.pageSize <= 100 &&
    isInteger(value.total) &&
    isInteger(value.totalPages)
  );
}

export function isMainPlanBriefResult(value: unknown): value is MainPlanBriefResult {
  return (
    isRecord(value) &&
    Array.isArray(value.items) &&
    value.items.every(isMainPlanBrief) &&
    isInteger(value.total) &&
    value.total === value.items.length
  );
}
