# -*- coding: utf-8 -*-
"""Location: ./tests/unit/mcpgateway/test_final_coverage_push.py
Copyright 2026
SPDX-License-Identifier: Apache-2.0
Authors: Mihai Criveti

Final push to reach 75% coverage.
"""

# Standard
import json
import tempfile
from unittest.mock import AsyncMock, MagicMock, patch

# Third-Party
from fastapi import HTTPException
import orjson
import pytest

# First-Party
from mcpgateway.common.models import ImageContent, LogLevel, ResourceContent, Role, TextContent
from mcpgateway.schemas import BaseModelWithConfigDict


async def _call_toolops(handler, monkeypatch, **kwargs):
    # First-Party
    import mcpgateway.middleware.rbac as rbac_module
    import cpex.framework as plugins_framework

    class DummyPermissionService:
        def __init__(self, db):
            self.db = db

        async def check_permission(self, **_):
            return True

    monkeypatch.setattr(rbac_module, "PermissionService", DummyPermissionService)
    monkeypatch.setattr(plugins_framework, "get_plugin_manager", AsyncMock(return_value=None))
    return await handler(**kwargs)


def test_role_enum_comprehensive():
    """Test Role enum comprehensively."""
    # Test values
    assert Role.USER.value == "user"
    assert Role.ASSISTANT.value == "assistant"

    # Test enum iteration
    roles = list(Role)
    assert len(roles) == 2
    assert Role.USER in roles
    assert Role.ASSISTANT in roles


def test_log_level_enum_comprehensive():
    """Test LogLevel enum comprehensively."""
    levels = [
        (LogLevel.DEBUG, "debug"),
        (LogLevel.INFO, "info"),
        (LogLevel.NOTICE, "notice"),
        (LogLevel.WARNING, "warning"),
        (LogLevel.ERROR, "error"),
        (LogLevel.CRITICAL, "critical"),
        (LogLevel.ALERT, "alert"),
        (LogLevel.EMERGENCY, "emergency"),
    ]

    for level, expected_value in levels:
        assert level.value == expected_value


def test_content_types():
    """Test content type models."""
    # Standard
    import base64

    # Test TextContent
    text = TextContent(type="text", text="Hello world")
    assert text.type == "text"
    assert text.text == "Hello world"

    # Test ImageContent - now uses base64-encoded string per MCP spec
    image_bytes = b"fake_image_bytes"
    image_data = base64.b64encode(image_bytes).decode("utf-8")
    image = ImageContent(type="image", data=image_data, mime_type="image/png")
    assert image.type == "image"
    assert image.data == image_data
    assert image.mime_type == "image/png"

    # Test ResourceContent
    resource = ResourceContent(type="resource", id="res123", uri="/api/data", mime_type="application/json", text="Sample content")
    assert resource.type == "resource"
    assert resource.uri == "/api/data"
    assert resource.mime_type == "application/json"
    assert resource.text == "Sample content"


def test_content_type_model_form_urlencoded(main_app_with_admin_api):
    """
    Test that the system can parse/accept application/x-www-form-urlencoded.
    """
    # Third-Party
    from fastapi.testclient import TestClient

    client = TestClient(main_app_with_admin_api)
    data = {"type": "text", "text": "Form encoded content"}
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    response = client.post("/admin/tools", data=data, headers=headers)
    assert response.status_code in [200, 201, 400, 401, 415, 422]


def test_base_model_with_config_dict():
    """Test BaseModelWithConfigDict functionality."""

    # Create a simple test model
    class TestModel(BaseModelWithConfigDict):
        name: str
        value: int

    model = TestModel(name="test", value=42)

    # Test to_dict method
    result = model.to_dict()
    assert result["name"] == "test"
    assert result["value"] == 42

    # Test to_dict with alias
    result_alias = model.to_dict(use_alias=True)
    assert isinstance(result_alias, dict)


@pytest.mark.asyncio
async def test_cli_export_import_main_flows():
    """Test CLI export/import main execution flows."""
    # Standard
    import sys

    # First-Party
    from mcpgateway.cli_export_import import main_with_subcommands

    # Test with no subcommands (should fall back to main CLI)
    with patch.object(sys, "argv", ["mcpgateway", "--version"]):
        with patch("mcpgateway.cli.main") as mock_main:
            main_with_subcommands()
            mock_main.assert_called_once()

    # Test with export command but invalid args
    with patch.object(sys, "argv", ["mcpgateway", "export", "--invalid-option"]):
        with pytest.raises(SystemExit):
            main_with_subcommands()


@pytest.mark.asyncio
async def test_export_command_parameter_building():
    """Test export command parameter building logic."""
    # Standard
    import argparse

    # First-Party
    from mcpgateway.cli_export_import import export_command

    # Test with all parameters set
    args = argparse.Namespace(types="tools,gateways", exclude_types="servers", tags="production,api", include_inactive=True, include_dependencies=False, output="test-output.json", verbose=True)

    # Mock the API call to just capture parameters
    with patch("mcpgateway.cli_export_import.make_authenticated_request") as mock_request:
        mock_request.return_value = {"version": "2025-03-26", "entities": {"tools": []}, "metadata": {"entity_counts": {"tools": 0}}}

        with patch("mcpgateway.cli_export_import.Path.mkdir"):
            with patch("pathlib.Path.write_bytes"):  # Changed from builtins.open for asyncio.to_thread
                await export_command(args)

        # Verify API was called with correct parameters
        mock_request.assert_called_once()
        call_args = mock_request.call_args
        params = call_args[1]["params"]

        assert params["types"] == "tools,gateways"
        assert params["exclude_types"] == "servers"
        assert params["tags"] == "production,api"
        assert params["include_inactive"] == "true"
        assert params["include_dependencies"] == "false"


@pytest.mark.asyncio
async def test_import_command_parameter_parsing():
    """Test import command parameter parsing logic."""
    # Standard
    import argparse

    # First-Party
    from mcpgateway.cli_export_import import import_command

    # Create temp file with valid JSON
    with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
        test_data = {"version": "2025-03-26", "entities": {"tools": []}, "metadata": {"entity_counts": {"tools": 0}}}
        json.dump(test_data, f)
        temp_file = f.name

    args = argparse.Namespace(input_file=temp_file, conflict_strategy="update", dry_run=True, rekey_secret="new-secret", include="tools:tool1,tool2;servers:server1", verbose=True)

    # Mock the API call
    with patch("mcpgateway.cli_export_import.make_authenticated_request") as mock_request:
        mock_request.return_value = {"import_id": "test_123", "status": "completed", "progress": {"total": 1, "processed": 1, "created": 1, "failed": 0}, "warnings": [], "errors": []}

        await import_command(args)

        # Verify API was called with correct data
        mock_request.assert_called_once()
        call_args = mock_request.call_args
        request_data = call_args[1]["json_data"]

        assert request_data["conflict_strategy"] == "update"
        assert request_data["dry_run"] == True
        assert request_data["rekey_secret"] == "new-secret"
        assert "selected_entities" in request_data


def test_utils_coverage():
    """Test various utility functions for coverage."""
    # First-Party
    from mcpgateway.utils.create_slug import slugify

    # Test slugify variations
    test_cases = [("Simple Test", "simple-test"), ("API_Gateway", "api-gateway"), ("Multiple   Spaces", "multiple-spaces"), ("", ""), ("123Numbers", "123numbers")]

    for input_text, expected in test_cases:
        result = slugify(input_text)
        assert isinstance(result, str)


def test_config_properties():
    """Test config module properties."""
    # First-Party
    from mcpgateway.config import settings

    # Test basic properties exist
    assert hasattr(settings, "app_name")
    assert hasattr(settings, "host")
    assert hasattr(settings, "port")
    assert hasattr(settings, "database_url")

    # Test computed properties
    api_key = settings.api_key
    assert isinstance(api_key, str)
    assert ":" in api_key  # Should be "user:password" format

    # Test transport support properties
    assert isinstance(settings.supports_http, bool)
    assert isinstance(settings.supports_websocket, bool)
    assert isinstance(settings.supports_sse, bool)


def test_schemas_basic():
    """Test basic schema imports."""
    # First-Party
    from mcpgateway.schemas import ToolCreate

    # Test class exists
    assert ToolCreate is not None


def test_db_utility_functions():
    """Test database utility functions."""
    # Standard
    from datetime import datetime, timezone

    # First-Party
    from mcpgateway.db import utc_now

    # Test utc_now function
    now = utc_now()
    assert isinstance(now, datetime)
    assert now.tzinfo == timezone.utc


def test_validation_imports():
    """Test validation module imports."""
    # First-Party
    from mcpgateway.validation import jsonrpc, tags

    # Test modules can be imported
    assert tags is not None
    assert jsonrpc is not None


def test_services_init():
    """Test services module initialization."""
    # First-Party
    from mcpgateway.services import __init__

    # Just test the module exists
    assert __init__ is not None


def test_cli_module_main_execution():
    """Test CLI module main execution path."""
    # Standard

    # First-Party
    # Test __main__ execution path exists
    from mcpgateway import cli_export_import

    assert hasattr(cli_export_import, "main_with_subcommands")

    # Test module can be executed
    assert cli_export_import.__name__ == "mcpgateway.cli_export_import"


def test_security_logger_threat_score_and_review():
    """Exercise SecurityLogger threat scoring and audit review logic."""
    # First-Party
    from mcpgateway.services.security_logger import SecurityLogger

    logger = SecurityLogger()
    assert logger._calculate_auth_threat_score(True, 0, "basic") == 0.0
    assert logger._calculate_auth_threat_score(False, 0, "basic") == 0.3
    assert logger._calculate_auth_threat_score(False, 3, "basic") == 0.5
    assert logger._calculate_auth_threat_score(False, 5, "basic") == 0.6
    assert logger._calculate_auth_threat_score(False, 10, "basic") == 0.8

    assert logger._requires_audit_review("delete", "resource", "confidential", True) is True
    assert logger._requires_audit_review("update", "role", None, True) is True
    assert logger._requires_audit_review("update", "tool", None, True) is False


def test_security_logger_count_failures(monkeypatch):
    """Count recent failures uses DB session and returns scalar results."""
    # First-Party
    from mcpgateway.services.security_logger import SecurityLogger

    logger = SecurityLogger()

    class DummyResult:
        def scalar(self):
            return 7

    class DummySession:
        def __init__(self):
            self.committed = False
            self.closed = False

        def execute(self, _stmt):
            return DummyResult()

        def commit(self):
            self.committed = True

        def close(self):
            self.closed = True

    dummy = DummySession()
    monkeypatch.setattr("mcpgateway.services.security_logger.SessionLocal", lambda: dummy)

    result = logger._count_recent_failures(user_id="user1")
    assert result == 7
    assert dummy.committed is True
    assert dummy.closed is True


def test_security_logger_data_access_and_errors(monkeypatch):
    """Log data access builds audit trail and security event for sensitive data."""
    # First-Party
    from mcpgateway.services.security_logger import SecurityLogger

    logger = SecurityLogger()
    mock_audit = MagicMock()
    monkeypatch.setattr(logger, "_create_audit_trail", MagicMock(return_value=mock_audit))
    monkeypatch.setattr(logger, "_create_security_event", MagicMock())

    audit = logger.log_data_access(
        action="update",
        resource_type="tool",
        resource_id="tool-1",
        resource_name="Tool",
        user_id="user1",
        user_email="user@example.com",
        team_id=None,
        client_ip="127.0.0.1",
        user_agent="agent",
        success=False,
        data_classification="confidential",
        old_values={"a": 1},
        new_values={"a": 2},
    )

    assert audit is mock_audit
    logger._create_security_event.assert_called_once()


def test_security_logger_create_event_error(monkeypatch):
    """Ensure _create_security_event handles DB errors."""
    # First-Party
    from mcpgateway.services.security_logger import SecurityLogger, SecuritySeverity

    logger = SecurityLogger()

    class DummySession:
        def add(self, _obj):
            raise RuntimeError("boom")

        def commit(self):
            raise AssertionError("commit should not be called")

        def rollback(self):
            self.rolled_back = True

        def close(self):
            self.closed = True

    dummy = DummySession()

    result = logger._create_security_event(
        event_type="authentication_failure",
        severity=SecuritySeverity.HIGH,
        category="auth",
        client_ip="127.0.0.1",
        description="fail",
        threat_score=0.5,
        db=dummy,
    )

    assert result is None


@pytest.mark.asyncio
async def test_toolops_generate_testcases_success(monkeypatch):
    """Exercise toolops test case generation endpoint."""
    # First-Party
    from mcpgateway.routers import toolops_router

    monkeypatch.setattr(
        toolops_router,
        "validation_generate_test_cases",
        AsyncMock(return_value=[{"case": 1}]),
    )

    result = await _call_toolops(
        toolops_router.generate_testcases_for_tool,
        monkeypatch,
        tool_id="tool1",
        number_of_test_cases=1,
        number_of_nl_variations=1,
        mode="generate",
        db=MagicMock(),
        _user={"email": "user@example.com"},
    )

    assert result == [{"case": 1}]


@pytest.mark.asyncio
async def test_toolops_generate_testcases_invalid_json(monkeypatch):
    """Ensure JSON decode errors surface as HTTP 400."""
    # First-Party
    from mcpgateway.routers import toolops_router

    monkeypatch.setattr(
        toolops_router,
        "validation_generate_test_cases",
        AsyncMock(side_effect=orjson.JSONDecodeError("err", "doc", 0)),
    )

    with pytest.raises(HTTPException) as exc:
        await _call_toolops(
            toolops_router.generate_testcases_for_tool,
            monkeypatch,
            tool_id="tool1",
            number_of_test_cases=1,
            number_of_nl_variations=1,
            mode="generate",
            db=MagicMock(),
            _user={"email": "user@example.com"},
        )

    assert exc.value.status_code == 400


@pytest.mark.asyncio
async def test_toolops_execute_nl_testcases_success(monkeypatch):
    """Exercise toolops NL test execution endpoint."""
    # First-Party
    from mcpgateway.routers import toolops_router

    monkeypatch.setattr(
        toolops_router,
        "execute_tool_nl_test_cases",
        AsyncMock(return_value=["ok"]),
    )

    payload = toolops_router.ToolNLTestInput(tool_id="tool1", tool_nl_test_cases=["hello"])
    result = await _call_toolops(
        toolops_router.execute_tool_nl_testcases,
        monkeypatch,
        tool_nl_test_input=payload,
        db=MagicMock(),
        _user={"email": "user@example.com"},
    )

    assert result == ["ok"]


@pytest.mark.asyncio
async def test_toolops_enrich_tool_success(monkeypatch):
    """Exercise tool enrichment endpoint."""
    # First-Party
    from mcpgateway.routers import toolops_router

    tool_schema = MagicMock()
    tool_schema.name = "Tool"
    tool_schema.description = "desc"

    monkeypatch.setattr(
        toolops_router,
        "enrich_tool",
        AsyncMock(return_value=("enriched", tool_schema)),
    )

    result = await _call_toolops(
        toolops_router.enrich_a_tool,
        monkeypatch,
        tool_id="tool1",
        db=MagicMock(),
        _user={"email": "user@example.com"},
    )

    assert result["tool_id"] == "tool1"
    assert result["enriched_desc"] == "enriched"


@pytest.mark.asyncio
async def test_toolops_enrich_tool_error(monkeypatch):
    """Ensure enrich errors surface as HTTP 400."""
    # First-Party
    from mcpgateway.routers import toolops_router

    monkeypatch.setattr(
        toolops_router,
        "enrich_tool",
        AsyncMock(side_effect=Exception("boom")),
    )

    with pytest.raises(HTTPException) as exc:
        await _call_toolops(
            toolops_router.enrich_a_tool,
            monkeypatch,
            tool_id="tool1",
            db=MagicMock(),
            _user={"email": "user@example.com"},
        )

    assert exc.value.status_code == 400
