# shellcheck shell=bash

validate_worker_prompt_metadata() {
  local task_group="$1"
  local task_id="$2"
  local expected_task_manifest_relative_path=""

  expected_task_manifest_relative_path="$(task_manifest_relative_path "$task_group" "$task_id")"

  python3 - "$PROJECT_ROOT" "$expected_task_manifest_relative_path" <<'PY'
from pathlib import Path
import json
import sys

project_root = Path(sys.argv[1])
task_manifest_path = project_root / sys.argv[2]
errors = []


def load_json(path: Path) -> dict:
    return json.loads(path.read_text())


def validate_prompt_contract(
    prefix: str,
    worker_ids: list[str],
    prompt_dir: str,
    prompt_map: object,
    required_worker_roles: object,
) -> None:
    if not prompt_dir:
        errors.append(f"{prefix} is missing worker prompt directory metadata")
    if not isinstance(prompt_map, dict):
        errors.append(f"{prefix} worker prompt map is missing or invalid")
        prompt_map = {}
    if not isinstance(required_worker_roles, list):
        errors.append(f"{prefix} requiredWorkerRoles is missing or invalid")
        required_worker_roles = []

    expected_dir_prefix = prompt_dir.rstrip("/") + "/" if prompt_dir else ""
    for worker_id in worker_ids:
        if worker_id not in prompt_map:
            errors.append(f"{prefix} worker prompt map is missing selected worker: {worker_id}")

    for worker in required_worker_roles:
        if not isinstance(worker, dict):
            errors.append(f"{prefix} requiredWorkerRoles contains a non-object entry")
            continue
        worker_id = str(worker.get("workerId", "")).strip()
        prompt_relative = str(worker.get("promptPath", "")).strip()
        if not worker_id:
            errors.append(f"{prefix} requiredWorkerRoles contains an entry without workerId")
            continue
        if not prompt_relative:
            errors.append(
                f"{prefix} required worker is missing promptPath: {worker_id}"
            )
            continue
        if prompt_map.get(worker_id) != prompt_relative:
            errors.append(
                f"{prefix} worker prompt map does not match requiredWorkerRoles for {worker_id}"
            )
        if expected_dir_prefix and not prompt_relative.startswith(expected_dir_prefix):
            errors.append(
                f"{prefix} worker prompt path is outside the prompt directory: {prompt_relative}"
            )


if not task_manifest_path.is_file():
    errors.append(f"task manifest is missing: {task_manifest_path}")
else:
    task_manifest = load_json(task_manifest_path)
    selected_workers = task_manifest.get("recommendedWorkers", [])
    if not isinstance(selected_workers, list):
        errors.append("task manifest recommendedWorkers is missing or invalid")
        selected_workers = []

    task_prompt_dir = str(task_manifest.get("latestRunPromptsPath", "")).strip()
    if not task_prompt_dir:
        errors.append("task manifest is missing latestRunPromptsPath")

    task_artifacts = task_manifest.get("artifacts", {})
    if not isinstance(task_artifacts, dict):
        errors.append("task manifest artifacts is missing or invalid")
        task_artifacts = {}

    validate_prompt_contract(
        "task manifest",
        selected_workers,
        str(task_artifacts.get("workerPromptsDirectoryPath", "")).strip(),
        task_artifacts.get("workerPromptPathByWorkerId"),
        task_manifest.get("resultContract", {}).get("requiredWorkerRoles"),
    )

    if (
        task_prompt_dir
        and str(task_artifacts.get("workerPromptsDirectoryPath", "")).strip()
        and task_prompt_dir
        != str(task_artifacts.get("workerPromptsDirectoryPath", "")).strip()
    ):
        errors.append(
            "task manifest latestRunPromptsPath does not match artifacts.workerPromptsDirectoryPath"
        )

    timeline_relative_path = str(
        task_manifest.get("historyTimelinePath", "")
    ).strip()
    if not timeline_relative_path:
        errors.append("task manifest is missing historyTimelinePath")
    else:
        timeline_path = project_root / timeline_relative_path
        if not timeline_path.is_file():
            errors.append(f"timeline file is missing: {timeline_path}")
        else:
            timeline = load_json(timeline_path)
            runs = timeline.get("runs", [])
            latest_run = None
            if isinstance(runs, list):
                for item in reversed(runs):
                    if isinstance(item, dict):
                        latest_run = item
                        break
            if latest_run is None:
                errors.append("timeline does not contain a latest run entry")
            else:
                latest_run_prompt_dir = str(
                    latest_run.get("workerPromptDirectoryPath", "")
                ).strip()
                if not latest_run_prompt_dir:
                    errors.append(
                        "latest timeline entry is missing workerPromptDirectoryPath"
                    )
                if task_prompt_dir and latest_run_prompt_dir and task_prompt_dir != latest_run_prompt_dir:
                    errors.append(
                        "timeline latest run prompt directory does not match task manifest latestRunPromptsPath"
                    )
                prompt_map = latest_run.get("workerPromptPathByWorkerId")
                if not isinstance(prompt_map, dict):
                    errors.append(
                        "latest timeline entry is missing workerPromptPathByWorkerId"
                    )
                else:
                    for worker_id in selected_workers:
                        if worker_id not in prompt_map:
                            errors.append(
                                f"latest timeline entry is missing worker prompt path for {worker_id}"
                            )

                run_manifest_relative_path = str(
                    latest_run.get("runManifestPath", "")
                ).strip()
                if not run_manifest_relative_path:
                    errors.append(
                        "latest timeline entry is missing runManifestPath"
                    )
                else:
                    run_manifest_path = project_root / run_manifest_relative_path
                    if not run_manifest_path.is_file():
                        errors.append(f"latest run manifest is missing: {run_manifest_path}")
                    else:
                        run_manifest = load_json(run_manifest_path)
                        run_prompt_dir = str(
                            run_manifest.get("workerPromptsDirectoryPath", "")
                        ).strip()
                        if task_prompt_dir and run_prompt_dir and task_prompt_dir != run_prompt_dir:
                            errors.append(
                                "run manifest workerPromptsDirectoryPath does not match task manifest latestRunPromptsPath"
                            )
                        validate_prompt_contract(
                            "run manifest",
                            selected_workers,
                            run_prompt_dir,
                            run_manifest.get("workerPromptPathByWorkerId"),
                            run_manifest.get("teamContract", {}).get("requiredWorkerRoles"),
                        )

                        team_state_relative_path = str(
                            run_manifest.get("teamStatePath")
                            or task_manifest.get("teamStatePath", "")
                        ).strip()
                        if not team_state_relative_path:
                            errors.append(
                                "team state relative path is missing from run/task manifest"
                            )
                        else:
                            team_state_path = project_root / team_state_relative_path
                            if not team_state_path.is_file():
                                errors.append(f"team-state file is missing: {team_state_path}")
                            else:
                                team_state = load_json(team_state_path)
                                team_artifacts = team_state.get("artifacts", {})
                                if not isinstance(team_artifacts, dict):
                                    errors.append(
                                        "team-state artifacts is missing or invalid"
                                    )
                                    team_artifacts = {}
                                team_prompt_dir = str(
                                    team_artifacts.get(
                                        "workerPromptsDirectoryPath", ""
                                    )
                                ).strip()
                                if run_prompt_dir and team_prompt_dir and run_prompt_dir != team_prompt_dir:
                                    errors.append(
                                        "team-state worker prompt directory does not match run manifest"
                                    )
                                workers = team_state.get("workers", [])
                                if not isinstance(workers, list):
                                    errors.append("team-state workers is missing or invalid")
                                else:
                                    team_workers = {}
                                    for worker in workers:
                                        if not isinstance(worker, dict):
                                            errors.append(
                                                "team-state workers contains a non-object entry"
                                            )
                                            continue
                                        worker_id = str(worker.get("workerId", "")).strip()
                                        if not worker_id:
                                            errors.append(
                                                "team-state workers contains an entry without workerId"
                                            )
                                            continue
                                        team_workers[worker_id] = worker

                                    run_prompt_map = run_manifest.get(
                                        "workerPromptPathByWorkerId", {}
                                    )
                                    if not isinstance(run_prompt_map, dict):
                                        run_prompt_map = {}

                                    for worker_id in selected_workers:
                                        worker = team_workers.get(worker_id)
                                        if worker is None:
                                            errors.append(
                                                f"team-state is missing selected worker entry: {worker_id}"
                                            )
                                            continue
                                        prompt_relative = str(
                                            worker.get("promptPath", "")
                                        ).strip()
                                        if not prompt_relative:
                                            errors.append(
                                                f"team-state worker is missing promptPath: {worker_id}"
                                            )
                                            continue
                                        if run_prompt_map.get(worker_id) != prompt_relative:
                                            errors.append(
                                                f"team-state worker prompt path does not match run manifest for {worker_id}"
                                            )
                                        expected_dir_prefix = (
                                            run_prompt_dir.rstrip("/") + "/"
                                            if run_prompt_dir
                                            else ""
                                        )
                                        if expected_dir_prefix and not prompt_relative.startswith(
                                            expected_dir_prefix
                                        ):
                                            errors.append(
                                                f"team-state worker prompt path is outside the run prompt directory: {prompt_relative}"
                                            )

if errors:
    for error in errors:
        print(error, file=sys.stderr)
    sys.exit(1)
PY
}
