"""Tests for lapis_spec.sections.ops."""

from lapis_spec.sections.ops import convert_ops


class TestConvertOps:
    def test_basic_get(self):
        doc = {
            "paths": {
                "/items/{item_id}": {
                    "get": {
                        "operationId": "get_item",
                        "summary": "Get an item by ID.",
                        "parameters": [
                            {
                                "name": "item_id",
                                "in": "path",
                                "required": True,
                                "schema": {"type": "string"},
                            }
                        ],
                        "responses": {
                            "200": {
                                "description": "OK",
                                "content": {
                                    "application/json": {
                                        "schema": {
                                            "type": "object",
                                            "properties": {
                                                "id": {"type": "string"},
                                                "name": {"type": "string"},
                                            },
                                        }
                                    }
                                },
                            }
                        },
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert len(ops) == 1
        op = ops[0]
        assert op.name == "get_item"
        assert op.method == "GET"
        assert op.path == "/items/{item_id}"
        assert op.description == "Get an item by ID."

    def test_post_with_body(self):
        doc = {
            "paths": {
                "/items": {
                    "post": {
                        "operationId": "create_item",
                        "summary": "Create item.",
                        "requestBody": {
                            "required": True,
                            "content": {
                                "application/json": {
                                    "schema": {
                                        "type": "object",
                                        "required": ["name"],
                                        "properties": {
                                            "name": {"type": "string"},
                                            "desc": {"type": "string"},
                                        },
                                    }
                                }
                            },
                        },
                        "responses": {
                            "201": {
                                "description": "Created",
                                "content": {
                                    "application/json": {
                                        "schema": {
                                            "type": "object",
                                            "properties": {
                                                "id": {"type": "string"},
                                            },
                                        }
                                    }
                                },
                            }
                        },
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert len(ops) == 1
        op = ops[0]
        assert op.name == "create_item"
        input_names = [i.name for i in op.inputs]
        assert "name" in input_names
        assert "desc" in input_names
        name_field = next(i for i in op.inputs if i.name == "name")
        assert not name_field.optional
        desc_field = next(i for i in op.inputs if i.name == "desc")
        assert desc_field.optional

    def test_generated_operation_id(self):
        doc = {
            "paths": {
                "/items": {
                    "get": {
                        "summary": "List items.",
                        "responses": {"200": {"description": "OK"}},
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert len(ops) == 1
        assert ops[0].name == "get_items"

    def test_deprecated_modifier(self):
        doc = {
            "paths": {
                "/old": {
                    "get": {
                        "operationId": "old_endpoint",
                        "deprecated": True,
                        "responses": {"200": {"description": "OK"}},
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert "+deprecated" in ops[0].modifiers

    def test_paginated_modifier(self):
        doc = {
            "paths": {
                "/items": {
                    "get": {
                        "operationId": "list_items",
                        "x-paginated": True,
                        "responses": {"200": {"description": "OK"}},
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert "+paginated" in ops[0].modifiers

    def test_idempotent_modifier(self):
        doc = {
            "paths": {
                "/items/{id}/process": {
                    "post": {
                        "operationId": "process_item",
                        "x-idempotent": True,
                        "responses": {"200": {"description": "OK"}},
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert "+idempotent" in ops[0].modifiers

    def test_stream_modifier(self):
        doc = {
            "paths": {
                "/stream": {
                    "get": {
                        "operationId": "stream_data",
                        "x-stream": True,
                        "responses": {"200": {"description": "OK"}},
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert "+stream" in ops[0].modifiers

    def test_since_annotation(self):
        doc = {
            "paths": {
                "/new": {
                    "post": {
                        "operationId": "new_endpoint",
                        "x-since": "2.1",
                        "responses": {"200": {"description": "OK"}},
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert ops[0].since == "2.1"

    def test_output_ref(self):
        doc = {
            "paths": {
                "/items/{id}": {
                    "get": {
                        "operationId": "get_item",
                        "responses": {
                            "200": {
                                "description": "OK",
                                "content": {
                                    "application/json": {
                                        "schema": {
                                            "x-schema-name": "Item",
                                            "type": "object",
                                            "properties": {"id": {"type": "string"}},
                                        }
                                    }
                                },
                            }
                        },
                    }
                }
            }
        }
        ops = convert_ops(doc, {"Item": "Item"})
        assert ops[0].output_ref == "Item"

    def test_delete_path_params_omitted(self):
        doc = {
            "paths": {
                "/items/{id}": {
                    "delete": {
                        "operationId": "delete_item",
                        "summary": "Delete item.",
                        "parameters": [
                            {
                                "name": "id",
                                "in": "path",
                                "required": True,
                                "schema": {"type": "string"},
                            }
                        ],
                        "responses": {"204": {"description": "Deleted"}},
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert len(ops) == 1
        # Path params are implicit from {id} in path, not listed as inputs
        assert ops[0].inputs == []
        assert ops[0].outputs == []
        assert ops[0].output_ref is None

    def test_delete_without_params(self):
        doc = {
            "paths": {
                "/items/all": {
                    "delete": {
                        "operationId": "delete_all_items",
                        "summary": "Delete all items.",
                        "responses": {"204": {"description": "Deleted"}},
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert len(ops) == 1
        assert ops[0].inputs == []
        assert ops[0].outputs == []

    def test_query_param_location(self):
        doc = {
            "paths": {
                "/items": {
                    "get": {
                        "operationId": "list_items",
                        "parameters": [
                            {
                                "name": "search",
                                "in": "query",
                                "schema": {"type": "string"},
                            }
                        ],
                        "responses": {"200": {"description": "OK"}},
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        search = ops[0].inputs[0]
        assert search.name == "search"
        assert search.location is None  # default for GET is query

    def test_header_param_location(self):
        doc = {
            "paths": {
                "/items": {
                    "get": {
                        "operationId": "list_items",
                        "parameters": [
                            {
                                "name": "X-Request-ID",
                                "in": "header",
                                "schema": {"type": "string"},
                            }
                        ],
                        "responses": {"200": {"description": "OK"}},
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert ops[0].inputs[0].location == "header"

    def test_default_value_param(self):
        doc = {
            "paths": {
                "/items": {
                    "get": {
                        "operationId": "list_items",
                        "parameters": [
                            {
                                "name": "sort",
                                "in": "query",
                                "schema": {"type": "string", "default": "name"},
                            }
                        ],
                        "responses": {"200": {"description": "OK"}},
                    }
                }
            }
        }
        ops = convert_ops(doc, {})
        assert ops[0].inputs[0].default == '"name"'

    def test_empty_paths(self):
        doc = {"paths": {}}
        ops = convert_ops(doc, {})
        assert ops == []
