"""CompositionRequirement -- what codegen is allowed to author. Schema-level rejections
only; derivation/hard-validation is tested in test_feature_pack_composer.py."""
from __future__ import annotations

import pytest
from pydantic import ValidationError

from src.domain.feature_packs.requirements_spec import CompositionRequirement


def _base(**overrides):
    payload = {
        "name": "quote_request",
        "actors": ["anonymous", "admin"],
        "requirements": [
            {"action": "create", "resource": "quote_request", "actor": "anonymous"},
            {"action": "read", "resource": "quote_request", "actor": "admin"},
        ],
        "fields": [{"key": "name", "label": "Name", "type": "text", "required": True}],
    }
    payload.update(overrides)
    return payload


def test_valid_quote_request_shape_parses():
    spec = CompositionRequirement.model_validate(_base())
    assert spec.name == "quote_request"


def test_criticality_defaults_to_required():
    spec = CompositionRequirement.model_validate(_base())
    assert spec.criticality == "required"


def test_criticality_can_be_declared_optional():
    spec = CompositionRequirement.model_validate(_base(criticality="optional"))
    assert spec.criticality == "optional"


def test_criticality_rejects_unknown_values():
    with pytest.raises(ValidationError):
        CompositionRequirement.model_validate(_base(criticality="nice_to_have"))


def test_unknown_role_rejected():
    with pytest.raises(ValidationError):
        CompositionRequirement.model_validate(
            _base(
                requirements=[
                    {"action": "create", "resource": "quote_request", "actor": "customer"},
                ]
            )
        )


def test_requirement_actor_not_in_actors_list_rejected():
    with pytest.raises(ValidationError):
        CompositionRequirement.model_validate(
            _base(
                actors=["anonymous"],
                requirements=[
                    {"action": "create", "resource": "quote_request", "actor": "anonymous"},
                    {"action": "read", "resource": "quote_request", "actor": "admin"},
                ],
            )
        )


def test_duplicate_field_keys_rejected():
    with pytest.raises(ValidationError):
        CompositionRequirement.model_validate(
            _base(
                fields=[
                    {"key": "name", "label": "Name", "type": "text"},
                    {"key": "name", "label": "Name Again", "type": "text"},
                ]
            )
        )


def test_extra_fields_forbidden_no_inline_code_escape_hatch():
    """The structural guarantee: there is no field in this schema an author could use
    to smuggle in raw code or an arbitrary Cloud call -- extra="forbid" rejects
    anything not explicitly modeled."""
    with pytest.raises(ValidationError):
        CompositionRequirement.model_validate(_base(inline_code="console.log('x')"))


def test_select_field_without_options_rejected():
    """Reuses ProvisionField's own validation -- confirms composition doesn't bypass
    the existing, already-audited field-level rules."""
    with pytest.raises(ValidationError):
        CompositionRequirement.model_validate(
            _base(fields=[{"key": "status", "label": "Status", "type": "select"}])
        )


def test_workflow_block_parses():
    """Schema-level only: the WorkflowRequirement shape itself is well-formed. Whether
    `field` actually exists among `fields` / is a select type / transitions reference
    real options is a cross-referential check, not expressible at this schema layer --
    covered in test_feature_pack_composer.py (derive_pack_composition)."""
    spec = CompositionRequirement.model_validate(
        _base(
            fields=[
                {"key": "subject", "label": "Subject", "type": "text", "required": True},
                {"key": "status", "label": "Status", "type": "select",
                 "options": ["open", "pending", "resolved"]},
            ],
            workflow={"field": "status", "transitions": {"open": ["pending"], "pending": ["resolved"], "resolved": []}},
        )
    )
    assert spec.workflow is not None
    assert spec.workflow.field == "status"
    assert spec.workflow.transitions["open"] == ["pending"]


def test_workflow_extra_fields_forbidden():
    with pytest.raises(ValidationError):
        CompositionRequirement.model_validate(
            _base(workflow={"field": "status", "transitions": {"open": ["pending"]}, "actor_gated": True})
        )


def test_workflow_empty_transitions_rejected():
    with pytest.raises(ValidationError):
        CompositionRequirement.model_validate(
            _base(workflow={"field": "status", "transitions": {}})
        )
