namespace Chapters {
    export type Hash = string;

    /** Persisted per-story, maps content hash -> cached summary */
    export interface Chapter {
        header: string;
        summaryCache: Record<Hash, string>;
    }

    /** Transient result of parsing the story text */
    export interface ParsedChapter {
        header: string; // empty for preamble before first header
        body: string;
    }

    export function hashChunk(text: string): Hash {
        let h = 5381;
        for (let i = 0; i < text.length; i++) {
            h = (h * 33) ^ text.charCodeAt(i);
        }
        return (h >>> 0).toString(36);
    }

    const CHUNK_TARGET_CHARS = 2400; // ~800 tokens

    /** Split a chapter body into paragraph-grouped chunks for summarization */
    export function splitIntoChunks(body: string): string[] {
        const paragraphs = body.split(/\n{2,}/).map(p => p.trim()).filter(Boolean);
        const chunks: string[] = [];
        let current = '';

        for (const para of paragraphs) {
            const sep = current ? '\n\n' : '';
            if (current && current.length + sep.length + para.length > CHUNK_TARGET_CHARS) {
                chunks.push(current);
                current = para;
            } else {
                current = current + sep + para;
            }
        }

        if (current) chunks.push(current);
        return chunks;
    }

    /** Split story text into chapters by top-level `# ` headers */
    export function parseText(text: string): ParsedChapter[] {
        const lines = text.split('\n');
        const result: ParsedChapter[] = [];
        let currentHeader = '';
        let currentLines: string[] = [];

        for (const line of lines) {
            if (line.startsWith('# ')) {
                if (currentHeader || currentLines.length > 0) {
                    result.push({ header: currentHeader, body: currentLines.join('\n').trim() });
                }
                currentHeader = line;
                currentLines = [];
            } else {
                currentLines.push(line);
            }
        }

        if (currentHeader || currentLines.length > 0) {
            result.push({ header: currentHeader, body: currentLines.join('\n').trim() });
        }

        return result;
    }

    /**
     * Look up a cached summary for a parsed chapter body.
     * Returns the hash (for later cache writes) and the summary if it exists.
     */
    export function lookupSummary(chapter: Chapter, body: string): { hash: Hash; summary: string | null } {
        const h = hashChunk(body);
        return { hash: h, summary: chapter.summaryCache[h] ?? null };
    }

    /** Return a new Chapter with the summary stored under the given hash */
    export function storeSummary(chapter: Chapter, hash: Hash, summary: string): Chapter {
        return { ...chapter, summaryCache: { ...chapter.summaryCache, [hash]: summary } };
    }

    /** Create an empty Chapter record for a given header */
    export function emptyChapter(header: string): Chapter {
        return { header, summaryCache: {} };
    }
}

export default Chapters;
