"""All-parameters tests — verify every field serializes to the correct camelCase wire name.

One test per resource type per operation (create / update). Each test sets ALL
parameters and asserts every field appears in the JSON body with the right alias
and value. This catches alias mismatches, type coercion bugs, and exclude_unset
surprises without exercising server validation.
"""

import base64
import json

import httpx
import pytest

from goodmem._generated.models.llm_update_request import LLMUpdateRequest
from goodmem._generated.models.update_embedder_request import UpdateEmbedderRequest
from goodmem._generated.models.update_reranker_request import UpdateRerankerRequest
from goodmem._generated.models.update_space_request import UpdateSpaceRequest
from tests.conftest import (
    EMBEDDER_RESPONSE,
    LLM_RESPONSE,
    MEMORY_RESPONSE,
    RERANKER_RESPONSE,
    SPACE_RESPONSE,
)


# ---------------------------------------------------------------------------
# Embedder create — all params
# ---------------------------------------------------------------------------

def test_create_embedder_all_params(mock_client_factory):
    """Every EmbedderCreationRequest field serializes with correct alias."""
    captured = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["body"] = json.loads(request.content)
        return httpx.Response(200, json=EMBEDDER_RESPONSE)

    client = mock_client_factory(handler)
    client.embedders.create(
        display_name="All-Params Embedder",
        description="Full param test",
        provider_type="OPENAI",
        endpoint_url="https://api.openai.com/v1",
        api_path="/embeddings",
        model_identifier="text-embedding-3-small",
        dimensionality=1536,
        distribution_type="DENSE",
        max_sequence_length=8191,
        supported_modalities=["TEXT"],
        api_key="sk-test",
        labels={"env": "test", "team": "sdk"},
        version="1.0",
        monitoring_endpoint="https://monitor.example.com",
        owner_id="owner-abc",
        embedder_id="emb-custom-id",
    )

    body = captured["body"]
    assert body["displayName"] == "All-Params Embedder"
    assert body["description"] == "Full param test"
    assert body["providerType"] == "OPENAI"
    assert body["endpointUrl"] == "https://api.openai.com/v1"
    assert body["apiPath"] == "/embeddings"
    assert body["modelIdentifier"] == "text-embedding-3-small"
    assert body["dimensionality"] == 1536
    assert body["distributionType"] == "DENSE"
    assert body["maxSequenceLength"] == 8191
    assert body["supportedModalities"] == ["TEXT"]
    assert body["labels"] == {"env": "test", "team": "sdk"}
    assert body["version"] == "1.0"
    assert body["monitoringEndpoint"] == "https://monitor.example.com"
    assert body["ownerId"] == "owner-abc"
    assert body["embedderId"] == "emb-custom-id"
    # api_key convenience → credentials
    assert body["credentials"]["kind"] == "CREDENTIAL_KIND_API_KEY"
    assert body["credentials"]["apiKey"]["inlineSecret"] == "sk-test"


# ---------------------------------------------------------------------------
# Embedder update — all params
# ---------------------------------------------------------------------------

def test_update_embedder_all_params(mock_client_factory):
    """Every UpdateEmbedderRequest field serializes with correct alias."""
    captured = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["body"] = json.loads(request.content)
        return httpx.Response(200, json=EMBEDDER_RESPONSE)

    client = mock_client_factory(handler)
    client.embedders.update(id="emb-123", request=UpdateEmbedderRequest(
        display_name="Updated Embedder",
        description="Updated desc",
        endpoint_url="https://api.openai.com/v2",
        api_path="/new-embeddings",
        model_identifier="text-embedding-3-large",
        dimensionality=3072,
        max_sequence_length=16384,
        supported_modalities=["TEXT", "IMAGE"],
        credentials={
            "kind": "CREDENTIAL_KIND_API_KEY",
            "api_key": {"inline_secret": "sk-new"},
        },
        replace_labels={"env": "prod"},
        version="2.0",
        monitoring_endpoint="https://monitor2.example.com",
    ))

    body = captured["body"]
    assert body["displayName"] == "Updated Embedder"
    assert body["description"] == "Updated desc"
    assert body["endpointUrl"] == "https://api.openai.com/v2"
    assert body["apiPath"] == "/new-embeddings"
    assert body["modelIdentifier"] == "text-embedding-3-large"
    assert body["dimensionality"] == 3072
    assert body["maxSequenceLength"] == 16384
    assert body["supportedModalities"] == ["TEXT", "IMAGE"]
    assert body["credentials"]["kind"] == "CREDENTIAL_KIND_API_KEY"
    assert body["replaceLabels"] == {"env": "prod"}
    assert body["version"] == "2.0"
    assert body["monitoringEndpoint"] == "https://monitor2.example.com"


# ---------------------------------------------------------------------------
# LLM create — all params
# ---------------------------------------------------------------------------

def test_create_llm_all_params(mock_client_factory):
    """Every LLMCreationRequest field serializes with correct alias."""
    captured = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["body"] = json.loads(request.content)
        # LlmsAPI.create expects CreateLLMResponse envelope
        return httpx.Response(200, json={"llm": LLM_RESPONSE})

    client = mock_client_factory(handler)
    client.llms.create(
        display_name="All-Params LLM",
        description="Full param test",
        provider_type="OPENAI",
        endpoint_url="https://api.openai.com/v1",
        api_path="/chat/completions",
        model_identifier="gpt-4o",
        supported_modalities=["TEXT", "IMAGE"],
        api_key="sk-llm-test",
        labels={"tier": "premium"},
        version="2024-08",
        monitoring_endpoint="https://monitor.example.com",
        capabilities={
            "supports_chat": True,
            "supports_completion": False,
            "supports_function_calling": True,
            "supports_system_messages": True,
            "supports_streaming": True,
            "supports_sampling_parameters": True,
        },
        default_sampling_params={
            "max_tokens": 4096,
            "temperature": 0.7,
            "top_p": 0.95,
            "top_k": 50,
            "frequency_penalty": 0.1,
            "presence_penalty": 0.2,
            "stop_sequences": ["###", "END"],
        },
        max_context_length=128000,
        client_config={"openai": {"organization": "org-test"}},
        owner_id="owner-llm",
        llm_id="llm-custom-id",
    )

    body = captured["body"]
    assert body["displayName"] == "All-Params LLM"
    assert body["description"] == "Full param test"
    assert body["providerType"] == "OPENAI"
    assert body["endpointUrl"] == "https://api.openai.com/v1"
    assert body["apiPath"] == "/chat/completions"
    assert body["modelIdentifier"] == "gpt-4o"
    assert body["supportedModalities"] == ["TEXT", "IMAGE"]
    assert body["labels"] == {"tier": "premium"}
    assert body["version"] == "2024-08"
    assert body["monitoringEndpoint"] == "https://monitor.example.com"
    assert body["maxContextLength"] == 128000
    assert body["clientConfig"] == {"openai": {"organization": "org-test"}}
    assert body["ownerId"] == "owner-llm"
    assert body["llmId"] == "llm-custom-id"
    # credentials from api_key convenience
    assert body["credentials"]["kind"] == "CREDENTIAL_KIND_API_KEY"
    assert body["credentials"]["apiKey"]["inlineSecret"] == "sk-llm-test"
    # nested capabilities
    caps = body["capabilities"]
    assert caps["supportsChat"] is True
    assert caps["supportsCompletion"] is False
    assert caps["supportsFunctionCalling"] is True
    assert caps["supportsSystemMessages"] is True
    assert caps["supportsStreaming"] is True
    assert caps["supportsSamplingParameters"] is True
    # nested sampling params
    sp = body["defaultSamplingParams"]
    assert sp["maxTokens"] == 4096
    assert sp["temperature"] == 0.7
    assert sp["topP"] == 0.95
    assert sp["topK"] == 50
    assert sp["frequencyPenalty"] == 0.1
    assert sp["presencePenalty"] == 0.2
    assert sp["stopSequences"] == ["###", "END"]


# ---------------------------------------------------------------------------
# LLM update — all params
# ---------------------------------------------------------------------------

def test_update_llm_all_params(mock_client_factory):
    """Every LLMUpdateRequest field serializes with correct alias."""
    captured = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["body"] = json.loads(request.content)
        return httpx.Response(200, json=LLM_RESPONSE)

    client = mock_client_factory(handler)
    client.llms.update(id="llm-123", request=LLMUpdateRequest(
        display_name="Updated LLM",
        description="Updated desc",
        endpoint_url="https://api.openai.com/v2",
        api_path="/v2/chat",
        model_identifier="gpt-4o-mini",
        supported_modalities=["TEXT"],
        credentials={
            "kind": "CREDENTIAL_KIND_API_KEY",
            "api_key": {"inline_secret": "sk-new"},
        },
        version="2025-01",
        monitoring_endpoint="https://mon.example.com",
        capabilities={"supports_chat": True},
        default_sampling_params={"max_tokens": 2048, "temperature": 0.5},
        max_context_length=64000,
        client_config={"openai": {"timeout": "30s"}},
        replace_labels={"env": "staging"},
    ))

    body = captured["body"]
    assert body["displayName"] == "Updated LLM"
    assert body["description"] == "Updated desc"
    assert body["endpointUrl"] == "https://api.openai.com/v2"
    assert body["apiPath"] == "/v2/chat"
    assert body["modelIdentifier"] == "gpt-4o-mini"
    assert body["supportedModalities"] == ["TEXT"]
    assert body["credentials"]["kind"] == "CREDENTIAL_KIND_API_KEY"
    assert body["version"] == "2025-01"
    assert body["monitoringEndpoint"] == "https://mon.example.com"
    assert body["capabilities"]["supportsChat"] is True
    assert body["defaultSamplingParams"]["maxTokens"] == 2048
    assert body["maxContextLength"] == 64000
    assert body["clientConfig"] == {"openai": {"timeout": "30s"}}
    assert body["replaceLabels"] == {"env": "staging"}


# ---------------------------------------------------------------------------
# Reranker create — all params
# ---------------------------------------------------------------------------

def test_create_reranker_all_params(mock_client_factory):
    """Every RerankerCreationRequest field serializes with correct alias."""
    captured = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["body"] = json.loads(request.content)
        return httpx.Response(200, json=RERANKER_RESPONSE)

    client = mock_client_factory(handler)
    client.rerankers.create(
        display_name="All-Params Reranker",
        description="Full param test",
        provider_type="COHERE",
        endpoint_url="https://api.cohere.com",
        api_path="/v2/rerank",
        model_identifier="rerank-v3.5",
        supported_modalities=["TEXT"],
        api_key="sk-reranker-test",
        labels={"use": "search"},
        version="3.5",
        monitoring_endpoint="https://monitor.example.com",
        owner_id="owner-rr",
        reranker_id="rr-custom-id",
    )

    body = captured["body"]
    assert body["displayName"] == "All-Params Reranker"
    assert body["description"] == "Full param test"
    assert body["providerType"] == "COHERE"
    assert body["endpointUrl"] == "https://api.cohere.com"
    assert body["apiPath"] == "/v2/rerank"
    assert body["modelIdentifier"] == "rerank-v3.5"
    assert body["supportedModalities"] == ["TEXT"]
    assert body["labels"] == {"use": "search"}
    assert body["version"] == "3.5"
    assert body["monitoringEndpoint"] == "https://monitor.example.com"
    assert body["ownerId"] == "owner-rr"
    assert body["rerankerId"] == "rr-custom-id"
    # api_key convenience → credentials
    assert body["credentials"]["kind"] == "CREDENTIAL_KIND_API_KEY"
    assert body["credentials"]["apiKey"]["inlineSecret"] == "sk-reranker-test"


# ---------------------------------------------------------------------------
# Reranker update — all params
# ---------------------------------------------------------------------------

def test_update_reranker_all_params(mock_client_factory):
    """Every UpdateRerankerRequest field serializes with correct alias."""
    captured = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["body"] = json.loads(request.content)
        return httpx.Response(200, json=RERANKER_RESPONSE)

    client = mock_client_factory(handler)
    client.rerankers.update(id="rr-123", request=UpdateRerankerRequest(
        display_name="Updated Reranker",
        description="Updated desc",
        endpoint_url="https://api.cohere.com/v2",
        api_path="/rerank-v2",
        model_identifier="rerank-v4",
        supported_modalities=["TEXT"],
        credentials={
            "kind": "CREDENTIAL_KIND_API_KEY",
            "api_key": {"inline_secret": "sk-new"},
        },
        replace_labels={"env": "prod"},
        version="4.0",
        monitoring_endpoint="https://mon.example.com",
    ))

    body = captured["body"]
    assert body["displayName"] == "Updated Reranker"
    assert body["description"] == "Updated desc"
    assert body["endpointUrl"] == "https://api.cohere.com/v2"
    assert body["apiPath"] == "/rerank-v2"
    assert body["modelIdentifier"] == "rerank-v4"
    assert body["supportedModalities"] == ["TEXT"]
    assert body["credentials"]["kind"] == "CREDENTIAL_KIND_API_KEY"
    assert body["replaceLabels"] == {"env": "prod"}
    assert body["version"] == "4.0"
    assert body["monitoringEndpoint"] == "https://mon.example.com"


# ---------------------------------------------------------------------------
# Space create — all params
# ---------------------------------------------------------------------------

def test_create_space_all_params(mock_client_factory):
    """Every SpaceCreationRequest field serializes with correct alias."""
    captured = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["body"] = json.loads(request.content)
        return httpx.Response(200, json=SPACE_RESPONSE)

    client = mock_client_factory(handler)
    client.spaces.create(
        name="All-Params Space",
        labels={"project": "sdk-test"},
        space_embedders=[
            {"embedder_id": "emb-1", "default_retrieval_weight": 0.7},
            {"embedder_id": "emb-2", "default_retrieval_weight": 0.3},
        ],
        public_read=True,
        owner_id="owner-space",
        default_chunking_config={
            "recursive": {
                "chunk_size": 1024,
                "chunk_overlap": 128,
                "keep_strategy": "KEEP_END",
                "length_measurement": "CHARACTER_COUNT",
            },
        },
        space_id="space-custom-id",
    )

    body = captured["body"]
    assert body["name"] == "All-Params Space"
    assert body["labels"] == {"project": "sdk-test"}
    assert body["publicRead"] is True
    assert body["ownerId"] == "owner-space"
    assert body["spaceId"] == "space-custom-id"
    # space_embedders
    se = body["spaceEmbedders"]
    assert len(se) == 2
    assert se[0]["embedderId"] == "emb-1"
    assert se[0]["defaultRetrievalWeight"] == 0.7
    assert se[1]["embedderId"] == "emb-2"
    assert se[1]["defaultRetrievalWeight"] == 0.3
    # chunking config
    cc = body["defaultChunkingConfig"]
    assert cc["recursive"]["chunkSize"] == 1024
    assert cc["recursive"]["chunkOverlap"] == 128
    assert cc["recursive"]["keepStrategy"] == "KEEP_END"
    assert cc["recursive"]["lengthMeasurement"] == "CHARACTER_COUNT"


# ---------------------------------------------------------------------------
# Space update — all params
# ---------------------------------------------------------------------------

def test_update_space_all_params(mock_client_factory):
    """Every UpdateSpaceRequest field serializes with correct alias."""
    captured = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["body"] = json.loads(request.content)
        return httpx.Response(200, json=SPACE_RESPONSE)

    client = mock_client_factory(handler)
    client.spaces.update(id="space-123", request=UpdateSpaceRequest(
        name="Updated Space",
        public_read=False,
        replace_labels={"env": "prod"},
        default_chunking_config={
            "recursive": {
                "chunk_size": 256,
                "chunk_overlap": 32,
                "keep_strategy": "KEEP_START",
                "length_measurement": "CHARACTER_COUNT",
            },
        },
    ))

    body = captured["body"]
    assert body["name"] == "Updated Space"
    assert body["publicRead"] is False
    assert body["replaceLabels"] == {"env": "prod"}
    cc = body["defaultChunkingConfig"]
    assert cc["recursive"]["chunkSize"] == 256
    assert cc["recursive"]["chunkOverlap"] == 32


# ---------------------------------------------------------------------------
# Memory create (text) — all params
# ---------------------------------------------------------------------------

def test_create_memory_text_all_params(mock_client_factory):
    """Every MemoryCreationRequest field serializes with correct alias (text path)."""
    captured = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["body"] = json.loads(request.content)
        return httpx.Response(200, json=MEMORY_RESPONSE)

    client = mock_client_factory(handler)
    client.memories.create(
        space_id="space-123",
        memory_id="mem-custom-id",
        original_content="Hello world, this is test content.",
        content_type="text/plain",
        metadata={"source": "unit-test", "priority": "high"},
        chunking_config={
            "recursive": {
                "chunk_size": 512,
                "chunk_overlap": 64,
                "keep_strategy": "KEEP_END",
                "length_measurement": "CHARACTER_COUNT",
            },
        },
    )

    body = captured["body"]
    assert body["spaceId"] == "space-123"
    assert body["memoryId"] == "mem-custom-id"
    assert body["originalContent"] == "Hello world, this is test content."
    assert body["contentType"] == "text/plain"
    assert body["metadata"] == {"source": "unit-test", "priority": "high"}
    assert body["chunkingConfig"]["recursive"]["chunkSize"] == 512
    assert body["chunkingConfig"]["recursive"]["chunkOverlap"] == 64
    # Should NOT have base64 or ref fields
    assert "originalContentB64" not in body
    assert "originalContentRef" not in body


# ---------------------------------------------------------------------------
# Memory create (base64) — all params
# ---------------------------------------------------------------------------

def test_create_memory_b64_all_params(mock_client_factory):
    """MemoryCreationRequest with base64 content path serializes correctly."""
    captured = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["body"] = json.loads(request.content)
        return httpx.Response(200, json=MEMORY_RESPONSE)

    client = mock_client_factory(handler)
    raw_bytes = b"%PDF-1.4 fake pdf content for testing"
    b64_content = base64.standard_b64encode(raw_bytes).decode("ascii")

    client.memories.create(
        space_id="space-123",
        memory_id="mem-b64-id",
        original_content_b64=b64_content,
        content_type="application/pdf",
        metadata={"filename": "test.pdf"},
    )

    body = captured["body"]
    assert body["spaceId"] == "space-123"
    assert body["memoryId"] == "mem-b64-id"
    assert body["originalContentB64"] == b64_content
    assert body["contentType"] == "application/pdf"
    assert body["metadata"] == {"filename": "test.pdf"}
    assert "originalContent" not in body


# ---------------------------------------------------------------------------
# Retrieve — all RetrieveMemoryRequest params (via convenience args)
# ---------------------------------------------------------------------------

def test_retrieve_all_params():
    """_build_retrieve_body serializes all fields with correct aliases."""
    from goodmem.api.memories import _build_retrieve_body

    body = _build_retrieve_body("What is the meaning of life?", {
        "space_ids": ["s1", "s2"],
        "requested_size": 20,
        "fetch_memory": True,
        "fetch_memory_content": True,
        "llm_id": "llm-1",
        "reranker_id": "rr-1",
        "prompt": "Answer concisely",
        "sys_prompt": "You are a helpful assistant",
        "gen_token_budget": 500,
        "relevance_threshold": 0.5,
        "llm_temp": 0.3,
        "max_results": 10,
        "chronological_resort": True,
    })

    assert body["message"] == "What is the meaning of life?"
    assert body["requestedSize"] == 20
    assert body["fetchMemory"] is True
    assert body["fetchMemoryContent"] is True
    # space_ids → spaceKeys
    assert len(body["spaceKeys"]) == 2
    assert body["spaceKeys"][0]["spaceId"] == "s1"
    assert body["spaceKeys"][1]["spaceId"] == "s2"
    # post-processor convenience args → nested postProcessor
    pp = body["postProcessor"]
    assert pp["config"]["llm_id"] == "llm-1"
    assert pp["config"]["reranker_id"] == "rr-1"
    assert pp["config"]["prompt"] == "Answer concisely"
    assert pp["config"]["sys_prompt"] == "You are a helpful assistant"
    assert pp["config"]["gen_token_budget"] == 500
    assert pp["config"]["relevance_threshold"] == 0.5
    assert pp["config"]["llm_temp"] == 0.3
    assert pp["config"]["max_results"] == 10
    assert pp["config"]["chronological_resort"] is True
