"""읽은 값을 고정 Markdown 줄로 바꾼다.

이 층은 파일을 열지 않는다 — 이미 읽힌 매핑을 받아 문자열만 만든다. 그래서
참조 해석과 독립이고, 서식을 고칠 때 경로 규칙을 다시 읽을 필요가 없다.
`_provider_models` 만 예외적으로 거절하지만, 그것도 디스크가 아니라 받은
매핑 안의 모순을 보는 것이다.
"""
from __future__ import annotations

from pathlib import Path
from typing import Any, Mapping

from ..fixed_text import line as _line, scalar as _value
from ..json_boundary import JsonBoundaryError


def _mapping(payload: Mapping[str, Any], key: str) -> Mapping[str, Any]:
    value = payload.get(key)
    return value if isinstance(value, Mapping) else {}


def _project_policy_lines(project: Mapping[str, Any]) -> str:
    packs = project.get("reviewRulePacks")
    pack_lines = (
        [
            _line("Review rule pack", path)
            for path in packs
            if isinstance(path, str)
        ]
        if isinstance(packs, list)
        else []
    )
    qa_lines: list[str] = []
    qa_commands = project.get("qaCommands")
    if isinstance(qa_commands, Mapping):
        for category, entries in sorted(qa_commands.items()):
            if not isinstance(entries, list):
                continue
            for entry in entries:
                if not isinstance(entry, Mapping):
                    continue
                qa_lines.append(
                    f"- QA `{_value(category)}` / `{_value(entry.get('label'))}`: "
                    f"`{_value(entry.get('cmd'))}`\n"
                )
    return (
        "\n## Project Review Rule Packs\n\n"
        + ("".join(pack_lines) or "- None\n")
        + "\n## Project QA Commands\n\n"
        + ("".join(qa_lines) or "- None\n")
    )


def _assignment_line(prefix: str, assignment: Mapping[str, Any], identifier: object = None) -> str:
    label = prefix if identifier is None else f"{prefix} `{_value(identifier)}`"
    return (
        f"- {label}: role `{_value(assignment.get('role'))}`; "
        f"provider `{_value(assignment.get('provider'))}`; "
        f"model `{_value(assignment.get('model'))}`\n"
    )


def _worker_assignment_lines(run: Mapping[str, Any]) -> str:
    assignments = run.get("workerAssignments")
    if not isinstance(assignments, list):
        return "- None\n"
    lines = [
        _assignment_line("Worker", assignment, assignment.get("workerId"))
        for assignment in assignments
        if isinstance(assignment, Mapping)
    ]
    return "".join(lines) or "- None\n"


def _worker_prompt_lines(run: Mapping[str, Any]) -> str:
    paths = run.get("workerPromptPathByWorkerId")
    if not isinstance(paths, Mapping):
        return "- None\n"
    lines = [
        f"- Worker prompt `{_value(worker_id)}`: `{_value(path)}`\n"
        for worker_id, path in sorted(paths.items(), key=lambda item: _value(item[0]))
    ]
    return "".join(lines) or "- None\n"


def _next_phase_lines(workflow: Mapping[str, Any]) -> str:
    pointer = _mapping(workflow, "nextRecommendedPhase")
    return (
        _line("Next phase", pointer.get("phase"))
        + _line("Next phase status", pointer.get("status"))
        + _line("Next phase rationale", pointer.get("rationale"))
    )


def _status_overview_text(rows: list[dict[str, object]]) -> str:
    blocks = [_line("Task count", len(rows))]
    for row in rows:
        blocks.extend((
            "\n## Task\n\n",
            _line("Task key", row.get("taskKey")),
            _line("Task group", row.get("taskGroup")),
            _line("Task type", row.get("taskType")),
            _line("Latest run status", row.get("latestRunStatus")),
            _line("Updated at", row.get("updatedAt")),
            _line("Latest run manifest", row.get("latestRunManifestPath")),
            _line("Work category", row.get("workCategory")),
            _line("Current status", row.get("currentStatus")),
            _line("Current phase", row.get("currentPhase")),
            _line("Current phase state", row.get("currentPhaseState")),
            _line("Next phase", row.get("nextPhase")),
            _line("Next phase status", row.get("nextPhaseStatus")),
            _line("Next phase rationale", row.get("nextPhaseRationale")),
            _line("Awaiting approval", row.get("awaitingApproval")),
            _line("Latest report", row.get("latestReportRecordPath")),
            _line("Latest resume command", row.get("latestResumeCommandPath")),
            _line("Work status", row.get("workStatus")),
            _line("Direct work record", row.get("latestWorkRecordPath")),
        ))
    return "# Okstra Status Input\n\n" + "".join(blocks)


def _history_overview_text(rows: list[dict[str, object]], limit: int) -> str:
    shown = rows[:limit]
    blocks = [
        _line("Task count", len(rows)),
        _line("Shown task count", len(shown)),
        _line("Remaining task count", max(0, len(rows) - len(shown))),
    ]
    for row in shown:
        blocks.extend((
            "\n## Task\n\n", _line("Task key", row.get("taskKey")),
            _line("Task group", row.get("taskGroup")),
            _line("Task type", row.get("taskType")),
            _line("Current status", row.get("currentStatus")),
            _line("Latest run status", row.get("latestRunStatus")),
            _line("Last run", row.get("lastRun")),
            _line("Latest report", row.get("latestReportRecordPath")),
            _line("Updated at", row.get("updatedAt")),
            _line("Latest run manifest", row.get("latestRunManifestPath")),
        ))
    return "# Okstra History Input\n\n" + "".join(blocks)


def _csv(value: object) -> str:
    return ",".join(str(item) for item in value) if isinstance(value, list) else ""


def _provider_models(run: Mapping[str, Any]) -> dict[str, str]:
    values: dict[str, set[str]] = {}
    assignments = run.get("workerAssignments")
    if isinstance(assignments, list):
        for assignment in assignments:
            if not isinstance(assignment, Mapping):
                continue
            provider = assignment.get("provider")
            model = assignment.get("model")
            if isinstance(provider, str) and isinstance(model, str) and model:
                values.setdefault(provider, set()).add(model)
    for provider, models in values.items():
        if len(models) > 1:
            raise JsonBoundaryError(
                Path("workerAssignments"), "run manifest", f"conflicting models for provider {provider}"
            )
    return {provider: next(iter(models)) for provider, models in values.items()}
