from __future__ import annotations


def words_from_segments(segments) -> list[dict]:
    words: list[dict] = []
    for segment in segments:
        items = getattr(segment, "words", None) or []
        if not items:
            text = (getattr(segment, "text", "") or "").strip()
            if text:
                words.append(
                    {
                        "start": float(getattr(segment, "start", 0.0)),
                        "end": float(getattr(segment, "end", 0.0)),
                        "text": text,
                        "probability": 0.0,
                    }
                )
            continue
        for word in items:
            text = (getattr(word, "word", "") or "").strip()
            if not text:
                continue
            words.append(
                {
                    "start": float(word.start),
                    "end": float(word.end),
                    "text": text,
                    "probability": float(getattr(word, "probability", 0.0) or 0.0),
                }
            )
    return words


def chunks_from_segments(segments, pause: float = 0.7) -> list[dict]:
    chunks: list[dict] = []
    for segment in segments:
        words = getattr(segment, "words", None) or []
        if not words:
            text = (getattr(segment, "text", "") or "").strip()
            if text:
                chunks.append(
                    {
                        "start": float(getattr(segment, "start", 0.0)),
                        "end": float(getattr(segment, "end", 0.0)),
                        "text": text,
                    }
                )
            continue
        group = [words[0]]

        def flush(items) -> None:
            text = "".join(getattr(word, "word", "") for word in items).strip()
            if not text:
                return
            chunks.append(
                {
                    "start": float(items[0].start),
                    "end": float(items[-1].end),
                    "text": text,
                }
            )

        for word in words[1:]:
            gap = float(word.start) - float(group[-1].end)
            if gap >= pause:
                flush(group)
                group = [word]
            else:
                group.append(word)
        flush(group)
    return chunks


def drop_prompt(chunks: list[dict], prompt: str | None) -> list[dict]:
    if not chunks or not prompt:
        return chunks
    target = " ".join(prompt.split()).strip().rstrip("।.")
    built = ""
    for index, chunk in enumerate(chunks[:24]):
        built = f"{built} {str(chunk.get('text') or '')}".strip().rstrip("।.")
        if built == target:
            return chunks[index + 1 :]
        if built.startswith(f"{target} "):
            extra = built[len(target) :].strip()
            rest = chunks[index + 1 :]
            if extra:
                rest = [{**chunk, "text": extra}] + rest
            return rest
        if len(built) > len(target) + 12:
            break
    return chunks


def merge_turns(chunks: list[dict]) -> list[dict]:
    merged: list[dict] = []
    for chunk in chunks:
        text = " ".join(str(chunk.get("text") or "").split())
        if not text:
            continue
        speaker = chunk.get("speaker") or "Guest 1"
        if merged and merged[-1]["speaker"] == speaker:
            merged[-1]["text"] = f"{merged[-1]['text']} {text}".strip()
            merged[-1]["end"] = chunk.get("end", merged[-1]["end"])
            continue
        merged.append(
            {
                "speaker": speaker,
                "text": text,
                "start": float(chunk.get("start") or 0),
                "end": float(chunk.get("end") or 0),
            }
        )
    return merged


def project_speakers(spoken: list[dict], other: list[dict]) -> list[dict]:
    projected = []
    for chunk in other:
        midpoint = (float(chunk["start"]) + float(chunk["end"])) / 2
        speaker = "Guest 1"
        nearest = None
        nearest_distance = 1e9
        for turn in spoken:
            if float(turn["start"]) <= midpoint <= float(turn["end"]):
                speaker = turn["speaker"]
                nearest = None
                break
            distance = min(abs(float(turn["start"]) - midpoint), abs(float(turn["end"]) - midpoint))
            if distance < nearest_distance:
                nearest_distance = distance
                nearest = turn["speaker"]
        else:
            if nearest:
                speaker = nearest
        projected.append({**chunk, "speaker": speaker})
    return merge_turns(projected)


def render(turns: list[dict]) -> str:
    blocks = []
    for turn in turns:
        text = str(turn.get("text") or "").strip()
        if not text:
            continue
        blocks.append(f"{turn.get('speaker') or 'Guest 1'}\n{text}")
    if not blocks:
        return ""
    return "\n\n".join(blocks) + "\n"
