import 'dart:async'; import 'dart:convert'; import 'dart:math'; import 'package:Tripz/constant/colors.dart'; import 'package:Tripz/controller/auth/login_controller.dart'; import 'package:Tripz/controller/functions/add_error.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:Tripz/constant/links.dart'; import 'package:Tripz/constant/style.dart'; import 'package:Tripz/controller/functions/crud.dart'; import '../../constant/box_name.dart'; import '../../main.dart'; import '../../print.dart'; import '../../views/auth/verify_email_page.dart'; class RegisterController extends GetxController { // --- Keys --- final formKey = GlobalKey(); // For main registration form final formKey3 = GlobalKey(); // For OTP verification form // --- Text Controllers --- TextEditingController firstNameController = TextEditingController(); TextEditingController lastNameController = TextEditingController(); TextEditingController emailController = TextEditingController(); TextEditingController phoneController = TextEditingController(); TextEditingController passwordController = TextEditingController(); TextEditingController siteController = TextEditingController(); TextEditingController verifyCode = TextEditingController(); // --- State Management --- bool isLoading = false; bool isOtpSent = false; int remainingTime = 300; // 5 minutes in seconds Timer? _timer; // --- User Data --- String birthDate = 'Birth Date'.tr; String gender = 'Male'.tr; @override void onInit() { super.onInit(); } @override void onClose() { _timer?.cancel(); super.onClose(); } void _startTimer() { _timer?.cancel(); // Cancel any existing timer remainingTime = 300; _timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (remainingTime > 0) { remainingTime--; update(); // Update UI to show countdown } else { timer.cancel(); isOtpSent = false; update(); // Update UI to allow resend } }); } /// STEP 1: Send OTP to the user's phone number via SMS or WhatsApp. /// This function ONLY sends the OTP. It does NOT register the user. Future sendOtpMessage() async { final String phoneNumber = phoneController.text.trim(); if (phoneNumber.isEmpty) { Get.snackbar('Error'.tr, 'Please enter a phone number.'.tr, backgroundColor: AppColor.redColor); return; } // A simple check for general phone number validity (can be improved) if (phoneNumber.length < 10) { Get.snackbar('Error'.tr, 'The phone number seems too short.'.tr, backgroundColor: AppColor.redColor); return; } isLoading = true; update(); try { // Determine if the number is Egyptian to choose the sending method. // International format: +20 followed by 10 digits (total 13 chars) // Local format: 11 digits starting with 010, 011, 012, or 015. final bool isEgyptian = (phoneNumber.startsWith('+20') && phoneNumber.length == 13) || (RegExp(r'^01[0125]\d{8}$').hasMatch(phoneNumber)); final String method = isEgyptian ? 'sms' : 'whatsapp'; final String successMessage = isEgyptian ? 'An OTP has been sent to your phone via SMS.'.tr : 'An OTP has been sent to your phone via WhatsApp.'.tr; Log.print( 'Phone number identified as ${isEgyptian ? "Egyptian" : "International"}. Attempting to send OTP via $method.'); // API call to your backend to send the OTP. // We assume the backend uses the 'method' parameter to decide how to send the OTP. // If your backend uses different endpoints, you can change the 'link' parameter instead. var response = await CRUD().post( link: AppLink.sendVerifyOtpMessage, payload: { 'phone_number': phoneNumber, 'method': method, // 'sms' or 'whatsapp' }, ); Log.print('Send OTP Response: $response'); if (response != 'failure') { isOtpSent = true; _startTimer(); // After successfully sending the OTP, you should navigate to the // OTP verification screen. Example: // Get.to(() => OTPScreen()); Get.snackbar('Success'.tr, successMessage, backgroundColor: AppColor.greenColor); } else { Get.snackbar('Error'.tr, 'Failed to send OTP. Please try again.'.tr, backgroundColor: AppColor.redColor); } } catch (e) { addError(e.toString(), 'passenger send OTP'); Get.snackbar('Error'.tr, 'An unexpected error occurred.'.tr, backgroundColor: Colors.redAccent); } finally { isLoading = false; update(); } } /// STEP 2: Verify the entered OTP and, if successful, register the user. Future verifyOtpAndRegister() async { if (formKey3.currentState?.validate() != true) { Get.snackbar('Error'.tr, "Please enter the verification code.".tr, backgroundColor: AppColor.redColor); return; } isLoading = true; update(); try { // First, verify the OTP with the backend var verificationResponse = await CRUD().post( link: AppLink.verifyOtpPassenger, payload: { 'phone_number': phoneController.text.trim(), 'token': verifyCode.text.trim(), }, ); if (verificationResponse != 'failure') { // --- OTP VERIFICATION SUCCESSFUL --- // Now, proceed with user registration. Log.print("OTP Verified. Proceeding to registration."); await _registerVerifiedUser(); } else { // --- OTP VERIFICATION FAILED --- Get.snackbar('Error'.tr, "Invalid verification code.".tr, backgroundColor: Colors.redAccent); } } catch (e) { addError(e.toString(), 'passenger verify OTP & sign up'); Get.snackbar('Error'.tr, "Something went wrong. Please try again.".tr, backgroundColor: Colors.redAccent); } finally { isLoading = false; update(); } } /// Private helper function to handle the actual registration logic. /// This should ONLY be called after a successful OTP verification. Future _registerVerifiedUser() async { try { box.write(BoxName.phone, (phoneController.text.trim())); var nameParts = (box.read(BoxName.name) ?? "Unknown User").toString().split(' '); var firstName = nameParts.isNotEmpty ? nameParts[0] : 'unknown'; var lastName = nameParts.length > 1 ? nameParts.sublist(1).join(' ') : 'user'; var payload = { 'id': box.read(BoxName.passengerID), 'phone': (phoneController.text.trim()), 'email': box.read(BoxName.email), 'password': 'unknown', // This seems to be by design for social logins 'gender': 'unknown', 'birthdate': '2002-01-01', 'site': box.read(BoxName.passengerPhotoUrl) ?? 'unknown', 'first_name': firstName, 'last_name': lastName, }; var registrationResponse = await CRUD().post( link: AppLink.signUp, payload: payload, ); if (registrationResponse != 'failure') { // --- REGISTRATION SUCCESSFUL --- // This part for multi-server can be kept as is. // Save user state box.write(BoxName.isVerified, '1'); box.write(BoxName.isFirstTime, '0'); // Log the user in Get.put(LoginController()).loginUsingCredentials( box.read(BoxName.passengerID).toString(), box.read(BoxName.email).toString(), ); } else { Get.snackbar( 'Error'.tr, "The email or phone number is already registered.".tr, backgroundColor: Colors.redAccent); } } catch (e) { addError(e.toString(), 'passenger _registerVerifiedUser'); Get.snackbar('Error'.tr, "An error occurred during registration.".tr, backgroundColor: Colors.redAccent); } } // --- Other existing functions (unchanged) --- getBirthDate() { Get.defaultDialog( title: 'Select Date'.tr, titleStyle: AppStyle.title, content: SizedBox( width: 300, child: CalendarDatePicker( initialDate: DateTime.now().subtract(const Duration(days: 14 * 365)), firstDate: DateTime.parse('1940-06-01'), lastDate: DateTime.now().subtract(const Duration(days: 14 * 365)), onDateChanged: (date) { birthDate = date.toString().split(' ')[0]; update(); Get.back(); }, ), ), ); } void changeGender(String value) { gender = value; update(); } /// This is the old registration method, likely for a standard email/password form. /// It seems separate from the OTP flow, so I'm leaving it as is. void registerWithEmailPassword() async { if (formKey.currentState!.validate()) { var res = await CRUD().post(link: AppLink.signUp, payload: { 'first_name': firstNameController.text.toString(), 'last_name': lastNameController.text.toString(), 'email': emailController.text.toString(), 'phone': phoneController.text.toString(), 'password': passwordController.text.toString(), 'gender': 'yet', 'site': siteController.text, 'birthdate': birthDate, }); if (jsonDecode(res)['status'] == 'success') { int randomNumber = Random().nextInt(100000) + 1; await CRUD().post(link: AppLink.sendVerifyEmail, payload: { 'email': emailController.text, 'token': randomNumber.toString(), }); Get.to(() => const VerifyEmailPage()); } } } }