# Phase 4: Reservation System

## Objective
Implement the full reservation lifecycle for hotel manager (CRUD + status management + room assignment) and customer (browse, book, pay, cancel).

---

## Tasks

### 4.1 Filament — ReservationsRelationManager

**File:** `app/Filament/Resources/HotelResource/RelationManagers/ReservationsRelationManager.php`

CRUD for reservations with:
- Form: user (searchable select), room_type, check_in, check_out, adults, children, special_requests, status, source
- Table: guest name, room type, check_in, check_out, nights, status, source, total_price
- Filters: status, date range, source
- Actions: Confirm, Check-in, Check-out, Cancel, No-show (bulk + individual)

### 4.2 Actions

**Directory:** `app/Actions/HotelManager/`

#### CreateHotelReservationAction
```php
final class CreateHotelReservationAction
{
    /**
     * @throws NoRoomAvailableException
     * @throws InvalidOccupancyException
     * @throws MinStayNotMetException
     */
    public function __invoke(
        Hotel $hotel,
        User $guest,
        HotelRoomType $roomType,
        CarbonImmutable $checkIn,
        CarbonImmutable $checkOut,
        int $adults,
        int $children = 0,
        ?string $specialRequests = null,
        HotelReservationSource $source = HotelReservationSource::DIRECT_APP,
    ): HotelReservation;
}
```

Logic:
1. Validate occupancy against room type limits
2. Check availability via HotelAvailabilityService
3. Validate min/max stay from applicable rate
4. Calculate total price via HotelRateService
5. Create HotelReservation with status PENDING (or CONFIRMED if auto_confirm)
6. Return reservation

#### UpdateReservationStatusAction
```php
final class UpdateReservationStatusAction
{
    /**
     * @throws InvalidStatusTransitionException
     */
    public function __invoke(
        HotelReservation $reservation,
        HotelReservationStatus $newStatus,
        ?string $reason = null,
    ): HotelReservation;
}
```

Valid transitions:
- PENDING → CONFIRMED, CANCELLED
- CONFIRMED → CHECKED_IN, CANCELLED, NO_SHOW
- CHECKED_IN → CHECKED_OUT
- CHECKED_OUT → (terminal)
- CANCELLED → (terminal)
- NO_SHOW → (terminal)

#### AssignRoomsAction
```php
final class AssignRoomsAction
{
    /**
     * @throws RoomNotAvailableException
     * @throws RoomTypeMismatchException
     */
    public function __invoke(
        HotelReservation $reservation,
        array $roomIds,
    ): HotelReservation;
}
```

Logic:
1. Validate rooms belong to same hotel
2. Validate rooms match reservation's room type
3. Validate rooms are available for the dates
4. Sync pivot table
5. Update room status to RESERVED (if not yet checked in) or OCCUPIED (if checked in)

### 4.3 API — Manager Reservation Endpoints

#### ListReservationsController
```
GET /v1/places/{place}/hotels/{hotel}/reservations
Query: status, check_in_from, check_in_to, source, search (guest name/email), page, size
```

#### ShowReservationController
```
GET /v1/places/{place}/hotels/{hotel}/reservations/{reservation}
```
Returns reservation with: guest info, room type, assigned rooms, folio summary, extras.

#### CreateReservationController
```
POST /v1/places/{place}/hotels/{hotel}/reservations
Body: room_type_id, check_in, check_out, adults, children, special_requests, customer_id (optional — for walk-in with existing customer)
```

#### UpdateReservationStatusController
```
PATCH /v1/places/{place}/hotels/{hotel}/reservations/{reservation}/status
Body: status, reason (optional, for cancellation)
```

#### AssignRoomsController
```
POST /v1/places/{place}/hotels/{hotel}/reservations/{reservation}/rooms
Body: room_ids (array of room public_ids)
```

#### ShowFolioController
```
GET /v1/places/{place}/hotels/{hotel}/reservations/{reservation}/folio
```
Returns: room charges breakdown (nightly rates), extras charges, total, amount paid, balance due.

### 4.4 API — Customer Reservation Endpoints

#### CreateCustomerReservationController
```
POST /v1/places/{place}/hotels/{hotel}/reservations
Body: room_type_id, check_in, check_out, adults, children, special_requests
```
Creates reservation for authenticated customer. Source = DIRECT_APP.

#### PayReservationController
```
POST /v1/places/{place}/hotels/{hotel}/reservations/{reservation}/pay
Body: payment_method (stripe/wallet)
```
Creates Order + OrderItem for room charge prepayment.

#### ListMyHotelReservationsController
```
GET /v1/places/{place}/hotels/reservations
Query: status, upcoming (boolean)
```
Returns authenticated user's hotel reservations across all hotels at this place.

#### ShowMyReservationController
```
GET /v1/places/{place}/hotels/{hotel}/reservations/{reservation}
```

#### CancelReservationController
```
DELETE /v1/places/{place}/hotels/{hotel}/reservations/{reservation}
```
Delegates to `CancelReservationAction` (defined in Phase 5). Applies cancellation policy: calculates penalty based on hotel's `free_cancellation_hours` and `cancellation_penalty_percentage`. Handles refund for prepaid reservations. Sets status to CANCELLED.

### 4.5 Orderable Implementation

Implement Orderable interface on HotelReservation:
- `orderableMoneyPrice()` — returns `total_price`
- `orderableCreditsPrice()` — credits equivalent
- `orderableCreditsTaxes()` — tax amount
- `orderableConstraints()` — validate check_in/check_out, availability

### 4.6 Resources

- `HotelReservationResource` — Full reservation detail for manager
- `HotelReservationSummaryResource` — Compact for lists
- `HotelFolioResource` — Folio breakdown (room charges + extras)
- `CustomerHotelReservationResource` — Customer-facing reservation

### 4.7 Form Requests

- `CreateReservationRequest` — room_type_id, check_in, check_out, adults, children
- `UpdateReservationStatusRequest` — status (enum), reason (nullable)
- `AssignRoomsRequest` — room_ids (array)

### 4.8 Exceptions

- `NoRoomAvailableException` — No rooms available for dates
- `InvalidOccupancyException` — Adults/children exceed room type limit
- `MinStayNotMetException` — Stay shorter than minimum
- `InvalidStatusTransitionException` — Invalid reservation status change
- `RoomNotAvailableException` — Room already assigned
- `RoomTypeMismatchException` — Room doesn't match reservation room type

### 4.9 Tests

**File:** `tests/Feature/Controllers/HotelManager/ReservationsTest.php`

- [ ] Manager can list reservations with filters
- [ ] Manager can view reservation detail with folio
- [ ] Manager can create walk-in reservation
- [ ] Manager can update reservation status (all valid transitions)
- [ ] Manager gets error on invalid status transition
- [ ] Manager can assign rooms to reservation
- [ ] Manager gets error assigning unavailable room
- [ ] Customer can create reservation
- [ ] Customer can pay for reservation (Stripe + Wallet)
- [ ] Customer can list their reservations
- [ ] Customer can cancel reservation
- [ ] Customer cannot cancel another user's reservation
- [ ] Availability decreases after reservation created
- [ ] Folio shows correct room charges and extras

---

## Acceptance Criteria

- [ ] Filament reservations CRUD with status actions
- [ ] All 6 manager API endpoints functional
- [ ] All 5 customer API endpoints functional
- [ ] Status transitions validated correctly
- [ ] Room assignment works with validation
- [ ] Orderable interface implementation correct
- [ ] Folio calculation accurate (room charges + extras)
- [ ] All exceptions have proper error codes
- [ ] All Swagger annotations present
- [ ] All tests pass
- [ ] Pint + PHPStan pass on new/modified files
