"""VP equip — build character_block for Chat pipeline."""

from __future__ import annotations

from typing import TYPE_CHECKING

from niannian.skills.context import SkillInjectContext
from niannian.skills.injector import inject_skills
from niannian.virtual_persona.assembly import build_character_assembly_block
from niannian.virtual_persona.assembly_master import read_assembly_master
from niannian.virtual_persona.pack import read_equipped_skills

if TYPE_CHECKING:
    from niannian.skills.loader import SkillLoader
    from niannian.virtual_persona.store import VirtualPersonaStore

DEFAULT_VP_CHARACTER_MAX_CHARS = 4000


def resolve_vp_character_max_chars(chat_cfg: dict | None = None) -> int:
    raw = (chat_cfg or {}).get("max_vp_character_chars", DEFAULT_VP_CHARACTER_MAX_CHARS)
    try:
        return max(256, int(raw))
    except (TypeError, ValueError):
        return DEFAULT_VP_CHARACTER_MAX_CHARS


def build_equipped_vp_character_block(
    store: VirtualPersonaStore,
    vp_id: str,
    *,
    max_chars: int = DEFAULT_VP_CHARACTER_MAX_CHARS,
) -> tuple[str, list[str]]:
    """Load VP character_block; prefer assembly_master.md over profile.md."""
    assembly_md = read_assembly_master(store, vp_id).strip()
    trace: list[str] = []

    if assembly_md:
        source_md = assembly_md
        trace.append("vp character assembly_master")
    else:
        row = store.get(vp_id)
        source_md = str(row.get("profile_md") or "").strip()
        if not source_md:
            return "", trace
        trace.append("vp character profile fallback")

    block = build_character_assembly_block(source_md)
    if len(block) > max_chars:
        block = block[: max(1, max_chars - 3)].rstrip() + "..."
        trace.append(f"vp character truncated {max_chars}")
    return block, trace


def _vp_persona_skill_names(loader: SkillLoader, equipped: list[str]) -> list[str]:
    names: list[str] = []
    for name in equipped:
        skill = loader.get(name)
        if skill is None or not skill.enabled:
            continue
        if skill.kind != "persona" or skill.scope != "vp":
            continue
        names.append(name)
    return names


def append_vp_persona_skills(
    character_block: str,
    store: VirtualPersonaStore,
    vp_id: str,
    loader: SkillLoader,
    *,
    max_total_chars: int = DEFAULT_VP_CHARACTER_MAX_CHARS,
) -> tuple[str, list[str]]:
    """Append pack.equipped_skills persona/vp skills after VP character_block."""
    equipped = read_equipped_skills(store, vp_id)
    if not equipped:
        return character_block, []

    persona_names = _vp_persona_skill_names(loader, equipped)
    if not persona_names:
        return character_block, []

    remaining = max(0, max_total_chars - len(character_block))
    if remaining <= 0:
        return character_block, ["vp persona skills skipped (char budget)"]

    skill_block = inject_skills(
        loader,
        "chat",
        max_chars=remaining,
        names=persona_names,
        context=SkillInjectContext(vp_id=vp_id, equipped_skill_names=persona_names),
    )
    if not skill_block.strip():
        return character_block, []

    trace = [f"vp persona skills {len(persona_names)}"]
    if character_block.strip():
        combined = f"{character_block.rstrip()}\n\n{skill_block.strip()}"
    else:
        combined = skill_block.strip()
    return combined, trace
