"""AP011: Broken Access Control."""

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 AP011BrokenAccessControl(ProSecurityRule):
    """
    AP011: Broken Access Control.

    Detects access control issues including:
    - ID-based operations without authorization checks
    - Admin/privileged endpoints without role requirements
    - User resource endpoints without ownership validation
    """

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

    @property
    def name(self) -> str:
        return "Broken Access Control"

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

    @property
    def description(self) -> str:
        return (
            "Detects broken access control patterns including missing authorization "
            "on ID-based operations, privilege escalation risks, and IDOR vulnerabilities "
            "(OWASP A01:2021)"
        )

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

        # Check for ID-based operations (potential IDOR)
        has_id_param = any(
            param in route_lower
            for param in ["{id}", "<id>", "{user_id}", "<user_id>", "{pk}", "<pk>"]
        )

        resource_keywords = ["user", "account", "profile", "order", "payment", "invoice"]
        is_user_resource = any(keyword in route_lower for keyword in resource_keywords)

        if has_id_param and is_user_resource and endpoint.is_write_endpoint:
            # Check if there's role/permission checking
            has_permission_check = bool(
                endpoint.authorization.roles
                or endpoint.authorization.permissions
                or endpoint.authorization.policies
            )

            if not has_permission_check and endpoint.authorization.requires_auth:
                yield self.create_finding(
                    endpoint,
                    message=(
                        f"ID-based endpoint '{endpoint.full_route}' may be vulnerable to IDOR. "
                        "Authenticated users can modify resources without ownership validation."
                    ),
                    recommendation=(
                        "Implement ownership validation. Verify that the authenticated user "
                        "owns or has permission to access the requested resource. "
                        "Check user.id == resource.owner_id before allowing modifications."
                    ),
                    severity=Severity.CRITICAL,
                )

        # Check for admin/privileged endpoints without role requirements
        admin_keywords = ["admin", "manage", "delete", "remove", "ban", "suspend", "moderate"]
        is_admin_endpoint = any(keyword in route_lower or keyword in func_name for keyword in admin_keywords)

        if is_admin_endpoint:
            has_role_check = bool(endpoint.authorization.roles or endpoint.authorization.policies)

            if not has_role_check:
                yield self.create_finding(
                    endpoint,
                    message=(
                        f"Administrative endpoint '{endpoint.full_route}' "
                        "lacks role-based access control"
                    ),
                    recommendation=(
                        "Implement role-based access control. Require specific roles "
                        "(e.g., @require_role('admin'), Depends(require_admin), "
                        "IsAdminUser permission class) for administrative operations."
                    ),
                    severity=Severity.CRITICAL,
                )

        # Check for bulk operations without authorization
        bulk_keywords = ["bulk", "batch", "all", "list"]
        is_bulk_operation = any(keyword in func_name for keyword in bulk_keywords)

        if is_bulk_operation and endpoint.is_write_endpoint:
            if not endpoint.authorization.has_specific_requirements:
                yield self.create_finding(
                    endpoint,
                    message=(
                        f"Bulk operation endpoint '{endpoint.full_route}' "
                        "lacks specific authorization requirements"
                    ),
                    recommendation=(
                        "Bulk operations should have strict authorization. "
                        "Require elevated permissions or implement per-item authorization checks."
                    ),
                    severity=Severity.HIGH,
                )

        # Check for direct object access patterns
        direct_access_keywords = ["by_id", "get_by", "find_by"]
        is_direct_access = any(keyword in func_name for keyword in direct_access_keywords)

        if is_direct_access and not endpoint.authorization.has_specific_requirements:
            yield self.create_finding(
                endpoint,
                message=(
                    f"Direct object access endpoint '{endpoint.full_route}' "
                    "may expose unauthorized data"
                ),
                recommendation=(
                    "Implement authorization checks for direct object access. "
                    "Filter results by user ownership or permissions before returning data."
                ),
                severity=Severity.MEDIUM,
            )

        # Check for elevation/privilege endpoints
        elevation_keywords = ["promote", "elevate", "grant", "assign_role", "permission"]
        is_elevation_endpoint = any(keyword in func_name for keyword in elevation_keywords)

        if is_elevation_endpoint:
            if not endpoint.authorization.roles:
                yield self.create_finding(
                    endpoint,
                    message=(
                        f"Privilege elevation endpoint '{endpoint.full_route}' "
                        "lacks role-based protection"
                    ),
                    recommendation=(
                        "Privilege elevation operations must require admin/superuser roles. "
                        "Implement strict role checks to prevent privilege escalation attacks."
                    ),
                    severity=Severity.CRITICAL,
                )
