from __future__ import annotations

import hashlib
from dataclasses import dataclass
from typing import Iterable, Iterator


@dataclass(frozen=True)
class TextChunk:
    index: int
    text: str
    hash: str
    start_line: int | None = None
    end_line: int | None = None


def _hash_text(text: str) -> str:
    return hashlib.sha256(text.encode("utf-8")).hexdigest()


def chunk_text(text: str, chunk_size: int, overlap: int) -> list[TextChunk]:
    tokens = text.split()
    if not tokens:
        return []

    chunks: list[TextChunk] = []
    start = 0
    index = 0
    while start < len(tokens):
        end = min(start + chunk_size, len(tokens))
        chunk_text_value = " ".join(tokens[start:end])
        chunks.append(
            TextChunk(
                index=index,
                text=chunk_text_value,
                hash=_hash_text(chunk_text_value),
                start_line=None,
                end_line=None,
            )
        )
        index += 1
        if end == len(tokens):
            break
        start = max(end - overlap, 0)
    return chunks


def iter_chunks(
    texts: Iterable[str], chunk_size: int, overlap: int
) -> Iterator[list[TextChunk]]:
    for text in texts:
        yield chunk_text(text, chunk_size, overlap)


def chunk_text_by_lines(
    text: str, max_lines: int, overlap_lines: int
) -> list[TextChunk]:
    """Chunk by consecutive lines; each chunk has start_line/end_line (1-based)."""
    lines = text.splitlines()
    if not lines:
        return []

    chunks: list[TextChunk] = []
    index = 0
    start = 0
    while start < len(lines):
        end = min(start + max_lines, len(lines))
        chunk_lines = lines[start:end]
        chunk_text_value = "\n".join(chunk_lines)
        chunks.append(
            TextChunk(
                index=index,
                text=chunk_text_value,
                hash=_hash_text(chunk_text_value),
                start_line=start + 1,
                end_line=end,
            )
        )
        index += 1
        if end == len(lines):
            break
        start = max(end - overlap_lines, 0)
    return chunks
