"""Pydantic models and helpers for the POST /deploy/stream endpoint.

This module contains:
- Request validation models (DeployStreamRequest)
- JSONL event envelope construction (make_event, to_jsonl_line)
- Token redaction (_redact / redact_event)
"""

import json
from datetime import datetime, timezone
from typing import Any

from pydantic import BaseModel, Field, field_validator

# ---------------------------------------------------------------------------
# Request models
# ---------------------------------------------------------------------------


class DeploymentPayload(BaseModel):
    """The deployment plan sent by the caller.

    Must contain a deploymentId (string) and a resources array.
    Each resource is validated later by fbnconfig's own model_validate,
    so here we just check the outer shape.
    """
    deployment_id: str = Field(..., alias="deploymentId", min_length=1)
    resources: list[dict[str, Any]]

    model_config = {"populate_by_name": True}


class DeployStreamRequest(BaseModel):
    """Top-level request body for POST /deploy/stream."""
    deployment: DeploymentPayload

    @field_validator("deployment", mode="before")
    @classmethod
    def deployment_must_be_dict(cls, v):
        if not isinstance(v, dict):
            raise ValueError("deployment must be an object")
        return v


# ---------------------------------------------------------------------------
# JSONL event helpers
# ---------------------------------------------------------------------------

def make_event(
    *,
    deployment_id: str,
    request_id: str,
    correlation_id: str | None = None,
    event_type: str,
    status: str,
    sequence: int,
    data: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Build a JSONL event envelope.

    Every line in the stream shares this shape - the 'data' field carries
    event-specific payload.
    """
    event = {
        "ts": datetime.now(timezone.utc).isoformat(),
        "deploymentId": deployment_id,
        "requestId": request_id,
        "eventType": event_type,
        "status": status,
        "sequence": sequence,
        "data": data or {},
    }
    if correlation_id is not None:
        event["correlationId"] = correlation_id
    return event


def to_jsonl_line(event: dict[str, Any]) -> str:
    """Serialise an event dict to a single JSONL line (with trailing newline)."""
    return json.dumps(event, separators=(",", ":")) + "\n"


def _redact_value(value: Any, secrets: set[str]) -> Any:
    """Recursively replace any occurrence of a known secret with '********'."""
    if isinstance(value, str):
        for secret in secrets:
            if secret in value:
                value = value.replace(secret, "********")
        return value
    if isinstance(value, dict):
        return {k: _redact_value(v, secrets) for k, v in value.items()}
    if isinstance(value, list):
        return [_redact_value(item, secrets) for item in value]
    return value


def redact_event(event: dict[str, Any], secrets: set[str]) -> dict[str, Any]:
    """Return a copy of *event* with any known secret strings scrubbed."""
    return _redact_value(event, secrets)
