"""Loader for p2hs/functions.json — the codegen agent's declarative function deployment file.

The agent writes this file when it needs a library function deployed for the project.
The worker reads it after the AI run and calls EdgeFunctionsService.deploy_from_library_by_slug
for each entry.  Processing is non-blocking: failures are logged but do not fail the run.

File shape (version 1):
{
  "version": 1,
  "deploy": [
    {
      "library_slug": "send-email",
      "project_slug": "contact-form-send-email",
      "trigger_type": "http",
      "trigger_config": {},
      "access_policy": "authenticated",
      "secret_refs": []
    }
  ]
}

Naming convention for project_slug: {context}-{library-slug}
  e.g. contact-form-send-email, signup-send-email, weekly-digest-schedule
  Context describes the specific use-case so the same library function can be deployed
  multiple times for different purposes without slug collision within the project.
"""
from __future__ import annotations

import json
import os
import re
from typing import Any, Optional

MANIFEST_RELPATH = "p2hs/functions.json"
_SLUG_RE = re.compile(r"^[a-z][a-z0-9-]*$")
_VALID_TRIGGERS = {"http", "event", "schedule", "webhook"}
_VALID_POLICIES = {"public", "authenticated", "private"}


def manifest_path(repo_path: str) -> str:
    return os.path.join(repo_path, *MANIFEST_RELPATH.split("/"))


def load_manifest_from_repo_path(repo_path: str) -> Optional[list[dict[str, Any]]]:
    """Load and validate p2hs/functions.json.  Returns the deploy list or None if absent/invalid."""
    p = manifest_path(repo_path)
    if not os.path.isfile(p):
        return None
    try:
        with open(p, encoding="utf-8") as f:
            data = json.load(f)
    except (OSError, json.JSONDecodeError, UnicodeDecodeError):
        return None
    if not isinstance(data, dict):
        return None
    ver = data.get("version")
    if ver is not None and ver != 1:
        return None
    deploy = data.get("deploy")
    if not isinstance(deploy, list) or not deploy:
        return None

    valid: list[dict[str, Any]] = []
    for entry in deploy:
        if not isinstance(entry, dict):
            continue
        lib_slug = entry.get("library_slug", "")
        proj_slug = entry.get("project_slug", "")
        if not lib_slug or not proj_slug:
            continue
        if not _SLUG_RE.match(str(lib_slug)) or not _SLUG_RE.match(str(proj_slug)):
            continue
        trigger_type = entry.get("trigger_type", "http")
        if trigger_type not in _VALID_TRIGGERS:
            continue
        access_policy = entry.get("access_policy", "authenticated")
        if access_policy not in _VALID_POLICIES:
            access_policy = "authenticated"
        valid.append({
            "library_slug": str(lib_slug),
            "project_slug": str(proj_slug),
            "trigger_type": trigger_type,
            "trigger_config": entry.get("trigger_config") or {},
            "access_policy": access_policy,
            "secret_refs": entry.get("secret_refs") or [],
        })
    return valid if valid else None
