# shellcheck shell=bash

validate_reference_expectations() {
  local brief_path="$1"
  local reference_expectations_file="$2"
  local expected_task_key="$3"

  python3 - "$brief_path" "$reference_expectations_file" "$expected_task_key" <<'PY'
from pathlib import Path
import sys

brief_path = Path(sys.argv[1])
reference_path = Path(sys.argv[2])
expected_task_key = sys.argv[3]
brief_lines = brief_path.read_text().splitlines()
section_map = {
    "Configuration References and Expected Values": "config",
    "Deployment Manifests and Expected Values": "deployment",
}
captured = {"config": [], "deployment": []}
current_section = None

for line in brief_lines:
    if line.startswith("## "):
        current_section = section_map.get(line[3:].strip())
        continue
    if current_section and line.strip():
        captured[current_section].append(line)

errors = []

if not reference_path.is_file():
    errors.append(f"reference expectations file is missing: {reference_path}")
    reference_content = ""
else:
    reference_content = reference_path.read_text()

for line in captured["config"] + captured["deployment"]:
    if line not in reference_content:
        errors.append(f"reference expectations file is missing brief line: {line}")

if f"- Task Key: `{expected_task_key}`" not in reference_content:
    errors.append("reference expectations file is missing the task key header")

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

validate_task_artifacts() {
  local task_group="$1"
  local task_id="$2"
  local expected_task_key=""
  local expected_task_manifest_relative_path=""
  local expected_reference_relative_path=""

  expected_task_key="$(task_key "$task_group" "$task_id")"
  expected_task_manifest_relative_path="$(task_manifest_relative_path "$task_group" "$task_id")"
  expected_reference_relative_path="$(reference_expectations_relative_path "$task_group" "$task_id")"

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

project_root = Path(sys.argv[1])
expected_task_key = sys.argv[2]
task_manifest_relative_path = sys.argv[3]
expected_reference_relative_path = sys.argv[4]
expected_task_catalog_relative_path = sys.argv[5]
errors = []

task_manifest_path = project_root / task_manifest_relative_path
if not task_manifest_path.is_file():
    errors.append(f"task manifest is missing: {task_manifest_path}")
else:
    task_manifest = json.loads(task_manifest_path.read_text())

    if task_manifest.get("taskKey") != expected_task_key:
        errors.append("task manifest does not contain the expected task key")

    if task_manifest.get("referenceExpectationsPath") != expected_reference_relative_path:
        errors.append("task manifest does not expose referenceExpectationsPath")

    if task_manifest.get("taskCatalogPath") != expected_task_catalog_relative_path:
        errors.append("task manifest does not expose taskCatalogPath")

    if (
        task_manifest.get("artifacts", {}).get("referenceExpectationsPath")
        != expected_reference_relative_path
    ):
        errors.append("task manifest artifacts do not expose referenceExpectationsPath")

    timeline_relative_path = task_manifest.get("historyTimelinePath", "")
    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 = json.loads(timeline_path.read_text())
            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:
                run_manifest_relative_path = latest_run.get("runManifestPath", "")
                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 = json.loads(run_manifest_path.read_text())
                        if run_manifest.get("taskKey") != expected_task_key:
                            errors.append("run manifest does not contain the expected task key")
                        if (
                            run_manifest.get("referenceExpectationsPath")
                            != expected_reference_relative_path
                        ):
                            errors.append(
                                "run manifest does not expose referenceExpectationsPath"
                            )
                        if (
                            run_manifest.get("taskCatalogPath")
                            != expected_task_catalog_relative_path
                        ):
                            errors.append("run manifest does not expose taskCatalogPath")

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

validate_latest_task_pointer() {
  local task_group="$1"
  local task_id="$2"
  local expected_task_key=""
  local expected_task_manifest_relative_path=""
  local expected_reference_relative_path=""

  expected_task_key="$(task_key "$task_group" "$task_id")"
  expected_task_manifest_relative_path="$(task_manifest_relative_path "$task_group" "$task_id")"
  expected_reference_relative_path="$(reference_expectations_relative_path "$task_group" "$task_id")"

  python3 - "$PROJECT_ROOT" "$DISCOVERY_FILE" "$expected_task_key" "$expected_task_manifest_relative_path" "$expected_reference_relative_path" "$TASK_CATALOG_RELATIVE_PATH" <<'PY'
from pathlib import Path
import json
import sys

project_root = Path(sys.argv[1])
discovery_path = Path(sys.argv[2])
expected_task_key = sys.argv[3]
expected_task_manifest_relative_path = sys.argv[4]
expected_reference_relative_path = sys.argv[5]
expected_task_catalog_relative_path = sys.argv[6]
errors = []

if not discovery_path.is_file():
    errors.append(f"latest-task discovery file is missing: {discovery_path}")
else:
    discovery = json.loads(discovery_path.read_text())

    if discovery.get("taskKey") != expected_task_key:
        errors.append("latest-task discovery does not point to the expected task key")

    if discovery.get("taskManifestPath") != expected_task_manifest_relative_path:
        errors.append("latest-task discovery does not point to the expected task manifest")

    if discovery.get("referenceExpectationsPath") != expected_reference_relative_path:
        errors.append("latest-task discovery does not expose the expected reference expectations path")

    if discovery.get("taskCatalogPath") != expected_task_catalog_relative_path:
        errors.append("latest-task discovery does not expose taskCatalogPath")

    latest_run_prompts_relative_path = discovery.get("latestRunPromptsPath", "")
    if not latest_run_prompts_relative_path:
        errors.append("latest-task discovery is missing latestRunPromptsPath")

    latest_run_manifest_relative_path = discovery.get("latestRunManifestPath", "")
    if not latest_run_manifest_relative_path:
        errors.append("latest-task discovery is missing latestRunManifestPath")
    else:
        latest_run_manifest_path = project_root / latest_run_manifest_relative_path
        if not latest_run_manifest_path.is_file():
            errors.append(f"latest run manifest is missing: {latest_run_manifest_path}")
        else:
            run_manifest = json.loads(latest_run_manifest_path.read_text())
            if (
                latest_run_prompts_relative_path
                and run_manifest.get("workerPromptsDirectoryPath")
                != latest_run_prompts_relative_path
            ):
                errors.append(
                    "latest-task discovery latestRunPromptsPath does not match the latest run manifest"
                )

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

validate_task_catalog() {
  local expected_latest_task_key="$1"
  shift
  local expected_task_keys=("$@")

  python3 - "$PROJECT_ROOT" "$CATALOG_FILE" "$expected_latest_task_key" "$LATEST_TASK_RELATIVE_PATH" "${expected_task_keys[@]}" <<'PY'
from pathlib import Path
import json
import sys

project_root = Path(sys.argv[1])
catalog_path = Path(sys.argv[2])
expected_latest_task_key = sys.argv[3]
expected_latest_task_relative_path = sys.argv[4]
expected_task_keys = sys.argv[5:]
errors = []

if not catalog_path.is_file():
    errors.append(f"task catalog is missing: {catalog_path}")
else:
    catalog = json.loads(catalog_path.read_text())
    tasks = catalog.get("tasks", [])

    if catalog.get("latestTaskKey") != expected_latest_task_key:
        errors.append("task catalog does not record the expected latestTaskKey")

    if catalog.get("latestTaskDiscoveryPath") != expected_latest_task_relative_path:
        errors.append("task catalog does not expose latestTaskDiscoveryPath")

    if catalog.get("taskCount") != len(expected_task_keys):
        errors.append("task catalog taskCount does not match the expected number of tasks")

    if not isinstance(tasks, list):
        errors.append("task catalog tasks entry is not a list")
    else:
        catalog_by_key = {}
        for entry in tasks:
            if not isinstance(entry, dict):
                errors.append("task catalog contains a non-object entry")
                continue
            task_key = entry.get("taskKey", "")
            if not task_key:
                errors.append("task catalog contains an entry without taskKey")
                continue
            if task_key in catalog_by_key:
                errors.append(f"task catalog contains a duplicate task key: {task_key}")
                continue
            catalog_by_key[task_key] = entry

        for expected_task_key in expected_task_keys:
            entry = catalog_by_key.get(expected_task_key)
            if entry is None:
                errors.append(f"task catalog is missing task key: {expected_task_key}")
                continue
            task_key_parts = expected_task_key.split(":", 2)
            if len(task_key_parts) != 3:
                errors.append(f"expected task key is malformed: {expected_task_key}")
                continue
            _, expected_task_group, expected_task_id = task_key_parts

            if entry.get("taskGroup") != expected_task_group:
                errors.append(f"task catalog entry is missing the expected taskGroup: {expected_task_key}")

            if entry.get("taskId") != expected_task_id:
                errors.append(f"task catalog entry is missing the expected taskId: {expected_task_key}")

            task_manifest_relative_path = entry.get("taskManifestPath", "")
            if not task_manifest_relative_path:
                errors.append(f"task catalog entry is missing taskManifestPath: {expected_task_key}")
            else:
                task_manifest_path = project_root / task_manifest_relative_path
                if not task_manifest_path.is_file():
                    errors.append(f"task catalog points to a missing task manifest: {task_manifest_path}")

            reference_relative_path = entry.get("referenceExpectationsPath", "")
            if not reference_relative_path:
                errors.append(
                    f"task catalog entry is missing referenceExpectationsPath: {expected_task_key}"
                )
            else:
                reference_path = project_root / reference_relative_path
                if not reference_path.is_file():
                    errors.append(
                        f"task catalog points to a missing reference expectations file: {reference_path}"
                    )

            latest_run_manifest_relative_path = entry.get("latestRunManifestPath", "")
            if not latest_run_manifest_relative_path:
                errors.append(
                    f"task catalog entry is missing latestRunManifestPath: {expected_task_key}"
                )
            else:
                latest_run_manifest_path = project_root / latest_run_manifest_relative_path
                if not latest_run_manifest_path.is_file():
                    errors.append(
                        f"task catalog points to a missing latest run manifest: {latest_run_manifest_path}"
                    )
                else:
                    run_manifest = json.loads(latest_run_manifest_path.read_text())
                    latest_run_prompts_relative_path = entry.get(
                        "latestRunPromptsPath", ""
                    )
                    if not latest_run_prompts_relative_path:
                        errors.append(
                            f"task catalog entry is missing latestRunPromptsPath: {expected_task_key}"
                        )
                    elif (
                        run_manifest.get("workerPromptsDirectoryPath")
                        != latest_run_prompts_relative_path
                    ):
                        errors.append(
                            f"task catalog entry latestRunPromptsPath does not match the latest run manifest: {expected_task_key}"
                        )

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