"""Тесты чистой функции реконструкции фраз `build_phrases` (ТЗ §1.3).

Синтетические таймлайны без БД/сети — алгоритм тестируется в изоляции (TDD).
"""

import uuid

from core.plugins.transcriber import Segment
from workers.transcription.phrases import (
    INTERJECTION_THRESHOLD_S,
    PhraseDraft,
    build_phrases,
)

ALICE = uuid.uuid4()
BOB = uuid.uuid4()


def test_single_speaker_multiple_segments_merge_into_one_phrase() -> None:
    """Несколько сегментов одного спикера → одна фраза с границами min start / max end."""
    segments = {ALICE: [Segment(0.0, 2.0, "a"), Segment(3.0, 5.0, "b")]}
    offsets = {ALICE: 0.0}

    result = build_phrases(segments, offsets)

    assert result == [PhraseDraft(participant_id=ALICE, start=0.0, end=5.0, text="a b")]


def test_alice_bob_alice_example_from_spec() -> None:
    """Пример A–B–A из §1.3: три фразы в порядке t_start."""
    segments = {
        ALICE: [Segment(0.0, 3.0, "alice1"), Segment(6.0, 9.0, "alice2")],
        BOB: [Segment(3.0, 6.0, "bob1")],
    }
    offsets = {ALICE: 0.0, BOB: 0.0}

    result = build_phrases(segments, offsets)

    assert result == [
        PhraseDraft(participant_id=ALICE, start=0.0, end=3.0, text="alice1"),
        PhraseDraft(participant_id=BOB, start=3.0, end=6.0, text="bob1"),
        PhraseDraft(participant_id=ALICE, start=6.0, end=9.0, text="alice2"),
    ]


def test_long_overlap_keeps_both_phrases_with_intersecting_intervals() -> None:
    """Перекрытие речи ≥ порога → сохраняются обе фразы с пересекающимися интервалами."""
    segments = {
        ALICE: [Segment(0.0, 10.0, "alice-long")],
        BOB: [Segment(3.0, 8.0, "bob-overlap")],
    }
    offsets = {ALICE: 0.0, BOB: 0.0}
    assert 8.0 - 3.0 >= INTERJECTION_THRESHOLD_S

    result = build_phrases(segments, offsets)

    assert result == [
        PhraseDraft(participant_id=ALICE, start=0.0, end=10.0, text="alice-long"),
        PhraseDraft(participant_id=BOB, start=3.0, end=8.0, text="bob-overlap"),
    ]
    # интервалы действительно пересекаются
    alice_phrase, bob_phrase = result
    assert bob_phrase.start < alice_phrase.end
    assert bob_phrase.end > alice_phrase.start


def test_short_interjection_does_not_break_monologue_but_is_kept_separately() -> None:
    """Короткое «угу» (<1.5с) внутри чужого монолога: монолог одной фразой, вставка — отдельной."""
    segments = {
        ALICE: [Segment(0.0, 2.0, "one"), Segment(2.5, 5.0, "two")],
        BOB: [Segment(2.1, 2.3, "угу")],
    }
    offsets = {ALICE: 0.0, BOB: 0.0}
    assert 2.3 - 2.1 < INTERJECTION_THRESHOLD_S

    result = build_phrases(segments, offsets)

    assert result == [
        PhraseDraft(participant_id=ALICE, start=0.0, end=5.0, text="one two"),
        PhraseDraft(participant_id=BOB, start=2.1, end=2.3, text="угу"),
    ]


def test_silence_gap_does_not_break_single_speaker_phrase() -> None:
    """Тишина (большой зазор) между сегментами одного спикера без чужой речи фразу не рвёт."""
    segments = {ALICE: [Segment(0.0, 2.0, "a"), Segment(20.0, 22.0, "b")]}
    offsets = {ALICE: 0.0}

    result = build_phrases(segments, offsets)

    assert result == [PhraseDraft(participant_id=ALICE, start=0.0, end=22.0, text="a b")]


def test_empty_input_returns_empty_list() -> None:
    """Пустой вход → пустой список фраз."""
    assert build_phrases({}, {}) == []


def test_participant_with_no_segments_contributes_nothing() -> None:
    """Участник без сегментов не создаёт фраз, но не ломает построение остальных."""
    segments = {ALICE: [Segment(0.0, 2.0, "a")], BOB: []}
    offsets = {ALICE: 0.0, BOB: 0.0}

    result = build_phrases(segments, offsets)

    assert result == [PhraseDraft(participant_id=ALICE, start=0.0, end=2.0, text="a")]


def test_track_offsets_shift_timeline() -> None:
    """Смещения треков (`track_offsets`) корректно сдвигают таймлайн сегментов."""
    segments = {
        ALICE: [Segment(0.0, 2.0, "alice-early-on-track")],
        BOB: [Segment(0.0, 2.0, "bob-early-on-track")],
    }
    # трек Боба стартовал позже (offset больше) → на общем таймлайне сеанса
    # его сегмент оказывается ПОСЛЕ сегмента Алисы, хотя на своих треках
    # оба сегмента начинались с нуля.
    offsets = {ALICE: 10.0, BOB: 20.0}

    result = build_phrases(segments, offsets)

    assert result == [
        PhraseDraft(participant_id=ALICE, start=10.0, end=12.0, text="alice-early-on-track"),
        PhraseDraft(participant_id=BOB, start=20.0, end=22.0, text="bob-early-on-track"),
    ]
