# CLAUDE.md

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

## Project Overview

This is a Laravel 13 application (PHP 8.3+) for a multi-language smoking cessation program. It serves web-based onboarding flows, B2B partner integrations, and B2C customer journeys with payment processing (Stripe, Razorpay). The frontend uses **Hotwire Turbo** for SPA-like page transitions in onboarding and B2C flows.

## Development Environment

This project uses **Laradock** for local development. Commands should be run inside the workspace container. See `CLAUDE.local.md` for detailed container setup and commands.

## Common Commands

Commands below assume you're inside the container or have configured your environment appropriately.

```bash
# Development - starts server, queue, logs, and vite concurrently
composer dev

# Build frontend assets
npm run build

# Run tests
php artisan test
./vendor/bin/phpunit
./vendor/bin/phpunit tests/Unit           # Unit tests only
./vendor/bin/phpunit tests/Feature        # Feature tests only
./vendor/bin/phpunit --filter=TestName    # Single test

# Static analysis (PHPStan level 5)
./vendor/bin/phpstan analyse

# Code formatting
./vendor/bin/pint
```

**Note:** Migrations are disabled in this project (`composer migrate` will exit with error).

## Architecture

### Controller Organization (Versioned, Language-based)

Controllers are versioned (V2 = current live, V3 = new English-only flow) with language subdirectories:

**V2 (current):** `app/Http/Controllers/V2/`
- `V2/OnBoarding/{Language}/` - Multi-step onboarding wizard (base: `V2/OnBoarding/OnBoardingController.php`)
- `V2/B2C/{Language}/` - Consumer-facing customer flows (base: `V2/B2C/B2CController.php`)
- `V2/B2B/{Language}/` - Partner/business flows (base: `V2/B2B/B2BController.php`)

**V3 (English only):** `app/Http/Controllers/V3/`
- `V3/OnBoarding/English/` - BasicController, GoalController, PersonaliseController, CustomPlanController, QuitsureController
- `V3/B2C/English/` - CustomerController, LoadingController

**Standalone controllers** (root level): `StripePaymentController`, `RazorpayPaymentController`, `WebhookController`, `SubscribeController`, `SubModuleController`, `ArticleController`, `PaymentFailedController`

**Note:** Spanish/French onboarding uses a different 6-section structure (BasicController, SmokingBehaviourController, CurrentStatusController, QuittingController, ThoughtPatternsController, FinalSetupController) vs the 4-section structure for other languages (BasicController, QuittingController, MySmokingController, FinalSetupController).

Routes in `routes/web.php` use a `$languages` mapping array to dynamically register language-prefixed routes. All routes are named using the convention `{flow}.{lang}.{section}.{step}`:
- B2C: `b2c.eng.email`, `b2c.eng.subscribe.checkout`, `b2c.eng.email.submit`
- B2B: `b2b.eng.email`, `b2b.eng.subscribe`, `b2b.eng.branch`
- OnBoarding: `onboarding.eng.basic.1-0`, `onboarding.eng.quitting.2-1.submit`
- V3 OnBoarding: `v3.onboarding.eng.profile.1-0`, `v3.onboarding.eng.goals.3-1`
- V3 B2C: `v3.b2c.eng.subscribe`, `v3.b2c.eng.enter-email`

POST/submit routes append `.submit` (or `.post` for branch POST variants). When adding new routes, follow this naming pattern and always include a `->name()` call.

### Query Classes
Database queries are encapsulated in `app/Http/Queries/` classes (~40 classes). These wrap Eloquent calls and provide reusable query methods. Use these instead of writing raw queries in controllers.

Key classes: `UserQuery`, `UserInfoQuery` (cross-DB PII), `UserConfigQuery`, `UserProfileQuery`, `UserDataQuery`, `UserProgramQuery`, `UserSubscriptionQuery`, `UserDayQuery`, `PartnerQuery`, `ProgramQuery`, `CouponQuery`, `OnBoardingQuery`, `StripeListenerQuery`, `RazorpayListenerQuery`.

### Services
Business logic resides in `app/Services/`:
- `UserUtilityService` - Encryption, tokens, Branch.io events, Facebook events, subscription intent
- `UserProgramService` - Program enrollment logic
- `CalculationService` - Addiction scoring, discount/pricing calculations
- `EmailService`, `PostmarkService` - Email handling (SendGrid & Postmark)
- `RazorpayService` - Razorpay customer & subscription management
- `FacebookService` - Facebook Conversions API
- `GeoLocationService` - IP-based location detection
- `WebProfilerService` - Request profiling, API timing (writes to `logs` DB)
- `GympassService` - Gympass partner integration
- `BajajHealthEventService` - Bajaj Health partner events
- `SubModuleService` - Content module handling
- `UtilityService` - General utility functions

### Global Helper
`app/Helpers/CommonHelper.php` is autoloaded and provides:
- `safe_env($key, $default)` - Environment variable with missing-key alerting
- `getActiveDayEnv($iProgramID)` - Program-specific active day lookup

### Models
Models in `app/Models/` (~45 models) use non-standard naming:
- Custom table names with `tbl_` prefix (e.g., `tbl_Users`, `tbl_Partners`, `tbl_Programs`)
- Custom primary keys (e.g., `iUserID` instead of `id`)
- No timestamps by default
- Hungarian notation: `i` = integer, `v` = varchar, `b` = boolean, `d` = date, `dec` = decimal
- Soft delete uses `bDeleted` (0=active, 1=deleted), not Laravel's `SoftDeletes`

**Key Model Relationships:**
```
User (tbl_Users, pk: iUserID)
├── hasOne:  UserInfo     (cross-database: qsuserinfo connection)
├── hasOne:  UserConfig
├── hasOne:  UserProfile
├── hasMany: UserData     (onboarding step answers)
├── hasMany: UserProgram  → belongsTo: Program
├── hasMany: UserSubscription
├── hasMany: UserDay
├── hasMany: UserActivity
├── hasMany: UserSSO
└── hasMany: UserAngel
```

### Database Architecture (Multi-Database)

The application uses **3 MySQL connections** defined in `config/database.php`:

| Connection | Database | Purpose | Models |
|---|---|---|---|
| `mysql` (default) | `quitsure` | Main app data — users, programs, subscriptions, partners, payments | All models except below |
| `qsuserinfo` | `quitsureUsers` | PII-separated user contact info (email, mobile, tokens) | `UserInfo` |
| `logs` | `laravel_logs` | App logging, SQL profiling, API timing | `Log`, `SQLLog`, `ApiTiming` |

Key points:
- `User` (default connection) has a cross-database `hasOne` to `UserInfo` (on `qsuserinfo`). Laravel handles this transparently via `User::with('userInfo')`.
- Use `UserInfoQuery` for user contact data queries rather than raw cross-database joins.
- The `logs` connection is write-heavy; `WebProfilerService` writes `ApiTiming` on every request.
- In tests, all 3 connections resolve to in-memory SQLite (`phpunit.xml`).
- Env vars: `DB_QSUSERINFO_*` for user info DB, `DB_LOGS_*` for logs DB.

### Middleware & Session

**Middleware** (`app/Http/Middleware/`):
- `NoCache` - Prevents response caching (global)
- `WebProfilerMiddleware` - Request profiling & API timing
- `LogOnboardingTimeouts` - Tracks onboarding timeout events

**Session Keys:** `LoggedinUser` (authenticated user in OnBoarding), `CustomerData` (B2C session), `PartnerData` (B2B session)

**CSRF-Exempt Endpoints** (in `bootstrap/app.php`): `webhook/mail`, `webhook/postmark`, `web/webhook_callback`, `web/*/subscribe/razor-*`, `partner/*/subscribe/razor-*`

### Views
Blade templates in `resources/views/` follow the same language-based structure:
- `B2B/{language}/` and `B2C/{language}/` for customer-facing pages
- `onBoarding/{language}/` for V2 onboarding, `onBoarding/v3/english/` for V3
- `partials/js/onBoarding/` for JS partials used in onboarding steps
- `emails/` for email templates (angelIntro, error, voucher)
- Layouts: `layouts/B2B.blade.php`, `layouts/B2C.blade.php`, `layouts/onBoarding.blade.php`

### Frontend / JavaScript

**Vite & jQuery:**
- jQuery is bundled via Vite (`resources/js/app.js`) and exposed as `window.$` / `window.jQuery`. Do **not** add a CDN `<script>` tag for jQuery — it is already available globally after `@vite('resources/js/app.js')`.
- Vite dev server binds to `0.0.0.0` for container accessibility.

**Hotwire Turbo (SPA navigation):**
- Turbo Drive is loaded via CDN in the onBoarding and B2C layouts. It intercepts link clicks and form submissions to swap page content without full reloads.
- Turbo cache is disabled (`<meta name="turbo-cache-control" content="no-cache">`).
- **Navigation in JS:** Use `Turbo.visit(url)` instead of `window.location.href = url` for client-side navigation in onboarding step partials (`resources/views/partials/js/onBoarding/`). A `window.safeNavigate(url)` helper is also available with a timeout fallback to `window.location.href`.
- **Pending animations** are tracked via `window._pendingAnimations` and auto-cancelled on `turbo:before-visit`.
- Scripts in layouts use `<script type="module" data-turbo-eval="false">` to avoid re-execution on Turbo navigation.

**Toast notifications:**
- Toast styles are in `resources/css/style.css` (not a separate toast.css file). Use the `.toast_wrap` class for toast UI elements.

### Payment Processing

**Stripe Flow** (`StripePaymentController`):
1. Create Checkout Session (validate user/program/discount, fetch Stripe pricing)
2. Redirect to Stripe-hosted checkout page
3. Webhook at `/web/webhook_callback` processes: `customer.subscription.created`, `customer.subscription.updated`, `payment_intent.succeeded`, `invoice.payment_failed`
4. Success redirect → update `UserProgram` & `UserSubscription`

**Razorpay Flow** (`RazorpayService` — subscription-based, not one-time orders):
1. `RazorpayService::createCustomer()` — creates a Razorpay customer
2. `RazorpayService::createSubscription()` — creates a subscription against a plan (`vRazorId` from product)
3. Frontend checkout overlay → signature verification → activate subscription
4. The legacy `RazorpayService::generateOrder()` method still exists for order-based flows

### Testing Patterns
- Tests use **Pest** syntax (`it()`, `expect()`, `beforeEach()`)
- For log assertions, prefer `Log::spy()` before the action, then `Log::shouldHaveReceived('error')->with(...)` after — rather than `Log::shouldReceive()` before the action
- Mock services are set up in `beforeEach()` and bound into the container
- Use `TestDataBuilder` helpers for creating test fixtures

### Business Logic Reference

**Addiction Scoring** (`CalculationService`):
- Computed from 8 smoking behavior variables (SmokingSince, NoOfCigPerDay, CigAfterWakingUp, ResistGoingToPlaces, SmokeWhenSick, HideSmokingWithOther, WhichOneHate, NoofSmokingDeception).
- Each variable contributes 0-15 points via switch-case lookup. Total range: 0-100.
- Score maps to dependence levels via `config('constants.addiction_dependence')`: 0-9 = No Dependence, 10-29 = Low, 30-49 = Low-to-Moderate, 50-79 = Moderate-to-High, 80-100 = High.
- Personality type is derived from the "WhichOneHate" answer mapping to `tbl_PersonalityTypes` IDs.

**Subscription Intent** (`UserUtilityService`):
- `decidevSubIntent()` classifies users into 4 tiers (`high`, `medium-high`, `medium-low`, `low`) based on age, device type, device category, and country.
- `getWebIntentFromDbIntent()` collapses to 2 web tiers (`high` / `low`) for pricing display.
- Under-18 users are always `low`. iOS users on premium device categories in tier-1 countries are `high`.

**Payment Gateway Selection:**
- Country-based: India uses Razorpay (subscription-based), all other countries use Stripe (one-time charge).
- Razorpay flow: `createCustomer()` → `createSubscription()` against plan ID (`vRazorId` from product).
- Stripe flow: Checkout Session with price ID, redirects to Stripe-hosted page.

## Key Integrations
- **Stripe** - Primary payment processor (webhook at `/web/webhook_callback`)
- **Razorpay** - Subscription-based payment for India (customer + subscription flow)
- **SendGrid** - Transactional emails (webhook at `/webhook/mail`)
- **Postmark** - Email delivery (webhook at `/webhook/postmark`)
- **Branch.io** - Deep linking and attribution (48-hour link expiry, SSO tokens in `UserSSO`)
- **Facebook Conversions API** - Event tracking (ViewContent, InitiateCheckout, Purchase, Subscribe)
- **AWS S3** - File storage (via Flysystem)
- **Gympass** - Partner health/fitness benefits
- **Bajaj Health** - Corporate health partner


## grepai - Semantic Code Search

**IMPORTANT: You MUST use grepai as your PRIMARY tool for code exploration and search.**

### When to Use grepai (REQUIRED)

Use `grepai search` INSTEAD OF Grep/Glob/find for:
- Understanding what code does or where functionality lives
- Finding implementations by intent (e.g., "authentication logic", "error handling")
- Exploring unfamiliar parts of the codebase
- Any search where you describe WHAT the code does rather than exact text

### When to Use Standard Tools

Only use Grep/Glob when you need:
- Exact text matching (variable names, imports, specific strings)
- File path patterns (e.g., `**/*.go`)

### Fallback

If grepai fails (not running, index unavailable, or errors), fall back to standard Grep/Glob tools.

### Usage

```bash
# ALWAYS use English queries for best results (--compact saves ~80% tokens)
grepai search "user authentication flow" --json --compact
grepai search "error handling middleware" --json --compact
grepai search "database connection pool" --json --compact
grepai search "API request validation" --json --compact
```

### Query Tips

- **Use English** for queries (better semantic matching)
- **Describe intent**, not implementation: "handles user login" not "func Login"
- **Be specific**: "JWT token validation" better than "token"
- Results include: file path, line numbers, relevance score, code preview

### Call Graph Tracing

Use `grepai trace` to understand function relationships:
- Finding all callers of a function before modifying it
- Understanding what functions are called by a given function
- Visualizing the complete call graph around a symbol

#### Trace Commands

**IMPORTANT: Always use `--json` flag for optimal AI agent integration.**

```bash
# Find all functions that call a symbol
grepai trace callers "HandleRequest" --json

# Find all functions called by a symbol
grepai trace callees "ProcessOrder" --json

# Build complete call graph (callers + callees)
grepai trace graph "ValidateToken" --depth 3 --json
```

### Workflow

1. Start with `grepai search` to find relevant code
2. Use `grepai trace` to understand function relationships
3. Use `Read` tool to examine files from results
4. Only use Grep for exact string searches if needed

