"""
Genesis Persistent Context Architecture — Output Validator
Story 3.03 — Track B

Typed schema validator for swarm output.
Catches missing fields, wrong types, empty required strings.
Collects ALL errors (not fail-fast).
"""
from dataclasses import dataclass, field
from typing import Dict, Any, List


@dataclass
class ValidationResult:
    """Result of output validation."""
    valid: bool
    errors: List[str] = field(default_factory=list)


# Default schema for standard dispatch_to_swarm output
DEFAULT_SWARM_OUTPUT_SCHEMA: Dict[str, Dict[str, Any]] = {
    "task_id": {"type": "str", "required": True},
    "status": {"type": "str", "required": True},
}

# Type mapping for validation
_TYPE_MAP = {
    "str": str,
    "int": int,
    "float": (int, float),  # Accept int as float
    "list": list,
    "dict": dict,
    "bool": bool,
}


class OutputValidator:
    """Validates swarm output against typed schemas."""

    def validate(self, output: dict, schema: dict = None) -> ValidationResult:
        """
        Validate output dict against schema.

        Args:
            output: The swarm output to validate
            schema: Schema dict. Format: {"field": {"type": "str|int|float|list|dict|bool", "required": True/False}}
                    Defaults to DEFAULT_SWARM_OUTPUT_SCHEMA if not provided.

        Returns:
            ValidationResult with valid=True/False and list of all errors
        """
        if schema is None:
            schema = DEFAULT_SWARM_OUTPUT_SCHEMA

        errors = []

        for field_name, field_spec in schema.items():
            required = field_spec.get("required", False)
            expected_type = field_spec.get("type")

            value = output.get(field_name)

            # Check required presence
            if required and (field_name not in output or value is None):
                errors.append(f"Missing required field: '{field_name}'")
                continue

            # Skip if not present and not required
            if field_name not in output:
                continue

            # Check for empty required string
            if required and expected_type == "str" and isinstance(value, str) and value.strip() == "":
                errors.append(f"Empty string for required field: '{field_name}'")
                continue

            # Type check (only when value is present and non-None)
            if expected_type and value is not None:
                expected_python_type = _TYPE_MAP.get(expected_type)
                if expected_python_type and not isinstance(value, expected_python_type):
                    actual = type(value).__name__
                    errors.append(
                        f"Type mismatch for '{field_name}': expected {expected_type}, got {actual}"
                    )

        return ValidationResult(
            valid=len(errors) == 0,
            errors=errors,
        )


# VERIFICATION_STAMP
# Story: 3.03 (Track B)
# Verified By: parallel-builder
# Verified At: 2026-02-25
# Tests: 8/8
# Coverage: 100%
