import { readFile } from 'fs/promises';
import { PDFParse } from 'pdf-parse';
import { extractRawText } from 'mammoth';

// 문서 1건당 추출 텍스트 상한(자) — DB 용량과 AI 토큰 사용량을 함께 보호한다.
export const PER_DOC_TEXT_LIMIT = 8000;

// 문서의 AI 반영 상태 — 목록 배지 표기에 사용
// ready: 추출 성공(반영 가능) / failed: 지원 형식이나 추출 실패 / unsupported: 미지원 형식
export type AttachmentAiStatus = 'ready' | 'failed' | 'unsupported';

// 텍스트 추출 대상 형식인지 여부(txt·csv·pdf·docx). 미지원/추출실패 배지 구분에 사용한다.
export function isExtractableType(mime: string, name: string): boolean {
  const lower = name.toLowerCase();
  return (
    mime === 'text/plain' ||
    mime === 'text/csv' ||
    lower.endsWith('.txt') ||
    lower.endsWith('.csv') ||
    mime === 'application/pdf' ||
    lower.endsWith('.pdf') ||
    mime ===
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
    lower.endsWith('.docx')
  );
}

// 첨부 문서에서 평문 텍스트를 추출한다(업로드 시 1회). 추출 불가 형식/실패/빈 결과는 null.
// 지원 형식: txt·csv(직접 읽기), pdf(pdf-parse), docx(mammoth).
// 그 외(이미지·xlsx·ppt·압축 등)는 추출하지 않고 null 을 반환한다.
export async function extractAttachmentText(
  absPath: string,
  mime: string,
  name: string,
): Promise<string | null> {
  const lower = name.toLowerCase();
  try {
    // 1) 평문 텍스트 — 라이브러리 없이 그대로 읽는다
    if (
      mime === 'text/plain' ||
      mime === 'text/csv' ||
      lower.endsWith('.txt') ||
      lower.endsWith('.csv')
    ) {
      const buf = await readFile(absPath);
      return finalize(buf.toString('utf8'));
    }

    // 2) PDF — pdf-parse v2(PDFParse 클래스)로 페이지 텍스트 추출
    if (mime === 'application/pdf' || lower.endsWith('.pdf')) {
      const buf = await readFile(absPath);
      const parser = new PDFParse({ data: buf });
      try {
        const result = await parser.getText();
        return finalize(result.text);
      } finally {
        await parser.destroy();
      }
    }

    // 3) Word(.docx) — mammoth 로 서식 제거한 본문만 추출
    if (
      mime ===
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
      lower.endsWith('.docx')
    ) {
      const result = await extractRawText({ path: absPath });
      return finalize(result.value);
    }

    return null;
  } catch {
    // 추출 실패는 무시한다 — 첨부 자체는 정상 저장되어야 한다.
    return null;
  }
}

// 공백·개행 정리 후 길이 상한 적용. 내용이 없으면 null.
function finalize(text: string): string | null {
  const cleaned = text
    .replace(/\r\n/g, '\n')
    .replace(/[ \t]+\n/g, '\n')
    .replace(/\n{3,}/g, '\n\n')
    .trim();
  if (!cleaned) return null;
  return cleaned.length > PER_DOC_TEXT_LIMIT
    ? cleaned.slice(0, PER_DOC_TEXT_LIMIT)
    : cleaned;
}
