package chunk
import (
"archive/zip"
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"os"
"path"
"regexp"
"strings"
"unicode"
"unicode/utf8"
"textmachine/backend/internal/lang"
"textmachine/backend/internal/text"
"golang.org/x/net/html"
"golang.org/x/text/encoding/simplifiedchinese"
xunicode "golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
// ingest.go: the step-3a import layer. Ingest turns a source file (txt or epub)
// into an ordered list of per-chapter NORMALIZED text plus the ruby/furigana
// readings captured on the way (04-unhappy §4 / D9). It is the ONLY place that
// reads the source; the runner then feeds doc.Chapters to SplitChunks and persists
// doc.Ruby (seeding.go). It is offline and deterministic — no LLM, no time/rand — so
// the whole path is $0 and reproducible.
//
// epub is a text extraction (02-mvp:25): read the chapters the book's own table of contents declares —
// nav (EPUB 3) or NCX (EPUB 2), falling back to the spine only when neither resolves — strip tags, drop
// inline markup/styling. The ONE thing we do NOT drop is ruby:
// base carries an author reading of a name/term that a
// plain text export would silently lose (the documented hole, 04-unhappy §4). We
// keep the BASE in the body and hand the READING to the caller for a glossary lock
// (memory v2, step 4) — never injecting it into a prompt here (§7d).
// chapterSep is the ASCII form feed (U+000C), the "page/section break". It is a SEPARATOR the file
// carries, NOT a statement that a chapter begins — see the Structure constants. It is invisible in prose
// and untouched by text.NormalizeSource, and it draws the boundaries only when no header line does
// (cutTXT). A txt with neither is a single chapter. epub has its own table of contents and never uses this.
const chapterSep = "\f"
// RubyReading is one captured (base, reading) pair and the chapter it appeared in.
// The pipeline aggregates these (min chapter, count) before persisting (seeding.go).
type RubyReading struct {
Base string // the ruby body: the kanji/base surface form
Reading string // the