# Code Standards

This document defines the code quality standards for the ThrillWiki backend.

## Formatting & Style

### PEP 8 Compliance

All Python code must comply with PEP 8, verified using:
- **black**: Code formatting (line length: 88)
- **flake8**: Style checking (max-line-length: 88, max-complexity: 10)
- **ruff**: Fast linting and import sorting

### Running Formatters

```bash
# Format code
uv run black backend/

# Check style
uv run flake8 backend/ --max-line-length=88 --max-complexity=10

# Lint and fix
uv run ruff check backend/ --fix
```

## Docstring Requirements

### Coverage

- 100% coverage for public classes and methods
- 100% coverage for all functions
- Optional for private methods (but encouraged)

### Style

Follow Google-style docstrings:

```python
def function_name(arg1: Type1, arg2: Type2) -> ReturnType:
    """
    Brief description of what this function does.

    Longer description if needed, explaining the purpose,
    behavior, and any important details.

    Args:
        arg1: Description of arg1
        arg2: Description of arg2

    Returns:
        Description of return value

    Raises:
        ExceptionType: When this exception is raised

    Example:
        >>> function_name("value1", "value2")
        "result"
    """
```

### Class Docstrings

```python
class ClassName:
    """
    Brief description of what this class does.

    Longer description if needed.

    Attributes:
        attr1: Description of attr1
        attr2: Description of attr2

    Example:
        instance = ClassName()
        instance.method()
    """
```

### View Docstrings

Views should include URL patterns and permissions:

```python
class MyView(DetailView):
    """
    Brief description of what this view does.

    View Type: CBV (DetailView)
    URL Pattern: /resource/<slug>/
    Template: app/resource_detail.html
    Permissions: LoginRequired
    """
```

## Complexity Guidelines

### Limits

- **Maximum McCabe complexity**: 10
- **Maximum method length**: 50 lines
- **Maximum nesting depth**: 3 levels

### Checking Complexity

```bash
# Check McCabe complexity
uv run flake8 backend/ --max-complexity=10 --select=C901

# Get complexity metrics
uv run radon cc backend/apps/ -a
```

### Refactoring Strategies

1. **Extract helper methods** for distinct responsibilities:

```python
# Before
def process_data(self, data):
    # Validate data (10 lines)
    # Transform data (10 lines)
    # Save data (10 lines)
    # Send notifications (10 lines)
    pass

# After
def process_data(self, data):
    self._validate_data(data)
    transformed = self._transform_data(data)
    result = self._save_data(transformed)
    self._send_notifications(result)
    return result
```

2. **Use early returns** to reduce nesting:

```python
# Before
def process(self, data):
    if data:
        if data.get('field1'):
            if data.get('field2'):
                return result
    return None

# After
def process(self, data):
    if not data:
        return None
    if not data.get('field1'):
        return None
    if not data.get('field2'):
        return None
    return result
```

3. **Move complex logic to service layer**

## Service Layer Patterns

### Service Method Signature

Always use keyword-only arguments for service methods:

```python
class MyService:
    @staticmethod
    def create_entity(
        *,  # Force keyword-only arguments
        name: str,
        description: str = "",
        created_by: Optional[User] = None,
    ) -> Entity:
        """Create a new entity."""
        pass
```

### Validation Pattern

Always call `full_clean()` before save:

```python
@staticmethod
def create_park(*, name: str, ...) -> Park:
    with transaction.atomic():
        park = Park(name=name, ...)
        park.full_clean()  # Validate before save
        park.save()
        return park
```

## Import Organization

Imports should be organized in this order:
1. Standard library
2. Third-party packages
3. Django imports
4. Local app imports

```python
import logging
from typing import Any, Dict, Optional

from django.contrib.auth import get_user_model
from django.db import transaction
from rest_framework import status

from apps.core.exceptions import ServiceError
from .models import MyModel
```

## Type Hints

Use type hints for all function signatures:

```python
def process_data(
    data: Dict[str, Any],
    user: Optional[User] = None,
) -> ProcessResult:
    """Process data and return result."""
    pass
```

## Testing Requirements

- Maintain or improve test coverage with changes
- Add tests for new service methods
- Add tests for new mixins and base classes
- Run tests before committing:

```bash
pytest backend/tests/ --cov=backend/apps --cov-report=html
```

## Logging Standards

### Architecture Decision

The logging standardization pattern is documented in [ADR-007: Logging Standardization Pattern](../../docs/architecture/adr-007-logging-standardization.md).

### Logger Initialization

Every view and middleware file should initialize a logger:

```python
import logging

logger = logging.getLogger(__name__)
```

### Centralized Logging Utilities

Use the centralized logging utilities from `apps.core.logging` for structured logging:

```python
from apps.core.logging import log_exception, log_business_event, log_security_event
```

### When to Use Each Log Level

- **`logger.debug()`**: Detailed diagnostic information (disabled in production)
- **`logger.info()`**: General operational events (search queries, user actions)
- **`logger.warning()`**: Unexpected conditions that don't prevent operation
- **`logger.error()`**: Error conditions that require attention
- **`log_exception()`**: Exception handling with full stack trace

### Exception Logging

Use `log_exception` for all exception handlers:

```python
try:
    # operation
except Exception as e:
    log_exception(
        logger,
        e,
        context={"operation": "get_filtered_queryset", "filters": filter_params},
        request=self.request,
    )
    messages.error(self.request, f"Error: {str(e)}")
```

### Business Event Logging

Use `log_business_event` for significant business operations:

```python
log_business_event(
    logger,
    event_type="fsm_transition",
    message=f"Park approved: {park.name}",
    context={
        "model": "Park",
        "object_id": park.id,
        "old_state": old_status,
        "new_state": park.status,
    },
    request=request,
)
```

### Security Event Logging

Use `log_security_event` for authentication and security-related events:

```python
log_security_event(
    logger,
    event_type="user_login",
    message=f"User {user.username} logged in successfully",
    severity="low",  # low, medium, high, critical
    context={"user_id": user.id, "username": user.username},
    request=request,
)
```

### What NOT to Log

Never log:
- Passwords or password hashes
- API tokens or secrets
- Session IDs
- Full credit card numbers
- Other sensitive PII

### Log Message Guidelines

- Use clear, concise messages
- Include relevant context (IDs, usernames, operation names)
- Use consistent naming conventions
- Avoid logging large data structures

## Pre-commit Configuration

The following pre-commit hooks are configured:

```yaml
repos:
  - repo: https://github.com/psf/black
    rev: 24.1.0
    hooks:
      - id: black

  - repo: https://github.com/pycqa/flake8
    rev: 7.1.1
    hooks:
      - id: flake8
        args: [--max-line-length=88, --max-complexity=10]

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.12.10
    hooks:
      - id: ruff
        args: [--fix]
```
