# Copilot Instructions for Studio EinsZwoVier Web Application

## Project Overview
This is a **FastAPI-based web application** serving as a multi-service hub for Studio EinsZwoVier, a collaborative print studio. The main app provides a PDF print cost calculator with automated ink coverage analysis, integrated with a Matrix server for order collection and payment tracking.

## Architecture & Service Boundaries

### Core FastAPI Application (`main.py`, port 80→8000)
- **Landing page** (`/`) with presence state management (studio open/closed indicator)
- **Cost calculator** (`/cost`) for PDF analysis and price estimation
- **About page** (`/about`)
- **Order submission** via Matrix integration

### Multi-Container Stack (Docker Compose)
The application orchestrates 7+ containerized services:
- **web**: FastAPI app (this repo's code)
- **synapse**: Matrix server for print job collection (port 8008)
- **ollama**: Local LLM inference (port 11434)
- **open-webui**: LLM chat interface (port 8080)
- **bookstack + mariadb**: Documentation wiki
- **docmost + postgres + redis**: Knowledge management system
- **watchtower**: Auto-updates all containers every 24h

### PDF Processing Pipeline (`cost_calculator.py`)
1. **Upload** → Validate `.pdf` extension only
2. **Page-by-page analysis**:
   - Extract dimensions from `CropBox` (preferred) or `MediaBox`
   - Convert PDF points to meters: `(points / 72.0) * 0.0254`
   - Render at 150 DPI using `pdf2image`
   - Calculate ink coverage: pixels with RGB < 250 = ink
   - Detect color vs B&W: HSV saturation analysis (>0.1% pixels with sat>10 = color)
3. **Cost calculation**: area (m²) × rate (from env vars `RATE_PER_M2_BLACK`/`COLOR`)

### Matrix Integration (`mailer.py`)
- Uses `matrix-nio` async client to send orders
- **Critical**: Must set `MATRIX_USER`, `MATRIX_PASS`, `MATRIX_HOMESERVER` env vars
- Sends structured summary (German language) + PDF upload to hardcoded room ID
- **Synchronous wrapper** `send_order_sync()` bridges async/sync contexts for FastAPI

## Developer Workflows

### Local Development
```bash
# Option 1: Direct Python execution (auto-reload)
python run_app.py

# Option 2: Full stack with Docker Compose
docker-compose up --build -d

# Access at: http://localhost (or einszwovier.local in local network)
```

### Environment Configuration
Create `.env` file with:
```bash
# Print rates (€/m²)
RATE_PER_M2_BLACK=4.0
RATE_PER_M2_COLOR=5.0

# Matrix integration
MATRIX_USER=@bot:homeserver
MATRIX_PASS=secret
MATRIX_HOMESERVER=http://einszwovier.local:8008

# Service ports
SYNAPSE_PORT=8008
OLLAMA_PORT=11434
OPENWEBUI_PORT=8080
BOOKSTACK_PORT=6875

# BookStack/DB configs (see docker-compose.yml for full list)
```

Note: this repository also contains a local `.env` with example credentials (do NOT commit real secrets). Current repository `.env` contents:
```properties
MATRIX_USER=einszwovier_bot
MATRIX_PASS=einszwo4
```
Prefer injecting secrets via CI environment variables or a secure vault for production. Add `MATRIX_ROOM` and/or `MATRIX_HOMESERVER` when testing Matrix flows.

### Debugging Matrix Integration
Use `get_room_id.py` to discover Matrix room IDs:
```python
# Connects to local Matrix server, joins room, prints room IDs
python get_room_id.py
```
Then update the hardcoded room ID in `main.py` line 127: `room_id="!eFW..."`

## Project-Specific Conventions

### Presence State Management
- In-memory global `presence_state` dict tracks studio open/closed status
- API endpoints: `GET /presence`, `POST /presence`, `POST /presence/toggle`
- Background task `update_presence_periodically()` runs every 5 min (currently no-op placeholder)

### German UI/UX
- Templates use German language exclusively
- Error/success messages in German (e.g., "Dein Auftrag wurde erfolgreich gesendet!")
- Cost calculator displays "S/W" (Schwarz/Weiß) vs "Farbe" (color)

### File Organization
- **Uploads persist** in `data/uploads/` (mounted volume in Docker)
- **Templates** in `templates/` (Jinja2, extending `base.html`)
- **Static assets** in `static/` (CSS, fonts, images)
- **Service data** in dedicated dirs: `bookstack/`, `docmost/`, `matrix/`, `ollama/`, `open-webui/`

### Docker Production Command
The `Dockerfile` uses **Gunicorn with Uvicorn workers** for production:
```bash
gunicorn main:app -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000 --workers 4 --timeout 120
```
But `docker-compose.yml` overrides with `--reload` for dev convenience.

## Critical Integration Points

### PDF Analysis Return Structure
`analyze_pdf()` returns:
```python
{
    "filename": str,
    "pages": [{"page": int, "width_m": float, "height_m": float,
               "area_m2": float, "ink_pct": float, "is_color": bool, "cost": float}],
    "total_area_black": float,
    "total_area_color": float,
    "total_cost_black": float,
    "total_cost_color": float,
    "grand_total": float
}
```

### Matrix Room ID Hardcoding
⚠️ **Known technical debt**: Room ID is hardcoded in `send_order_endpoint()`. Update when Matrix server changes or for different deployment environments.

### Async/Sync Bridge Pattern
Matrix client is async, but FastAPI endpoint is sync. Pattern used:
```python
def send_order_sync(...):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(send_order(...))
    loop.close()
```

## External Dependencies
- **Poppler** (system): Required for `pdf2image` (installed in Dockerfile)
- **OpenCV** (system): Requires `libgl1`, `libglib2.0-0` (installed in Dockerfile)
- **Font files**: Custom fonts in `static/fonts/` (BauPro, HealTheWebA/B, SISTEMAS)

## Testing & Utilities
- `test_send_pdf.py`: Manual test script for Matrix integration
- No automated test suite currently implemented
