import {
  AttachmentType,
  SecurityLevel,
  type AttachmentType as AttachmentTypeCode,
  type SecurityLevel as SecurityLevelCode,
} from "../../shared/api/enums";
import type { Id } from "../../shared/api/types";
import {
  isDocumentDetail,
  type DocumentDetail,
} from "../documents/documentTypes";

interface DocumentMetadataBase {
  documentName: string;
  summary: string | null;
  securityLevel: SecurityLevelCode;
  tags: string[];
}

export interface MainDocumentCreateMetadata extends DocumentMetadataBase {
  documentType: "MAIN";
  categoryId: Id;
}

export interface SubPlanCreateMetadata extends DocumentMetadataBase {
  documentType: "SUB_PLAN";
  parentDocumentId: Id;
}

export type DocumentCreateMetadata =
  | MainDocumentCreateMetadata
  | SubPlanCreateMetadata;

export type DocumentUploadMetadata = DocumentCreateMetadata & {
  overwriteDocumentId?: Id;
  rowVersion?: number;
};

export interface AttachmentCreateMetadata {
  documentName: string;
  attachmentType: AttachmentTypeCode;
  summary: string | null;
  tags: string[];
}

export interface MainDocumentUpdateRequest {
  documentName: string;
  summary: string | null;
  categoryId: Id;
  securityLevel: SecurityLevelCode;
  tags: string[];
  rowVersion: number;
}

export interface SubPlanUpdateRequest {
  documentName: string;
  summary: string | null;
  securityLevel: SecurityLevelCode;
  tags: string[];
  rowVersion: number;
}

export type UpdateDocumentRequest = MainDocumentUpdateRequest | SubPlanUpdateRequest;

export interface UpdateAttachmentRequest {
  documentName: string;
  attachmentType: AttachmentTypeCode;
  summary: string | null;
  tags: string[];
  rowVersion: number;
}

export interface DeleteDocumentResult {
  id: Id;
  deleted: true;
}

export type BatchImportItem =
  | {
      index: number;
      originalFileName: string;
      success: true;
      document: DocumentDetail;
    }
  | {
      index: number;
      originalFileName: string;
      success: false;
      errorCode: string;
      errorMessage: string;
    };

export interface BatchImportResult {
  total: number;
  successCount: number;
  failureCount: number;
  items: BatchImportItem[];
}

export interface BatchDraft<TMetadata> {
  key: string;
  file: File;
  metadata: TMetadata;
  result?: BatchImportItem;
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return !!value && typeof value === "object" && !Array.isArray(value);
}

function isId(value: unknown): value is Id {
  return typeof value === "string" && /^[0-9]+$/.test(value);
}

function isNonNegativeInteger(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 isTags(value: unknown): value is string[] {
  return (
    Array.isArray(value) &&
    value.length <= 50 &&
    value.every((tag) => typeof tag === "string" && tag.length > 0 && tag.length <= 64)
  );
}

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

function hasOnlyKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
  return Object.keys(value).every((key) => keys.includes(key)) &&
    keys.every((key) => key in value);
}

export function isDocumentCreateMetadata(value: unknown): value is DocumentUploadMetadata {
  if (!isRecord(value)) return false;
  const commonKeys = [
    "documentName",
    "documentType",
    "summary",
    "securityLevel",
    "tags",
  ];
  const common =
    typeof value.documentName === "string" &&
    value.documentName.length > 0 &&
    isNullableString(value.summary) &&
    isEnum(SecurityLevel, value.securityLevel) &&
    isTags(value.tags);
  if (!common) return false;
  const overwriteKeys = "overwriteDocumentId" in value || "rowVersion" in value
    ? ["overwriteDocumentId", "rowVersion"]
    : [];
  if (overwriteKeys.length && (!isId(value.overwriteDocumentId) || !isNonNegativeInteger(value.rowVersion))) {
    return false;
  }
  if (value.documentType === "MAIN") {
    return hasOnlyKeys(value, [...commonKeys, "categoryId", ...overwriteKeys]) && isId(value.categoryId);
  }
  return value.documentType === "SUB_PLAN" &&
    hasOnlyKeys(value, [...commonKeys, "parentDocumentId", ...overwriteKeys]) &&
    isId(value.parentDocumentId);
}

export function isAttachmentCreateMetadata(value: unknown): value is AttachmentCreateMetadata {
  return (
    isRecord(value) &&
    hasOnlyKeys(value, ["documentName", "attachmentType", "summary", "tags"]) &&
    typeof value.documentName === "string" &&
    value.documentName.length > 0 &&
    isEnum(AttachmentType, value.attachmentType) &&
    isNullableString(value.summary) &&
    isTags(value.tags)
  );
}

export function isUpdateDocumentRequest(value: unknown): value is UpdateDocumentRequest {
  if (!isRecord(value)) return false;
  const common =
    typeof value.documentName === "string" &&
    value.documentName.length > 0 &&
    isNullableString(value.summary) &&
    isEnum(SecurityLevel, value.securityLevel) &&
    isTags(value.tags) &&
    isNonNegativeInteger(value.rowVersion);
  if (!common) return false;
  const mainKeys = ["documentName", "summary", "categoryId", "securityLevel", "tags", "rowVersion"];
  const subKeys = ["documentName", "summary", "securityLevel", "tags", "rowVersion"];
  return (hasOnlyKeys(value, mainKeys) && isId(value.categoryId)) || hasOnlyKeys(value, subKeys);
}

export function isUpdateAttachmentRequest(value: unknown): value is UpdateAttachmentRequest {
  return (
    isRecord(value) &&
    hasOnlyKeys(value, ["documentName", "attachmentType", "summary", "tags", "rowVersion"]) &&
    typeof value.documentName === "string" &&
    value.documentName.length > 0 &&
    isEnum(AttachmentType, value.attachmentType) &&
    isNullableString(value.summary) &&
    isTags(value.tags) &&
    isNonNegativeInteger(value.rowVersion)
  );
}

export function isDeleteDocumentResult(value: unknown): value is DeleteDocumentResult {
  return isRecord(value) && isId(value.id) && value.deleted === true;
}

export function isBatchImportResult(value: unknown): value is BatchImportResult {
  if (
    !isRecord(value) ||
    !isNonNegativeInteger(value.total) ||
    !isNonNegativeInteger(value.successCount) ||
    !isNonNegativeInteger(value.failureCount) ||
    !Array.isArray(value.items) ||
    value.total !== value.items.length ||
    value.total !== value.successCount + value.failureCount
  ) return false;

  let successes = 0;
  for (let position = 0; position < value.items.length; position += 1) {
    const item = value.items[position];
    if (
      !isRecord(item) ||
      item.index !== position ||
      typeof item.originalFileName !== "string" ||
      typeof item.success !== "boolean"
    ) return false;
    if (item.success) {
      if (!isDocumentDetail(item.document)) return false;
      successes += 1;
    } else if (
      typeof item.errorCode !== "string" ||
      typeof item.errorMessage !== "string"
    ) return false;
  }
  return successes === value.successCount;
}

export function parseTags(value: string): string[] {
  return value
    .split(/[,，]/)
    .map((tag) => tag.trim())
    .filter(Boolean);
}
