"""
Pydantic models for API requests.
These define the contract between the API and external clients.

Following clean architecture principles, request models delegate validation
to domain model class methods and reuse field descriptions to avoid
duplication while maintaining single source of truth in the domain layer.
"""

from datetime import datetime, timezone
from typing import Any

from pydantic import BaseModel, Field, ValidationInfo, field_validator

from julee.contrib.ceap.domain.models import (
    AssemblySpecification,
    AssemblySpecificationStatus,
    KnowledgeServiceQuery,
)


class CreateAssemblySpecificationRequest(BaseModel):
    """Request model for creating an assembly specification.

    This model defines what clients need to provide when creating a new
    assembly specification. Validation logic is delegated to the domain
    model to ensure consistency and avoid duplication.

    Fields excluded from client control:
    - assembly_specification_id: Always generated by the server
    - status: Always set to DRAFT initially by the server
    - created_at/updated_at: System-managed timestamps
    """

    # Field definitions with descriptions reused from domain model
    name: str = Field(
        description=AssemblySpecification.model_fields["name"].description
    )
    applicability: str = Field(
        description=AssemblySpecification.model_fields["applicability"].description
    )
    jsonschema: dict[str, Any] = Field(
        description=AssemblySpecification.model_fields["jsonschema"].description
    )
    knowledge_service_queries: dict[str, str] = Field(
        default_factory=dict,
        description=AssemblySpecification.model_fields[
            "knowledge_service_queries"
        ].description,
    )
    version: str = Field(
        default=AssemblySpecification.model_fields["version"].default,
        description=AssemblySpecification.model_fields["version"].description,
    )

    # Delegate validation to domain model class methods
    @field_validator("name")
    @classmethod
    def validate_name(cls, v: str) -> str:
        return AssemblySpecification.name_must_not_be_empty(v)

    @field_validator("applicability")
    @classmethod
    def validate_applicability(cls, v: str) -> str:
        return AssemblySpecification.applicability_must_not_be_empty(v)

    @field_validator("jsonschema")
    @classmethod
    def validate_jsonschema(cls, v: dict[str, Any]) -> dict[str, Any]:
        return AssemblySpecification.jsonschema_must_be_valid(v)

    @field_validator("knowledge_service_queries")
    @classmethod
    def validate_knowledge_service_queries(
        cls, v: dict[str, str], info: ValidationInfo
    ) -> dict[str, str]:
        return AssemblySpecification.knowledge_service_queries_must_be_valid(v, info)

    @field_validator("version")
    @classmethod
    def validate_version(cls, v: str) -> str:
        return AssemblySpecification.version_must_not_be_empty(v)

    def to_domain_model(self, assembly_specification_id: str) -> AssemblySpecification:
        """Convert this request to a complete AssemblySpecification object.

        Args:
            assembly_specification_id: The ID to assign to the new spec

        Returns:
            AssemblySpecification: Complete domain object with system fields
        """
        now = datetime.now(timezone.utc)
        return AssemblySpecification(
            assembly_specification_id=assembly_specification_id,
            name=self.name,
            applicability=self.applicability,
            jsonschema=self.jsonschema,
            knowledge_service_queries=self.knowledge_service_queries,
            version=self.version,
            status=AssemblySpecificationStatus.DRAFT,
            created_at=now,
            updated_at=now,
        )


class CreateKnowledgeServiceQueryRequest(BaseModel):
    """Request model for creating a knowledge service query.

    This model defines what clients need to provide when creating a new
    knowledge service query. Validation logic is delegated to the domain
    model and descriptions are reused to avoid duplication while maintaining
    single source of truth in the domain layer.

    Fields excluded from client control:
    - query_id: Always generated by the server
    - created_at/updated_at: System-managed timestamps
    """

    # Field definitions with descriptions reused from domain model
    name: str = Field(
        description=KnowledgeServiceQuery.model_fields["name"].description
    )
    knowledge_service_id: str = Field(
        description=KnowledgeServiceQuery.model_fields[
            "knowledge_service_id"
        ].description
    )
    prompt: str = Field(
        description=KnowledgeServiceQuery.model_fields["prompt"].description
    )
    query_metadata: dict[str, Any] = Field(
        default_factory=dict,
        description=KnowledgeServiceQuery.model_fields["query_metadata"].description,
    )
    assistant_prompt: str | None = Field(
        default=KnowledgeServiceQuery.model_fields["assistant_prompt"].default,
        description=KnowledgeServiceQuery.model_fields["assistant_prompt"].description,
    )

    # Delegate validation to domain model class methods
    @field_validator("name")
    @classmethod
    def validate_name(cls, v: str) -> str:
        return KnowledgeServiceQuery.name_must_not_be_empty(v)

    @field_validator("knowledge_service_id")
    @classmethod
    def validate_knowledge_service_id(cls, v: str) -> str:
        return KnowledgeServiceQuery.knowledge_service_id_must_not_be_empty(v)

    @field_validator("prompt")
    @classmethod
    def validate_prompt(cls, v: str) -> str:
        return KnowledgeServiceQuery.prompt_must_not_be_empty(v)

    def to_domain_model(self, query_id: str) -> KnowledgeServiceQuery:
        """Convert this request to a complete KnowledgeServiceQuery object.

        Args:
            query_id: The ID to assign to the new query

        Returns:
            KnowledgeServiceQuery: Complete domain object with system fields
        """
        now = datetime.now(timezone.utc)
        return KnowledgeServiceQuery(
            query_id=query_id,
            name=self.name,
            knowledge_service_id=self.knowledge_service_id,
            prompt=self.prompt,
            query_metadata=self.query_metadata,
            assistant_prompt=self.assistant_prompt,
            created_at=now,
            updated_at=now,
        )
