"""People file (participant registry): read, compute, merge, write.

The people file (``people.yaml``) lives in the output directory and tracks
every participant across pipeline runs.  Computed stats are refreshed on
each run; human-editable fields (name, role, persona, notes) are preserved.
"""

from __future__ import annotations

import logging
from datetime import datetime, timezone
from pathlib import Path

import yaml

from bristlenose.models import (
    FullTranscript,
    InputSession,
    PeopleFile,
    PersonComputed,
    PersonEditable,
    PersonEntry,
    SpeakerRole,
)

logger = logging.getLogger(__name__)

PEOPLE_FILENAME = "people.yaml"

_HEADER = (
    "# people.yaml — participant registry (auto-generated by bristlenose)\n"
    "#\n"
    "# Computed fields are refreshed on each pipeline run.\n"
    "# Human-editable fields (full_name, short_name, role, persona, notes)\n"
    "# are preserved across runs — edit them freely.\n"
    "#\n"
    "# Set short_name to change how a participant appears in reports:\n"
    '#   short_name: Sarah  →  quotes render as "— Sarah" instead of "— p1"\n'
    "#\n"
    "# Note: inline YAML comments are lost when the file is rewritten.\n"
    "\n"
)


# ---------------------------------------------------------------------------
# Load
# ---------------------------------------------------------------------------


def load_people_file(output_dir: Path) -> PeopleFile | None:
    """Load an existing ``people.yaml`` from *output_dir*.

    Returns ``None`` if the file does not exist or is empty.
    """
    path = output_dir / PEOPLE_FILENAME
    if not path.exists():
        return None
    raw = yaml.safe_load(path.read_text(encoding="utf-8"))
    if raw is None:
        return None
    return PeopleFile.model_validate(raw)


# ---------------------------------------------------------------------------
# Compute
# ---------------------------------------------------------------------------


def compute_participant_stats(
    sessions: list[InputSession],
    transcripts: list[FullTranscript],
) -> dict[str, PersonComputed]:
    """Compute per-participant stats from session and transcript data.

    Returns a dict keyed by ``participant_id``.
    """
    transcript_map: dict[str, FullTranscript] = {
        t.participant_id: t for t in transcripts
    }

    stats: dict[str, PersonComputed] = {}
    total_words_all = 0

    for session in sessions:
        pid = session.participant_id
        transcript = transcript_map.get(pid)

        if transcript is None:
            stats[pid] = PersonComputed(
                participant_id=pid,
                session_date=session.session_date,
                duration_seconds=0.0,
                words_spoken=0,
                pct_words=0.0,
                pct_time_speaking=0.0,
                source_file=session.files[0].path.name if session.files else "",
            )
            continue

        # Count words and speaking time for PARTICIPANT-role segments
        words_spoken = 0
        speaking_seconds = 0.0
        for seg in transcript.segments:
            if seg.speaker_role == SpeakerRole.PARTICIPANT:
                words_spoken += len(seg.text.split())
                speaking_seconds += max(0.0, seg.end_time - seg.start_time)

        total_words_all += words_spoken

        stats[pid] = PersonComputed(
            participant_id=pid,
            session_date=transcript.session_date,
            duration_seconds=transcript.duration_seconds,
            words_spoken=words_spoken,
            pct_words=0.0,  # filled in second pass
            pct_time_speaking=(
                round(speaking_seconds / transcript.duration_seconds * 100, 1)
                if transcript.duration_seconds > 0
                else 0.0
            ),
            source_file=transcript.source_file,
        )

    # Second pass: compute pct_words relative to total across all participants.
    if total_words_all > 0:
        for computed in stats.values():
            computed.pct_words = round(
                computed.words_spoken / total_words_all * 100, 1
            )

    return stats


# ---------------------------------------------------------------------------
# Merge
# ---------------------------------------------------------------------------


def merge_people(
    existing: PeopleFile | None,
    computed: dict[str, PersonComputed],
) -> PeopleFile:
    """Merge new computed stats with existing human-edited fields.

    Strategy:

    * **Computed fields** are always overwritten with fresh data.
    * **Editable fields** are always preserved from the previous file.
    * New participants are added with empty editable defaults.
    * Participants present in the old file but absent from the current run
      are **kept** (the user may still want their notes).
    """
    people = PeopleFile(last_updated=datetime.now(tz=timezone.utc))

    # Start with existing entries to preserve removed-but-noted participants.
    if existing:
        for pid, entry in existing.participants.items():
            people.participants[pid] = entry

    # Update / add computed stats.
    for pid, comp in computed.items():
        if pid in people.participants:
            # Preserve editable, replace computed.
            people.participants[pid].computed = comp
        else:
            people.participants[pid] = PersonEntry(
                computed=comp, editable=PersonEditable()
            )

    return people


# ---------------------------------------------------------------------------
# Write
# ---------------------------------------------------------------------------


def write_people_file(people: PeopleFile, output_dir: Path) -> Path:
    """Write ``people.yaml`` to *output_dir*.

    Uses block-style YAML with a human-readable comment header.
    """
    path = output_dir / PEOPLE_FILENAME
    output_dir.mkdir(parents=True, exist_ok=True)

    data = people.model_dump(mode="json")

    yaml_content = yaml.dump(
        data,
        default_flow_style=False,
        sort_keys=False,
        allow_unicode=True,
    )

    path.write_text(_HEADER + yaml_content, encoding="utf-8")
    logger.info("Wrote people file: %s", path)
    return path


# ---------------------------------------------------------------------------
# Display name helper
# ---------------------------------------------------------------------------


def build_display_name_map(people: PeopleFile) -> dict[str, str]:
    """Build a mapping from ``participant_id`` → display name.

    Returns ``short_name`` if set, otherwise ``participant_id``.
    """
    names: dict[str, str] = {}
    for pid, entry in people.participants.items():
        if entry.editable.short_name:
            names[pid] = entry.editable.short_name
        else:
            names[pid] = pid
    return names
