import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.dart'; import 'package:kmobile/api/services/beneficiary_service.dart'; import 'package:kmobile/data/models/beneficiary.dart'; import 'beneficiary_result_page.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; import '../../../di/injection.dart'; import '../../../l10n/app_localizations.dart'; import 'package:kmobile/features/fund_transfer/screens/transaction_pin_screen.dart'; class AddBeneficiaryScreen extends StatefulWidget { final String customerName; const AddBeneficiaryScreen({ super.key, required this.customerName, }); @override State createState() => _AddBeneficiaryScreen(); } class _AddBeneficiaryScreen extends State { final _formKey = GlobalKey(); final TextEditingController accountNumberController = TextEditingController(); final TextEditingController confirmAccountNumberController = TextEditingController(); final TextEditingController nameController = TextEditingController(); final TextEditingController bankNameController = TextEditingController(); final TextEditingController branchNameController = TextEditingController(); final TextEditingController ifscController = TextEditingController(); final TextEditingController phoneController = TextEditingController(); final service = getIt(); bool _isValidating = false; bool _isBeneficiaryValidated = false; // ignore: unused_field String? _validationError; late String accountType; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { accountType = AppLocalizations.of(context).savings; }); }); } void _validateIFSC() async { var beneficiaryService = getIt(); final ifsc = ifscController.text.trim().toUpperCase(); if (ifsc.isEmpty) return; final result = await beneficiaryService.validateIFSC(ifsc); if (mounted) { if (result.bankName == '') { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(AppLocalizations.of(context).invalidIfsc)), ); bankNameController.clear(); branchNameController.clear(); } else { bankNameController.text = result.bankName; branchNameController.text = result.branchName; } } } void _validateBeneficiary() async { FocusScope.of(context).unfocus(); setState(() { _isValidating = true; _validationError = null; _isBeneficiaryValidated = false; nameController.text = ''; // clear previous name }); final String accountNo = accountNumberController.text.trim(); final String ifsc = ifscController.text.trim(); final String remitter = widget.customerName; final service = getIt(); try { final String beneficiaryName = await service.validateBeneficiary( accountNo: accountNo, ifscCode: ifsc, remitterName: remitter, ); setState(() { nameController.text = beneficiaryName; _isBeneficiaryValidated = true; _validationError = null; }); } catch (e) { setState(() { _validationError = e.toString(); _isBeneficiaryValidated = false; }); } finally { if (mounted) { setState(() { _isValidating = false; }); } } } void validateAndAddBeneficiary() async { // First, validate the form fields (account number, confirm account, ifsc, etc.) if (!_formKey.currentState!.validate()) { return; } // Ensure beneficiary is validated before proceeding to TPIN if (!_isBeneficiaryValidated) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Please validate beneficiary details first.')), ); return; } // Create the beneficiary object without TPIN for now final beneficiaryWithoutTpin = Beneficiary( accountNo: accountNumberController.text.trim(), accountType: accountType, name: nameController.text.trim(), ifscCode: ifscController.text.trim(), bankName: bankNameController.text.trim(), branchName: branchNameController.text.trim(), ); // Navigate to TPIN screen Navigator.push( context, MaterialPageRoute( builder: (context) => TransactionPinScreen( onPinCompleted: (pinScreenContext, tpin) async { // Show loading spinner while processing showDialog( context: pinScreenContext, barrierDismissible: false, builder: (BuildContext ctx) { // ignore: deprecated_member_use return WillPopScope( onWillPop: () async => false, child: const Center( child: CircularProgressIndicator(), ), ); }, ); // Create a new Beneficiary object with the TPIN final beneficiaryWithTpin = Beneficiary( accountNo: beneficiaryWithoutTpin.accountNo, accountType: beneficiaryWithoutTpin.accountType, name: beneficiaryWithoutTpin.name, ifscCode: beneficiaryWithoutTpin.ifscCode, bankName: beneficiaryWithoutTpin.bankName, branchName: beneficiaryWithoutTpin.branchName, tpin: tpin, ); try { final isSuccess = await service.sendForValidation(beneficiaryWithTpin); if (pinScreenContext.mounted) { Navigator.pop(pinScreenContext); // Close the spinner Navigator.pushReplacement( pinScreenContext, MaterialPageRoute( builder: (ctx) => BeneficiaryResultPage(isSuccess: isSuccess), ), ); } } on DioException catch (e) { if (pinScreenContext.mounted) { Navigator.pop(pinScreenContext); // Close the spinner ScaffoldMessenger.of(pinScreenContext).showSnackBar( SnackBar( content: Text(e.response?.statusCode == 409 ? 'Beneficiary already exists' : 'Something went Wrong')), ); } } catch (e) { if (pinScreenContext.mounted) { Navigator.pop(pinScreenContext); // Close the spinner ScaffoldMessenger.of(pinScreenContext).showSnackBar( SnackBar( content: Text( AppLocalizations.of(context).somethingWentWrong)), ); } } }, ), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: IconButton( icon: const Icon(Symbols.arrow_back_ios_new), onPressed: () { Navigator.pop(context); }, ), title: Text( AppLocalizations.of(context).addBeneficiary, style: const TextStyle(color: Colors.black, fontWeight: FontWeight.w500), ), centerTitle: false, actions: [ Padding( padding: const EdgeInsets.only(right: 10.0), child: CircleAvatar( backgroundColor: Colors.grey[200], radius: 20, child: SvgPicture.asset( 'assets/images/avatar_male.svg', width: 40, height: 40, fit: BoxFit.cover, ), ), ), ], ), body: SafeArea( child: Form( key: _formKey, child: Column( children: [ Expanded( child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Padding( padding: const EdgeInsets.all(10.0), child: Column( children: [ TextFormField( controller: accountNumberController, decoration: InputDecoration( labelText: AppLocalizations.of( context, ).accountNumber, // prefixIcon: Icon(Icons.person), border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), obscureText: true, keyboardType: TextInputType.number, textInputAction: TextInputAction.next, onChanged: (value) { nameController.clear(); setState(() { _isBeneficiaryValidated = false; }); }, validator: (value) { if (value == null || value.length < 10) { return AppLocalizations.of( context, ).enterValidAccountNumber; } return null; }, ), const SizedBox(height: 24), // Confirm Account Number TextFormField( controller: confirmAccountNumberController, decoration: InputDecoration( labelText: AppLocalizations.of( context, ).confirmAccountNumber, // prefixIcon: Icon(Icons.person), border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), keyboardType: TextInputType.number, textInputAction: TextInputAction.next, validator: (value) { if (value == null || value.isEmpty) { return AppLocalizations.of( context, ).reenterAccountNumber; } if (value != accountNumberController.text) { return AppLocalizations.of( context, ).accountMismatch; } return null; }, ), const SizedBox(height: 24), // 🔹 IFSC Code Field TextFormField( controller: ifscController, maxLength: 11, inputFormatters: [ LengthLimitingTextInputFormatter(11), ], decoration: InputDecoration( labelText: AppLocalizations.of(context).ifscCode, border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), textCapitalization: TextCapitalization.characters, textInputAction: TextInputAction.next, onFieldSubmitted: (_) { _validateIFSC(); }, onChanged: (value) { final trimmed = value.trim().toUpperCase(); if (trimmed.length < 11) { // clear bank/branch if backspace or changed bankNameController.clear(); branchNameController.clear(); } }, validator: (value) { final pattern = RegExp(r'^[A-Z]{4}0[A-Z0-9]{6}$'); if (value == null || value.trim().isEmpty) { return AppLocalizations.of(context).enterIfsc; } else if (!pattern.hasMatch( value.trim().toUpperCase(), )) { return AppLocalizations.of( context, ).invalidIfscFormat; } return null; }, ), const SizedBox(height: 24), // 🔹 Bank Name (Disabled) TextFormField( controller: bankNameController, enabled: false, // changed from readOnly to disabled decoration: InputDecoration( labelText: AppLocalizations.of(context).bankName, border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context) .dialogBackgroundColor, // disabled color enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), ), const SizedBox(height: 24), // 🔹 Branch Name (Disabled) TextFormField( controller: branchNameController, enabled: false, // changed from readOnly to disabled decoration: InputDecoration( labelText: AppLocalizations.of(context).branchName, border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).dialogBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), ), const SizedBox(height: 24), if (!_isBeneficiaryValidated) Padding( padding: const EdgeInsets.only(bottom: 24), child: SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _isValidating ? null : () { if (confirmAccountNumberController .text == accountNumberController.text) { _validateBeneficiary(); } else { setState(() { _validationError = 'Please enter a valid and matching account number.'; }); } }, child: _isValidating ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2), ) : Text(AppLocalizations.of(context) .validateBeneficiary), ), ), ), //Beneficiary Name (Disabled) TextFormField( controller: nameController, enabled: false, decoration: InputDecoration( labelText: AppLocalizations.of(context).beneficiaryName, border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).dialogBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black, width: 2), ), ), textInputAction: TextInputAction.next, validator: (value) => value == null || value.isEmpty ? AppLocalizations.of(context).nameRequired : null, ), const SizedBox(height: 24), // 🔹 Account Type Dropdown DropdownButtonFormField( value: accountType, decoration: InputDecoration( labelText: AppLocalizations.of(context).accountType, border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), items: [ AppLocalizations.of(context).savings, AppLocalizations.of(context).current, ] .map( (type) => DropdownMenuItem( value: type, child: Text(type), ), ) .toList(), onChanged: (value) { setState(() { accountType = value!; }); }, ), const SizedBox(height: 24), TextFormField( controller: phoneController, keyboardType: TextInputType.phone, decoration: InputDecoration( labelText: AppLocalizations.of(context).phone, prefixIcon: const Icon(Icons.phone), border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), textInputAction: TextInputAction.done, validator: (value) => value == null || value.length != 10 ? AppLocalizations.of(context).enterValidPhone : null, ), const SizedBox(height: 35), ], ), ), ), ), Padding( padding: const EdgeInsets.all(16.0), child: SizedBox( width: 250, child: ElevatedButton( onPressed: validateAndAddBeneficiary, style: ElevatedButton.styleFrom( shape: const StadiumBorder(), padding: const EdgeInsets.symmetric(vertical: 16), ), child: Text(AppLocalizations.of(context).validateAndAdd), ), ), ), ], ), ), ), ); } }