# Copyright 2025 Softwell S.r.l.
# Licensed under the Apache License, Version 2.0

"""Tests for EndpointDiscovery and EndpointInfo."""

from __future__ import annotations

from unittest.mock import AsyncMock

import pytest

from genro_cli.discovery import EndpointDiscovery, EndpointInfo


SAMPLE_SCHEMA = {
    "openapi": "3.1.0",
    "info": {"title": "Test API", "version": "1.0.0"},
    "paths": {
        "/api/users": {
            "get": {
                "summary": "List users",
                "tags": ["users"],
                "parameters": [
                    {"name": "limit", "in": "query", "schema": {"type": "integer"}},
                ],
            },
            "post": {
                "summary": "Create user",
                "tags": ["users"],
                "requestBody": {"content": {"application/json": {}}},
            },
        },
        "/api/health": {
            "get": {"summary": "Health check"},
        },
    },
}


class TestEndpointInfo:

    def test_display_name_with_summary(self) -> None:
        ep = EndpointInfo(path="/api/users", method="GET", summary="List users")
        assert ep.display_name == "GET /api/users — List users"

    def test_display_name_without_summary(self) -> None:
        ep = EndpointInfo(path="/api/users", method="POST")
        assert ep.display_name == "POST /api/users"


class TestEndpointDiscovery:

    def _make_connection(self, schema: dict) -> AsyncMock:
        conn = AsyncMock()
        conn.request = AsyncMock(return_value={"status": 200, "data": schema})
        return conn

    @pytest.mark.asyncio
    async def test_discover(self) -> None:
        conn = self._make_connection(SAMPLE_SCHEMA)
        discovery = EndpointDiscovery(conn)

        endpoints = await discovery.discover()
        assert len(endpoints) == 3

        # Sorted by path then method
        assert endpoints[0].path == "/api/health"
        assert endpoints[0].method == "GET"
        assert endpoints[1].path == "/api/users"
        assert endpoints[1].method == "GET"
        assert endpoints[2].path == "/api/users"
        assert endpoints[2].method == "POST"

    @pytest.mark.asyncio
    async def test_discover_stores_schema(self) -> None:
        conn = self._make_connection(SAMPLE_SCHEMA)
        discovery = EndpointDiscovery(conn)
        await discovery.discover()
        assert discovery.schema is not None
        assert discovery.schema["openapi"] == "3.1.0"

    @pytest.mark.asyncio
    async def test_discover_failure(self) -> None:
        conn = AsyncMock()
        conn.request = AsyncMock(return_value={"status": 500, "data": "error"})
        discovery = EndpointDiscovery(conn)
        with pytest.raises(RuntimeError, match="OpenAPI fetch failed"):
            await discovery.discover()

    @pytest.mark.asyncio
    async def test_search(self) -> None:
        conn = self._make_connection(SAMPLE_SCHEMA)
        discovery = EndpointDiscovery(conn)
        await discovery.discover()

        results = discovery.search("users")
        assert len(results) == 2
        assert all("users" in r.path for r in results)

    @pytest.mark.asyncio
    async def test_search_by_summary(self) -> None:
        conn = self._make_connection(SAMPLE_SCHEMA)
        discovery = EndpointDiscovery(conn)
        await discovery.discover()

        results = discovery.search("health")
        assert len(results) == 1
        assert results[0].summary == "Health check"

    @pytest.mark.asyncio
    async def test_get_endpoint(self) -> None:
        conn = self._make_connection(SAMPLE_SCHEMA)
        discovery = EndpointDiscovery(conn)
        await discovery.discover()

        ep = discovery.get_endpoint("/api/users", "POST")
        assert ep is not None
        assert ep.summary == "Create user"
        assert ep.request_body is not None

    @pytest.mark.asyncio
    async def test_get_endpoint_not_found(self) -> None:
        conn = self._make_connection(SAMPLE_SCHEMA)
        discovery = EndpointDiscovery(conn)
        await discovery.discover()

        assert discovery.get_endpoint("/api/missing") is None

    @pytest.mark.asyncio
    async def test_parameters_parsed(self) -> None:
        conn = self._make_connection(SAMPLE_SCHEMA)
        discovery = EndpointDiscovery(conn)
        await discovery.discover()

        ep = discovery.get_endpoint("/api/users", "GET")
        assert ep is not None
        assert len(ep.parameters) == 1
        assert ep.parameters[0]["name"] == "limit"

    @pytest.mark.asyncio
    async def test_endpoints_property_is_copy(self) -> None:
        conn = self._make_connection(SAMPLE_SCHEMA)
        discovery = EndpointDiscovery(conn)
        await discovery.discover()

        eps = discovery.endpoints
        eps.clear()
        assert len(discovery.endpoints) == 3  # Original unchanged
