"""CLI entrypoint for okstra manager."""
from __future__ import annotations

import argparse
import json
import os
import sys
from pathlib import Path

from okstra_project.dirs import okstra_home

from .manager_launch import build_launch_packet
from .fixed_text import line, value_lines
from .manager_store import (
    ManagerError,
    append_directive,
    assign_child,
    create_task,
    create_task_group,
    discover_projects,
    init_manager,
    link_project,
)
from .manager_sync import status_task, sync_task


def render_manager_text(command: str, payload: dict | list[dict]) -> str:
    """manager 모델 표면에 승인된 식별자와 상태만 투영한다."""
    allowed = {"init", "discover-projects", "new-project", "new-task-group", "new-task",
               "task-assign", "task-note", "task-sync", "task-status", "task-run"}
    if command not in allowed:
        raise ValueError("unknown manager text purpose")
    rows = [f"Okstra manager {command}\n", line("Status", "ready")]
    if isinstance(payload, list):
        rows.append(line("Result count", len(payload)))
        rows.extend(_render_manager_projects(payload))
        return "".join(rows)
    if command == "task-status":
        rows.extend(_render_manager_status(payload))
        rows.extend(value_lines("Status details", payload))
        return "".join(rows)
    for label, key in (("Manager ID", "managerId"), ("Schema version", "schemaVersion"),
                       ("Task key", "taskKey"), ("Project ID", "projectId"),
                       ("Task group", "taskGroup"), ("Task ID", "taskId"),
                       ("Work status", "workStatus"), ("Run command", "command"),
                       ("Backend", "backend"),
                       ("Worker dispatch backend", "workerDispatchBackend"),
                       ("Project root", "projectRoot"), ("Context path", "contextPath")):
        if key in payload:
            rows.append(line(label, payload.get(key)))
    run_args = payload.get("runArgs")
    if isinstance(run_args, list):
        for index, value in enumerate(run_args, 1):
            rows.append(line(f"Run arg {index}", value))
    structures = {
        "new-project": (("Projects", "projects"),),
        "new-task": (("Manifest", "manifest"), ("Children", "children")),
        "task-assign": (("Children", "children"),),
        "task-sync": (("Children", "children"),),
    }
    for label, key in structures.get(command, ()):
        rows.extend(value_lines(label, payload.get(key)))
    if command == "task-note":
        for label, key in (("Scope", "scope"), ("Directive project ID", "projectId"),
                           ("Directive body", "body"), ("Created at", "createdAt"),
                           ("Source", "source")):
            rows.append(line(label, payload.get(key)))
    if command == "task-sync" and "syncedAt" in payload:
        rows.append(line("Synced at", payload.get("syncedAt")))
    return "".join(rows)


def _render_manager_projects(projects: list[dict]) -> list[str]:
    rows: list[str] = []
    for index, project in enumerate(projects, 1):
        if not isinstance(project, dict):
            continue
        for label, key in (("ID", "projectId"), ("root", "projectRoot"),
                           ("run count", "runCount"), ("active count", "activeCount"),
                           ("last run at", "lastRunAt")):
            rows.append(line(f"Project {index} {label}", project.get(key)))
    return rows


def _render_manager_status(payload: dict) -> list[str]:
    rows: list[str] = []
    manifest = payload.get("manifest") if isinstance(payload.get("manifest"), dict) else {}
    for label, key in (("Manager ID", "managerId"), ("Task group", "taskGroup"),
                       ("Task ID", "taskId"), ("Progress mode", "progressMode")):
        rows.append(line(label, manifest.get(key)))
    summary = payload.get("summary") if isinstance(payload.get("summary"), dict) else {}
    for key in ("planned", "missing", "running", "done", "blocked"):
        rows.append(line(f"Summary {key}", summary.get(key)))
    children = payload.get("children") if isinstance(payload.get("children"), list) else []
    for index, child in enumerate(children, 1):
        if not isinstance(child, dict):
            continue
        for label, key in (("task key", "taskKey"), ("run status", "latestRunStatus"),
                           ("current phase", "currentPhase"), ("work status", "workStatus"),
                           ("report path", "latestReportRecordPath"), ("error", "error")):
            if key in child:
                rows.append(line(f"Child {index} {label}", child.get(key)))
    return rows


def _emit(command: str, payload: dict | list[dict], as_json: bool) -> None:
    if as_json:
        print(json.dumps(payload, ensure_ascii=False, indent=2))
        return
    print(render_manager_text(command, payload), end="")


def _normalize_global_flags(argv: list[str] | None) -> list[str]:
    values = list(argv or [])
    out: list[str] = []
    json_seen = False
    for value in values:
        if value == "--json":
            json_seen = True
            continue
        out.append(value)
    return (["--json"] if json_seen else []) + out


def _parse_child_specs(values: list[str], task_group: str, default_task_id: str) -> list[dict]:
    specs: list[dict] = []
    for value in values:
        parts = value.split(":")
        if len(parts) == 1:
            specs.append({"projectId": parts[0], "taskId": default_task_id})
            continue
        if len(parts) == 2:
            specs.append({"projectId": parts[0], "taskId": parts[1]})
            continue
        if len(parts) == 3:
            project_id, embedded_task_group, child_task_id = parts
            if embedded_task_group != task_group:
                raise ManagerError(
                    f"task-group mismatch for --task {value!r}: expected {task_group!r}, got {embedded_task_group!r}"
                )
            specs.append({"projectId": project_id, "taskId": child_task_id})
            continue
        raise ManagerError(
            f"invalid --task {value!r}: expected project-id, project-id:task-id, or project-id:task-group:task-id"
        )
    return specs


def _parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog="okstra manager")
    parser.add_argument("--json", action="store_true")
    parser.add_argument("--workspace-root", default="")
    sub = parser.add_subparsers(dest="command", required=True)

    init = sub.add_parser("init")
    init.add_argument("--manager-id", required=True)

    sub.add_parser("discover-projects")

    new = sub.add_parser("new")
    new_sub = new.add_subparsers(dest="new_command", required=True)

    project = new_sub.add_parser("project")
    project.add_argument("--manager-id", required=True)
    project.add_argument("--project-id", required=True)
    project.add_argument("--project-root", required=True)
    project.add_argument("--role", default="")
    project.add_argument("--tag", action="append", default=[])

    group = new_sub.add_parser("task-group")
    group.add_argument("--manager-id", required=True)
    group.add_argument("--task-group", required=True)

    task_new = new_sub.add_parser("task")
    task_new.add_argument("--manager-id", required=True)
    task_new.add_argument("--task-group", required=True)
    task_new.add_argument("--task-id", required=True)
    task_new.add_argument("--task", action="append", default=[])
    task_new.add_argument("--objective", default="")
    task_new.add_argument("--common-brief", default="")
    task_new.add_argument("--progress-mode", choices=["manual", "auto"], default="manual")

    task = sub.add_parser("task")
    task_sub = task.add_subparsers(dest="task_command", required=True)

    for name in ("status", "sync"):
        command = task_sub.add_parser(name)
        command.add_argument("--manager-id", required=True)
        command.add_argument("--task-group", required=True)
        command.add_argument("--task-id", required=True)

    assign = task_sub.add_parser("assign")
    assign.add_argument("--manager-id", required=True)
    assign.add_argument("--task-group", required=True)
    assign.add_argument("--task-id", required=True)
    assign.add_argument("--project-id", required=True)
    assign.add_argument("--child-task-id", default=None)
    assign.add_argument("--role", default="")
    assign.add_argument("--tag", action="append", default=[])
    assign.add_argument("--assignment", default="")

    note = task_sub.add_parser("note")
    note.add_argument("--manager-id", required=True)
    note.add_argument("--task-group", required=True)
    note.add_argument("--task-id", required=True)
    note.add_argument("--scope", choices=["shared", "project"], required=True)
    note.add_argument("--project-id", default="")
    note.add_argument("--body", required=True)

    run = task_sub.add_parser("run")
    run.add_argument("--manager-id", required=True)
    run.add_argument("--project-id", required=True)
    run.add_argument("--task-group", required=True)
    run.add_argument("--task-id", required=True)
    run.add_argument("--child-task-id", default=None)
    return parser


def main(argv: list[str] | None = None) -> int:
    parser = _parser()
    args = parser.parse_args(_normalize_global_flags(argv if argv is not None else sys.argv[1:]))
    home = okstra_home()
    try:
        if args.command == "init":
            _emit("init", init_manager(home, args.manager_id), args.json)
        elif args.command == "discover-projects":
            _emit("discover-projects", discover_projects(home), args.json)
        elif args.command == "new" and args.new_command == "project":
            _emit(
                "new-project",
                link_project(
                    home,
                    args.manager_id,
                    args.project_id,
                    Path(args.project_root),
                    role=args.role,
                    tags=args.tag,
                ),
                args.json,
            )
        elif args.command == "new" and args.new_command == "task-group":
            _emit("new-task-group", create_task_group(home, args.manager_id, args.task_group), args.json)
        elif args.command == "new" and args.new_command == "task":
            _emit(
                "new-task",
                create_task(
                    home,
                    args.manager_id,
                    args.task_group,
                    args.task_id,
                    _parse_child_specs(args.task, args.task_group, args.task_id),
                    objective=args.objective,
                    common_brief_path=args.common_brief,
                    progress_mode=args.progress_mode,
                ),
                args.json,
            )
        elif args.command == "task" and args.task_command == "assign":
            _emit(
                "task-assign",
                assign_child(
                    home,
                    args.manager_id,
                    args.task_group,
                    args.task_id,
                    args.project_id,
                    role=args.role,
                    tags=args.tag,
                    assignment=args.assignment,
                    child_task_id=args.child_task_id,
                ),
                args.json,
            )
        elif args.command == "task" and args.task_command == "note":
            _emit(
                "task-note",
                append_directive(
                    home,
                    args.manager_id,
                    args.task_group,
                    args.task_id,
                    scope=args.scope,
                    project_id=args.project_id,
                    body=args.body,
                ),
                args.json,
            )
        elif args.command == "task" and args.task_command == "sync":
            _emit("task-sync", sync_task(home, args.manager_id, args.task_group, args.task_id), args.json)
        elif args.command == "task" and args.task_command == "status":
            _emit("task-status", status_task(home, args.manager_id, args.task_group, args.task_id), args.json)
        elif args.command == "task" and args.task_command == "run":
            _emit(
                "task-run",
                build_launch_packet(
                    home,
                    args.manager_id,
                    args.task_group,
                    args.task_id,
                    args.project_id,
                    child_task_id=args.child_task_id,
                    env=os.environ,
                ),
                args.json,
            )
        else:
            parser.print_help()
            return 2
        return 0
    except ManagerError as exc:
        print(f"okstra manager: {exc}", file=sys.stderr)
        return 2


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))
