"""AP012: Security Misconfiguration."""

from collections.abc import Iterator

from apiposture.core.models.endpoint import Endpoint
from apiposture.core.models.enums import Severity
from apiposture.core.models.finding import Finding

from apiposture_pro.rules.base import ProSecurityRule


class AP012SecurityMisconfiguration(ProSecurityRule):
    """
    AP012: Security Misconfiguration.

    Detects security misconfigurations including:
    - Debug/test endpoints in production code
    - Endpoints exposing system information
    - Development-only endpoints without proper protection
    """

    @property
    def rule_id(self) -> str:
        return "AP012"

    @property
    def name(self) -> str:
        return "Security Misconfiguration"

    @property
    def severity(self) -> Severity:
        return Severity.HIGH

    @property
    def description(self) -> str:
        return (
            "Detects security misconfigurations including debug endpoints, "
            "information disclosure, and development-only features (OWASP A05:2021)"
        )

    def evaluate(self, endpoint: Endpoint) -> Iterator[Finding]:
        """Evaluate endpoint for security misconfigurations."""
        route = endpoint.route
        func_name = endpoint.function_name.lower()
        route_lower = route.lower()

        # Check for debug/test endpoints
        debug_keywords = ["debug", "test", "dev", "staging", "sandbox", "playground"]
        is_debug_endpoint = any(
            keyword in route_lower or keyword in func_name for keyword in debug_keywords
        )

        if is_debug_endpoint and endpoint.authorization.is_public:
            yield self.create_finding(
                endpoint,
                message=(
                    f"Debug/test endpoint '{endpoint.full_route}' is publicly accessible"
                ),
                recommendation=(
                    "Remove debug endpoints from production or protect them with authentication. "
                    "Use environment checks to disable debug features in production. "
                    "Consider using DEBUG=False checks or @require_debug decorators."
                ),
                severity=Severity.HIGH,
            )

        # Check for info disclosure endpoints
        info_keywords = ["info", "status", "health", "metrics", "stats", "version", "config"]
        is_info_endpoint = any(keyword in route_lower or keyword in func_name for keyword in info_keywords)

        if is_info_endpoint:
            # Health checks should be protected or limited
            if "health" in route_lower or "status" in route_lower:
                if endpoint.authorization.is_public:
                    yield self.create_finding(
                        endpoint,
                        message=(
                            f"Status/health endpoint '{endpoint.full_route}' "
                            "exposes system information publicly"
                        ),
                        recommendation=(
                            "Limit health check information exposure. Return minimal data "
                            "for public endpoints. Detailed system metrics should require authentication."
                        ),
                        severity=Severity.LOW,
                    )

            # Config/version endpoints should not be public
            sensitive_info = ["config", "version", "env", "environment"]
            is_sensitive_info = any(keyword in route_lower for keyword in sensitive_info)

            if is_sensitive_info and endpoint.authorization.is_public:
                yield self.create_finding(
                    endpoint,
                    message=(
                        f"Configuration endpoint '{endpoint.full_route}' "
                        "exposes sensitive system information"
                    ),
                    recommendation=(
                        "Configuration and version endpoints should require authentication. "
                        "Never expose environment variables, secrets, or detailed system "
                        "configuration to unauthenticated users."
                    ),
                    severity=Severity.MEDIUM,
                )

        # Check for database/cache management endpoints
        admin_keywords = ["flush", "clear", "reset", "migrate", "seed", "dump", "backup"]
        is_admin_operation = any(keyword in func_name for keyword in admin_keywords)

        if is_admin_operation:
            if not endpoint.authorization.roles:
                yield self.create_finding(
                    endpoint,
                    message=(
                        f"Administrative operation '{endpoint.full_route}' "
                        "lacks proper role-based protection"
                    ),
                    recommendation=(
                        "Database/cache management operations should require admin roles. "
                        "These operations can cause data loss or service disruption."
                    ),
                    severity=Severity.CRITICAL,
                )

        # Check for error/exception endpoints
        error_keywords = ["error", "exception", "traceback", "crash", "fault"]
        is_error_endpoint = any(keyword in route_lower or keyword in func_name for keyword in error_keywords)

        if is_error_endpoint and endpoint.authorization.is_public:
            yield self.create_finding(
                endpoint,
                message=(
                    f"Error/exception endpoint '{endpoint.full_route}' "
                    "may expose stack traces or debug information"
                ),
                recommendation=(
                    "Error endpoints should not expose detailed stack traces in production. "
                    "Log errors server-side and return generic error messages to clients."
                ),
                severity=Severity.MEDIUM,
            )

        # Check for documentation endpoints
        docs_keywords = ["docs", "swagger", "openapi", "redoc", "api-docs", "documentation"]
        is_docs_endpoint = any(keyword in route_lower for keyword in docs_keywords)

        if is_docs_endpoint and endpoint.authorization.is_public:
            yield self.create_finding(
                endpoint,
                message=(
                    f"API documentation endpoint '{endpoint.full_route}' "
                    "is publicly accessible"
                ),
                recommendation=(
                    "Consider requiring authentication for API documentation in production. "
                    "Documentation can reveal API structure and attack surface. "
                    "At minimum, disable automatic documentation generation in production."
                ),
                severity=Severity.LOW,
            )

        # Check for file upload endpoints without restrictions
        upload_keywords = ["upload", "file", "attachment", "media"]
        is_upload_endpoint = any(keyword in route_lower or keyword in func_name for keyword in upload_keywords)

        if is_upload_endpoint and endpoint.is_write_endpoint:
            if endpoint.authorization.is_public:
                yield self.create_finding(
                    endpoint,
                    message=(
                        f"File upload endpoint '{endpoint.full_route}' "
                        "accepts uploads from unauthenticated users"
                    ),
                    recommendation=(
                        "Require authentication for file uploads. Validate file types, "
                        "sizes, and content. Store uploads outside the web root and "
                        "scan for malware."
                    ),
                    severity=Severity.HIGH,
                )
