# Copyright 2026 Datastrato, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tests for ADP type definitions."""

# pyright: reportCallIssue=false

import unittest

from adp_sdk.types import (
    Capabilities,
    DiscoverResult,
    ExecuteResult,
    Field,
    FieldDefinition,
    IdentityPredicate,
    Implementation,
    InitializeResult,
    LookupIntent,
    Predicate,
    PredicateCapability,
    PredicateGroup,
    QueryIntent,
    Resource,
    ServerCapabilities,
    UsageContract,
    ValidateResult,
)


class TestResource(unittest.TestCase):
    """Tests for Resource type."""

    def test_create_resource(self) -> None:
        resource = Resource(
            resource_id="com.example:users",
            version=1,
            intent_classes=["QUERY", "LOOKUP"],
            description="User data",
        )
        self.assertEqual(resource.resource_id, "com.example:users")
        self.assertEqual(resource.version, 1)
        self.assertEqual(resource.intent_classes, ["QUERY", "LOOKUP"])

    def test_resource_serialization(self) -> None:
        resource = Resource(
            resource_id="com.example:users",
            version=1,
            intent_classes=["QUERY"],
            description="User data",
        )
        data = resource.model_dump(by_alias=True, exclude_none=True)
        self.assertEqual(data["resourceId"], "com.example:users")
        self.assertEqual(data["version"], 1)
        self.assertEqual(data["description"], "User data")

    def test_resource_deserialization(self) -> None:
        data = {
            "resourceId": "com.example:users",
            "version": 1,
            "intentClasses": ["QUERY"],
            "description": "User data",
        }
        resource = Resource.model_validate(data)
        self.assertEqual(resource.resource_id, "com.example:users")
        self.assertEqual(resource.intent_classes, ["QUERY"])


# ============================================================================


class TestIntents(unittest.TestCase):
    """Tests for Intent types."""

    def test_lookup_intent(self) -> None:
        intent = LookupIntent(
            intent_class="LOOKUP",
            resource_id="com.example:users",
            key=IdentityPredicate(field_id="id", op="EQ", value=123),
            projections=["name", "email"],
        )
        self.assertEqual(intent.intent_class, "LOOKUP")
        self.assertEqual(intent.resource_id, "com.example:users")
        self.assertEqual(intent.key.field_id, "id")
        self.assertEqual(intent.key.value, 123)

    def test_query_intent(self) -> None:
        intent = QueryIntent(
            intent_class="QUERY",
            resource_id="com.example:users",
            predicates=PredicateGroup(
                op="AND",
                predicates=[
                    Predicate(field_id="status", op="EQ", value="active"),
                ],  # type: ignore[list-item]
            ),
            projections=["id", "name"],
            limit=100,
        )
        self.assertEqual(intent.intent_class, "QUERY")
        self.assertEqual(intent.resource_id, "com.example:users")
        self.assertEqual(intent.limit, 100)

    def test_query_intent_with_single_predicate(self) -> None:
        """PredicateExpression allows a single Predicate without wrapping in a group."""
        intent = QueryIntent(
            intent_class="QUERY",
            resource_id="com.example:users",
            predicates=Predicate(field_id="status", op="EQ", value="active"),
        )
        self.assertIsInstance(intent.predicates, Predicate)
        self.assertEqual(intent.predicates.field_id, "status")  # type: ignore[union-attr]

    def test_intent_serialization(self) -> None:
        intent = LookupIntent(
            intent_class="LOOKUP",
            resource_id="com.example:users",
            key=IdentityPredicate(field_id="id", op="EQ", value=123),
        )
        data = intent.model_dump(by_alias=True, exclude_none=True)
        self.assertEqual(data["intentClass"], "LOOKUP")
        self.assertEqual(data["resourceId"], "com.example:users")
        self.assertEqual(data["key"]["fieldId"], "id")


# ============================================================================


class TestResponses(unittest.TestCase):
    """Tests for Response types."""

    def test_initialize_result(self) -> None:
        result = InitializeResult(
            protocol_version="2026-01-20",
            capabilities=ServerCapabilities(),
            server_info=Implementation(name="test-server", version="1.0.0"),
        )
        self.assertEqual(result.protocol_version, "2026-01-20")
        self.assertEqual(result.server_info.name, "test-server")

    def test_discover_result(self) -> None:
        result = DiscoverResult(
            resources=[
                Resource(resource_id="com.example:users", version=1, intent_classes=["QUERY"], description="Users"),
            ]
        )
        self.assertEqual(len(result.resources), 1)
        self.assertEqual(result.resources[0].resource_id, "com.example:users")

    def test_execute_result(self) -> None:
        result = ExecuteResult(
            results=[
                {"id": 1, "name": "Alice"},
                {"id": 2, "name": "Bob"},
            ]
        )
        self.assertEqual(len(result.results), 2)
        self.assertEqual(result.results[0]["name"], "Alice")

    def test_validate_result(self) -> None:
        result = ValidateResult(valid=True)
        self.assertTrue(result.valid)

        result_invalid = ValidateResult(
            valid=False,
            issues=[],
        )
        self.assertFalse(result_invalid.valid)


# ============================================================================


class TestFieldDefinition(unittest.TestCase):
    """Tests for FieldDefinition type."""

    def test_field_definition(self) -> None:
        field = FieldDefinition(
            field_id="user_id",
            type="STRING",
            description="Unique user identifier",
        )
        self.assertEqual(field.field_id, "user_id")
        self.assertEqual(field.type, "STRING")

    def test_field_alias(self) -> None:
        self.assertIs(Field, FieldDefinition)


# ============================================================================


class TestUsageContract(unittest.TestCase):
    """Tests for UsageContract type."""

    def test_usage_contract(self) -> None:
        contract = UsageContract(
            fields=[
                FieldDefinition(field_id="id", type="INTEGER"),
                FieldDefinition(field_id="name", type="STRING"),
            ],
            capabilities=Capabilities(
                predicates=[
                    PredicateCapability(
                        field_id="id",
                        usage="OPTIONAL",
                        operators=["EQ"],
                    ),
                ],
            ),
        )
        self.assertEqual(len(contract.fields), 2)
        self.assertEqual(contract.fields[0].field_id, "id")
