"""Build manager child launch packets and context documents."""
from __future__ import annotations

from pathlib import Path
from typing import Mapping

from okstra_ctl.jsonl import append_jsonl, read_jsonl

from .manager_paths import (
    child_context_path,
    children_json_path,
    directives_jsonl_path,
    events_jsonl_path,
    projects_json_path,
    snapshots_json_path,
    task_manifest_path,
)
from .manager_store import ManagerError, _now_iso, _read_json, _read_json_default, _write_json


WORKER_DISPATCH_BACKEND = "subagent"
LAUNCH_STATUS_PREPARED = "prepared"


def select_child_lead_backend(env: Mapping[str, str]) -> str:
    return "tmux-child-lead" if str(env.get("TMUX") or "").strip() else "subagent-child-lead"


def _find_project(projects: dict, project_id: str) -> dict:
    for project in projects.get("projects", []):
        if isinstance(project, dict) and project.get("projectId") == project_id:
            return project
    raise ManagerError(f"unknown project membership: {project_id}")


def _target_task_key(project_id: str, task_group: str, task_id: str) -> str:
    return f"{project_id}:{task_group}:{task_id}"


def _find_child(children: dict, project_id: str, task_group: str, task_id: str) -> dict:
    target_key = _target_task_key(project_id, task_group, task_id)
    for child in children.get("children", []):
        if not isinstance(child, dict):
            continue
        if child.get("taskKey") == target_key:
            return child
        if (
            child.get("projectId") == project_id
            and child.get("taskGroup") == task_group
            and child.get("taskId") == task_id
        ):
            return child
    raise ManagerError(f"planned child not found for task key: {target_key}")


def _validate_child_task_key(child: dict, target_task_key: str) -> None:
    persisted_key = str(child.get("taskKey") or "").strip()
    if persisted_key and persisted_key != target_task_key:
        raise ManagerError(f"child taskKey mismatch: {persisted_key!r}, expected {target_task_key!r}")


def _validated_project_root(project: dict) -> str:
    raw_root = project.get("projectRoot")
    if not isinstance(raw_root, str) or not raw_root.strip():
        raise ManagerError(f"projectRoot is required for project: {project.get('projectId') or ''}")
    root = Path(raw_root).resolve()
    if not root.is_dir():
        raise ManagerError(f"projectRoot is not a directory for project: {project.get('projectId') or ''}: {root}")
    return str(root)


def _directives_for_project(rows: list[dict], project_id: str) -> list[dict]:
    directives: list[dict] = []
    for row in rows:
        if row.get("scope") == "shared":
            directives.append(row)
        elif row.get("scope") == "project" and row.get("projectId") == project_id:
            directives.append(row)
    return directives


def _sibling_snapshots(snapshot: dict, target_task_key: str) -> list[dict]:
    return [
        row
        for row in snapshot.get("children", [])
        if isinstance(row, dict) and row.get("taskKey") != target_task_key
    ]


def _render_context(
    *,
    manifest: dict,
    child: dict,
    child_task_key: str,
    directives: list[dict],
    siblings: list[dict],
) -> str:
    lines = [
        "# Okstra Manager Child Context",
        "",
        f"- manager-id: {manifest['managerId']}",
        f"- task-group: {manifest['taskGroup']}",
        f"- manager task-id: {manifest['taskId']}",
        f"- child task-key: {child_task_key}",
        f"- objective: {manifest.get('objective') or ''}",
        f"- common brief: {manifest.get('commonBriefPath') or ''}",
        "",
        "## Assignment",
        "",
        f"- role: {child.get('role') or ''}",
        f"- tags: {', '.join(child.get('tags') or [])}",
        "",
        child.get("assignment") or "",
        "",
        "## Manager Directives",
        "",
    ]
    for row in directives:
        lines.append(f"- [{row.get('scope')}] {row.get('body')}")
    if not directives:
        lines.append("- none")
    lines.extend(
        [
            "",
            "## Sibling Project Snapshots",
            "",
            "Sibling synced reports are read-side source material.",
            "Do not write into sibling project roots.",
            "Project-local files remain the edit boundary for this child task.",
            "",
        ]
    )
    for row in siblings:
        lines.append(
            f"- {row.get('taskKey')}: {row.get('latestRunStatus') or 'unknown'} {row.get('latestReportRecordPath') or ''}"
        )
    if not siblings:
        lines.append("- none")
    lines.append("")
    return "\n".join(lines)


def _record_launch_prepared(
    *,
    home: Path,
    manager_id: str,
    task_group: str,
    task_id: str,
    children: dict,
    child: dict,
    packet: dict,
    created_at: str,
) -> None:
    launch = child.get("launch")
    if not isinstance(launch, dict):
        launch = {}
    launch.update(
        {
            "status": LAUNCH_STATUS_PREPARED,
            "childLeadBackend": packet["backend"],
            "workerDispatchBackend": WORKER_DISPATCH_BACKEND,
            "taskKey": packet["taskKey"],
            "contextPath": packet["contextPath"],
            "preparedAt": created_at,
        }
    )
    child["launch"] = launch
    _write_json(children_json_path(home, manager_id, task_group, task_id), children)
    append_jsonl(
        events_jsonl_path(home, manager_id, task_group, task_id),
        {
            "event": "child-launch-prepared",
            "createdAt": created_at,
            "projectId": packet["projectId"],
            "taskKey": packet["taskKey"],
            "backend": packet["backend"],
            "workerDispatchBackend": WORKER_DISPATCH_BACKEND,
            "contextPath": packet["contextPath"],
            "runArgs": packet["runArgs"],
        },
    )


def build_launch_packet(
    home: Path,
    manager_id: str,
    task_group: str,
    task_id: str,
    project_id: str,
    *,
    child_task_id: str | None = None,
    env: Mapping[str, str] | None = None,
    now: str | None = None,
) -> dict:
    manifest = _read_json(task_manifest_path(home, manager_id, task_group, task_id))
    projects = _read_json_default(projects_json_path(home, manager_id), {"projects": []})
    children = _read_json_default(children_json_path(home, manager_id, task_group, task_id), {"children": []})
    snapshot = _read_json_default(snapshots_json_path(home, manager_id, task_group, task_id), {"children": []})
    directives = read_jsonl(directives_jsonl_path(home, manager_id, task_group, task_id))
    project = _find_project(projects, project_id)
    effective_child_task_id = child_task_id or task_id
    child = _find_child(children, project_id, task_group, effective_child_task_id)
    child_task_id_value = str(child.get("taskId") or effective_child_task_id)
    target_task_key = _target_task_key(project_id, task_group, child_task_id_value)
    _validate_child_task_key(child, target_task_key)
    project_root = _validated_project_root(project)
    backend = select_child_lead_backend(env or {})
    created_at = now or _now_iso()
    context_path = child_context_path(home, manager_id, task_group, task_id, project_id, child_task_id_value)
    context_path.parent.mkdir(parents=True, exist_ok=True)
    context_path.write_text(
        _render_context(
            manifest=manifest,
            child=child,
            child_task_key=target_task_key,
            directives=_directives_for_project(directives, project_id),
            siblings=_sibling_snapshots(snapshot, target_task_key),
        ),
        encoding="utf-8",
    )
    packet = {
        "managerId": manager_id,
        "taskGroup": task_group,
        "taskId": task_id,
        "projectId": project_id,
        "taskKey": target_task_key,
        "backend": backend,
        "workerDispatchBackend": WORKER_DISPATCH_BACKEND,
        "projectRoot": project_root,
        "contextPath": str(context_path),
        "runArgs": [
            "run",
            "--project-root",
            project_root,
            "--project-id",
            project_id,
            "--task-group",
            str(child.get("taskGroup") or task_group),
            "--task-id",
            child_task_id_value,
            "--directive",
            f"Read manager child context: {context_path}",
        ],
        "createdAt": created_at,
    }
    _record_launch_prepared(
        home=home,
        manager_id=manager_id,
        task_group=task_group,
        task_id=task_id,
        children=children,
        child=child,
        packet=packet,
        created_at=created_at,
    )
    return packet
