import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import '../../../core/models/marketplace.dart'; import '../models/booking_result.dart'; import '../providers/client_providers.dart'; const _orange = Color(0xFFFF6B00); const _blue = Color(0xFF1A56DB); class BookingScreen extends ConsumerStatefulWidget { final String vehicleId; final MarketplaceVehicle? vehicle; const BookingScreen({ super.key, required this.vehicleId, this.vehicle, }); @override ConsumerState createState() => _BookingScreenState(); } class _BookingScreenState extends ConsumerState { final _formKey = GlobalKey(); final _firstNameCtrl = TextEditingController(); final _lastNameCtrl = TextEditingController(); final _emailCtrl = TextEditingController(); final _phoneCtrl = TextEditingController(); final _licenseCtrl = TextEditingController(); final _notesCtrl = TextEditingController(); bool _loading = false; String? _error; @override void dispose() { _firstNameCtrl.dispose(); _lastNameCtrl.dispose(); _emailCtrl.dispose(); _phoneCtrl.dispose(); _licenseCtrl.dispose(); _notesCtrl.dispose(); super.dispose(); } Future _submit() async { if (!_formKey.currentState!.validate()) return; final params = ref.read(searchParamsProvider); if (!params.hasDateRange) { setState(() => _error = 'Please select rental dates before booking.'); return; } final vehicle = widget.vehicle; if (vehicle == null) { setState(() => _error = 'Vehicle information missing. Please go back and try again.'); return; } setState(() { _loading = true; _error = null; }); try { final locale = Localizations.localeOf(context).languageCode; final lang = ['en', 'fr', 'ar'].contains(locale) ? locale : 'en'; final reservationId = await ref.read(marketplaceServiceProvider).createReservation( vehicleId: vehicle.id, companySlug: vehicle.companySlug, firstName: _firstNameCtrl.text.trim(), lastName: _lastNameCtrl.text.trim(), email: _emailCtrl.text.trim(), phone: _phoneCtrl.text.trim(), driverLicense: _licenseCtrl.text.trim(), startDate: params.startDate!, endDate: params.endDate!, notes: _notesCtrl.text.trim().isEmpty ? null : _notesCtrl.text.trim(), language: lang, ); if (mounted) { context.pushReplacement( '/booking-confirmation', extra: BookingResult( reservationId: reservationId, vehicle: vehicle, startDate: params.startDate!, endDate: params.endDate!, ), ); } } catch (e) { setState(() => _error = _parseError(e)); } finally { if (mounted) setState(() => _loading = false); } } String _parseError(Object e) { final s = e.toString().toLowerCase(); if (s.contains('unavailable') || s.contains('409')) { return 'This vehicle is not available for the selected dates.'; } if (s.contains('socket') || s.contains('connection')) { return 'Could not reach the server. Check your connection and try again.'; } return 'Booking failed. Please check your details and try again.'; } @override Widget build(BuildContext context) { final vehicle = widget.vehicle; final params = ref.watch(searchParamsProvider); final hasDates = params.hasDateRange; final days = hasDates ? params.endDate!.difference(params.startDate!).inDays.clamp(1, 9999) : 0; final fmt = DateFormat('MMM d, yyyy'); final total = vehicle != null && hasDates ? vehicle.dailyRateCents * days / 100 : null; return Scaffold( appBar: AppBar(title: const Text('Complete Booking')), body: Form( key: _formKey, child: ListView( padding: const EdgeInsets.fromLTRB(16, 0, 16, 40), children: [ // ── Vehicle summary ─────────────────────────────────────────── if (vehicle != null) ...[ const SizedBox(height: 16), Card( margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.all(14), child: Row( children: [ const Icon(Icons.directions_car, color: _blue, size: 28), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( vehicle.displayName, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 15), ), Text( vehicle.company.brand.displayName, style: const TextStyle( color: Color(0xFF6B7280), fontSize: 12), ), ], ), ), if (total != null) Text( '${total.toStringAsFixed(0)} MAD', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: _blue, ), ), ], ), ), ), ], // ── Date summary ────────────────────────────────────────────── if (hasDates) ...[ const SizedBox(height: 10), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFFEBF5FF), borderRadius: BorderRadius.circular(10), border: Border.all(color: const Color(0xFFBFDBFE)), ), child: Row( children: [ const Icon(Icons.calendar_today, color: _blue, size: 16), const SizedBox(width: 10), Expanded( child: Text( '${fmt.format(params.startDate!)} → ${fmt.format(params.endDate!)} · $days day${days == 1 ? '' : 's'}', style: const TextStyle( color: _blue, fontWeight: FontWeight.w600, fontSize: 13, ), ), ), ], ), ), ] else ...[ const SizedBox(height: 10), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFFFDF6B2), borderRadius: BorderRadius.circular(10), ), child: const Row( children: [ Icon(Icons.warning_amber, color: Color(0xFFB45309), size: 16), SizedBox(width: 8), Expanded( child: Text( 'No dates selected — go back and pick dates', style: TextStyle( color: Color(0xFF92400E), fontSize: 13), ), ), ], ), ), ], // ── Error ───────────────────────────────────────────────────── if (_error != null) ...[ const SizedBox(height: 12), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFFFDE8E8), borderRadius: BorderRadius.circular(10), ), child: Text(_error!, style: const TextStyle(color: Color(0xFF9B1C1C))), ), ], // ── Your information ────────────────────────────────────────── const SizedBox(height: 24), const _SectionLabel('Your Information'), const SizedBox(height: 14), Row( children: [ Expanded( child: TextFormField( controller: _firstNameCtrl, textCapitalization: TextCapitalization.words, textInputAction: TextInputAction.next, decoration: const InputDecoration(labelText: 'First Name'), validator: _required, ), ), const SizedBox(width: 12), Expanded( child: TextFormField( controller: _lastNameCtrl, textCapitalization: TextCapitalization.words, textInputAction: TextInputAction.next, decoration: const InputDecoration(labelText: 'Last Name'), validator: _required, ), ), ], ), const SizedBox(height: 14), TextFormField( controller: _emailCtrl, keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, decoration: const InputDecoration( labelText: 'Email', prefixIcon: Icon(Icons.email_outlined), ), validator: (v) => (v == null || !v.contains('@')) ? 'Enter a valid email' : null, ), const SizedBox(height: 14), TextFormField( controller: _phoneCtrl, keyboardType: TextInputType.phone, textInputAction: TextInputAction.next, decoration: const InputDecoration( labelText: 'Phone', prefixIcon: Icon(Icons.phone_outlined), ), validator: _required, ), const SizedBox(height: 14), TextFormField( controller: _licenseCtrl, textCapitalization: TextCapitalization.characters, textInputAction: TextInputAction.next, decoration: const InputDecoration( labelText: "Driver's License Number", prefixIcon: Icon(Icons.credit_card_outlined), ), validator: _required, ), const SizedBox(height: 14), TextFormField( controller: _notesCtrl, maxLines: 3, textInputAction: TextInputAction.done, decoration: const InputDecoration( labelText: 'Notes (optional)', prefixIcon: Icon(Icons.notes_outlined), alignLabelWithHint: true, ), ), // ── Info note ───────────────────────────────────────────────── const SizedBox(height: 16), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFFF0F9FF), borderRadius: BorderRadius.circular(10), border: Border.all(color: const Color(0xFFBAE6FD)), ), child: const Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(Icons.info_outline, size: 16, color: Color(0xFF0369A1)), SizedBox(width: 8), Expanded( child: Text( 'Full identity and license details will be collected when you pick up the vehicle.', style: TextStyle( color: Color(0xFF0369A1), fontSize: 12), ), ), ], ), ), // ── Submit ──────────────────────────────────────────────────── const SizedBox(height: 28), ElevatedButton( onPressed: (_loading || !hasDates) ? null : _submit, style: ElevatedButton.styleFrom( backgroundColor: _orange, foregroundColor: Colors.white, minimumSize: const Size.fromHeight(52), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), elevation: 0, ), child: _loading ? const SizedBox( height: 22, width: 22, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : const Text( 'Confirm Booking', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold), ), ), ], ), ), ); } static String? _required(String? v) => v == null || v.trim().isEmpty ? 'Required' : null; } class _SectionLabel extends StatelessWidget { final String text; const _SectionLabel(this.text); @override Widget build(BuildContext context) => Text( text, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 15, color: Color(0xFF374151), ), ); }