# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Concept

Despite the repo/directory name "Moodia" (and a partly outdated `README.md`), this is **Bubul** — a multi-tool AI SaaS platform built on **Symfony 7.3 / PHP 8.2+**. It bundles several AI "outils" behind one account; users spend **tokens** to run them, on individual (B2C) or company (B2B) plans, with subscriptions, an affiliate (parrainage) program, and gamification on top.

The tools (each is a vertical slice — see Architecture):

| Tool | Code | Purpose |
|------|------|---------|
| Moodia | `moodia` | Generates Moodle course structures from PDFs (the original app) |
| Serenia | `serenia` (`seren_ia`) | Conversational AI assistant with file processing & generation |
| Slidia | `slidia` | Generates PowerPoint/presentations (+ standalone presenter mode) |
| Scormia | `scormia` | Generates SCORM modules, exported as a ZIP with an embedded JS player |
| Castia | `castia` | Turns content into audio podcasts (ElevenLabs TTS + GPT script) |
| Pixia | `pixia` | AI image generation |
| Monalisia | `monalisia` | Infographic generation |
| Promptly | `promptly` | Prompt library — create, share and run AI prompts |
| Sam | `sam` | Moodle learner tracking — **being dissociated from the product and slated for removal; do not extend** |

## Commands

```bash
# Backend (Symfony)
composer install
php bin/console doctrine:migrations:migrate      # apply migrations
php bin/console make:migration                    # generate migration after entity changes
php bin/console cache:clear
symfony serve                                     # or: php -S localhost:8000 -t public/

# Frontend — Webpack Encore (NOT AssetMapper; ignore README's tailwind:build/asset-map:compile)
npm ci
npm run build                                     # production build → public/build/
npm run watch                                     # dev watch
```

### Tests

Tests run with **PHPUnit 12** via `php bin/phpunit`. CI (`.github/workflows/tests.yml`) splits the suite into parallel jobs by directory — mirror that when running locally:

```bash
php bin/phpunit                                   # everything
php bin/phpunit tests/Unit/Service/Slidia/ --testdox   # one tool's unit tests
php bin/phpunit tests/Functional/ --testdox            # functional (needs test DB + built assets)
php bin/phpunit path/to/SomeTest.php --testdox          # single file
php bin/phpunit --filter testMethodName path/to/SomeTest.php   # single test
```

Functional tests require a test DB and compiled assets:
```bash
php bin/console doctrine:database:create --env=test --no-interaction
php bin/console doctrine:schema:create --env=test --no-interaction
php bin/console doctrine:fixtures:load --env=test --no-interaction
npm run build
```
CI runs on PHP 8.3 with **sqlite** for the test DB; production/dev use MySQL/PostgreSQL.

## Architecture

### Per-tool vertical slicing
The codebase is organized **by tool, then by layer**. Each tool gets a subdirectory in most `src/` layers:
`src/Controller/<Tool>/`, `src/Entity/<Tool>/`, `src/Repository/<Tool>/`, `src/Service/<Tool>/`, `src/Config/<Tool>Config.php`, and `templates/<tool>/`. When adding a feature to a tool, stay within its slice and follow the neighboring tool's structure. Controllers are route-prefixed (`#[Route('/scormia')]`) and gated with `#[IsGranted('ROLE_USER')]`.

### Configuration lives in code
`src/Config/*.php` holds **final classes of constants** (`AppConfig`, `AppLimits`, `OpenAIConfig`, `<Tool>Config`, `BadgeConfig`, etc.) — not YAML. Business constants (token cycle days, limits, model names, prompts, themes) go here, not scattered in services. Check `src/Config/` before hardcoding a value.

### The `Tool` entity is the registry
`App\Entity\Tool` (seeded by `src/DataFixtures/ToolFixtures.php`) drives which tools appear in the sidebar/dashboard, their token cost (`minTokens`, `isTokenable`), route names, and visibility rules (`standard` / `admin_only` / `hidden`).

### Tokens are the core currency
`App\Service\Token\TokenService` manages user balances (`getTokenBalance`, `addTokens`, `addCredits`, `setTokenUsable`). `UserTokenDistributionService` + `ContractLifecycleService` handle the 30-day renewal cycle (`AppConfig::TOKEN_RENEWAL_CYCLE_DAYS`); `CompanyTokenService` handles B2B pooled tokens. AI tool actions debit tokens — wire new AI features through these services.

### AI layer
Shared OpenAI plumbing is in `src/Service/Shared/AI/` (`OpenAIHttpClient`, `OpenAIResponseParser`) and streaming in `src/Service/Shared/Streaming/`. Moodia additionally has a legacy `Service/Moodia/OpenAIService.php`; Castia/Moodia use ElevenLabs for TTS. API keys are injected via `config/services.yaml` bindings from env (`OPENAI_API_KEY`, `OPENAI_MODEL`, `ELEVENLABS_API_KEY`, `ANTHROPIC_API_KEY`). Long-running generation (e.g. Scormia) is dispatched through **Symfony Messenger** to dedicated async transports (`scormia_generation`, `stripe_webhook`, `async`) — see `config/packages/messenger.yaml` and `src/Message*/`.

### Security firewalls (config/packages/security.yaml)
Multiple stacked firewalls with **different authenticators per URL prefix** — this is deliberate, read it before touching auth:
- `main` — web session, form login + Google SSO (`GoogleAuthenticator`), remember-me.
- `api_badges`, `api_daily_reward` — session-based but return JSON 401 instead of redirecting.
- `api` (legacy REST) — API-key auth (`ApiKeyAuthenticator`), with a regex **excluding** the session-based `/api/` sub-prefixes.
Roles: `ROLE_ADMIN` → `ROLE_COMPANY_MANAGER` + `ROLE_COMMERCIAL` → `ROLE_USER`.

### Frontend: Stimulus + Turbo + Tailwind v4, bundled by Webpack Encore
JS is **Stimulus 3 controllers** under `assets/controllers/<tool>/` (registered via `assets/controllers.json` / `bootstrap.js`) with **Turbo** for SPA-like navigation. CSS is **Tailwind CSS v4** via PostCSS (`@import "tailwindcss"` in `assets/styles/app.css`) — there is no `tailwind.config.js`. Everything compiles through **Webpack Encore** (`webpack.config.js`) to `public/build/`. Separate Encore entries exist for special surfaces: `slidia_present`, `scormia_player`.

Twig templates follow **atomic design**: `templates/_atoms/`, `_molecules/`, `_organisms/`, `_components/`, plus per-tool folders extending `base.html.twig` (app) or `base.landing.html.twig` (public marketing pages).

### Payments & webhooks
Stripe integration under `src/Service/Payment/` and `StripeWebhookController` (`/webhook/stripe`, HMAC-verified, no session — public in access_control). Webhook events are queued via Messenger to the `stripe_webhook` transport for reliable processing. Plans/subscriptions: `Plan`, `UserSubscription`, `Contract` entities.

## Conventions
- Code comments, commit messages, and domain vocabulary are in **French** (parrainage = referral, commercial = affiliate/sales rep, outils = tools). Match this.
- `config/services.yaml` uses full autowiring/autoconfigure; only services needing explicit env/scalar args are listed there.
- App version is tracked in `config/services.yaml` (`app.version`) and `VERSION`; `CHANGELOG.md` is maintained.
- Migrations directory holds ~190 files — always generate via `make:migration`, never hand-edit schema.
