import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flux/features/customers/blocs/customers_cubit.dart'; import 'package:flux/features/operations/blocs/operations_cubit.dart'; import 'package:flux/features/operations/models/operation_model.dart'; // import 'package:flux/features/attachments/ui/operation_files_section.dart'; class OperationFormScreen extends StatefulWidget { final String? operationId; final OperationModel? existingOperation; const OperationFormScreen({ super.key, this.operationId, this.existingOperation, }); @override State createState() => _OperationFormScreenState(); } class _OperationFormScreenState extends State { final _formKey = GlobalKey(); // TEXT CONTROLLERS (Unici detentori di stato locale per evitare lag) final _referenceController = TextEditingController(); final _noteController = TextEditingController(); final _customSubtypeController = TextEditingController(); final List _availableTypes = [ 'AL', 'MNP', 'NIP', 'UNICA', 'TELEPASS', 'Energy', 'Fin', 'Entertainment', 'Custom', ]; bool _isInitialized = false; @override void initState() { super.initState(); // Inizializziamo il form nel Cubit context.read().initOperationForm( existingOperation: widget.existingOperation, operationId: widget.operationId, ); } @override void dispose() { _referenceController.dispose(); _noteController.dispose(); _customSubtypeController.dispose(); super.dispose(); } // Sincronizza SOLO i testi liberi quando il Cubit ha caricato da DB void _syncTextControllers(OperationModel model) { if (_referenceController.text.isEmpty && model.reference.isNotEmpty) { _referenceController.text = model.reference; } if (_noteController.text.isEmpty && model.note.isNotEmpty) { _noteController.text = model.note; } _isInitialized = true; } // --- LOGICA DI SALVATAGGIO --- void _saveOperation({required bool keepAdding}) { if (_formKey.currentState!.validate()) { final cubit = context.read(); final currentOperation = cubit.state.currentOperation!; // 1. "Travasiamo" i testi liberi dai controller al Modello prima di salvare final operationToSave = currentOperation.copyWith( reference: _referenceController.text, note: _noteController.text, // subtype: currentOperation.type == 'Custom' ? _customSubtypeController.text : currentOperation.subtype, // <-- Scommenta quando aggiungi subtype ); // 2. Aggiorniamo il Cubit con i testi cubit.initOperationForm(existingOperation: operationToSave); // 3. Salviamo! cubit.saveCurrentOperation( targetStatus: OperationStatus.ok, shouldPop: !keepAdding, ); } } // --- MODALE SELEZIONE CLIENTE --- void _showCustomerModal() { showModalBottomSheet( context: context, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (modalContext) { return DraggableScrollableSheet( initialChildSize: 0.8, minChildSize: 0.5, maxChildSize: 0.95, expand: false, builder: (_, scrollController) { return Column( children: [ // Header Padding( padding: const EdgeInsets.all(16.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Seleziona Cliente', style: Theme.of(context).textTheme.titleLarge, ), IconButton( icon: const Icon(Icons.close), onPressed: () => Navigator.pop(modalContext), ), ], ), ), // Barra di Ricerca Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: TextField( decoration: InputDecoration( hintText: 'Cerca per nome, telefono o email...', prefixIcon: const Icon(Icons.search), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), ), onChanged: (query) { // Evento di ricerca (usa debouncer nel cubit!) // context.read().searchCustomers(query); }, ), ), // Pulsante Nuovo Cliente Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton.icon( style: ElevatedButton.styleFrom( minimumSize: const Size.fromHeight(48), ), icon: const Icon(Icons.person_add), label: const Text('Crea Nuovo Cliente'), onPressed: () { // Apri form nuovo cliente... }, ), ), const Divider(), // Lista Clienti dal Bloc Expanded( child: BlocBuilder( builder: (context, state) { if (state.status == CustomersStatus.loading) { return const Center(child: CircularProgressIndicator()); } if (state.customers.isEmpty) { return const Center( child: Text( 'Nessun cliente trovato.', style: TextStyle(color: Colors.grey), ), ); } return ListView.builder( controller: scrollController, itemCount: state.customers.length, itemBuilder: (context, index) { final customer = state.customers[index]; return ListTile( leading: CircleAvatar( child: Text( customer.name.substring(0, 1).toUpperCase(), ), ), title: Text( customer.name, style: const TextStyle( fontWeight: FontWeight.bold, ), ), subtitle: Text( '${customer.phoneNumber} • ${customer.email}', ), onTap: () { // Aggiorniamo il form tramite il Cubit delle operazioni context .read() .updateOperationFields( customerId: customer.id, // customer.id customerDisplayName: customer.name, // customer.name ); Navigator.pop(modalContext); }, ); }, ); }, ), ), ], ); }, ); }, ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return BlocConsumer( listenWhen: (previous, current) => previous.status != current.status || previous.currentOperation?.id != current.currentOperation?.id, listener: (context, state) { // Sincronizzazione iniziale if (state.status == OperationsStatus.ready && state.currentOperation != null && !_isInitialized) { _syncTextControllers(state.currentOperation!); } if (state.status == OperationsStatus.saved) { Navigator.of(context).pop(); } else if (state.status == OperationsStatus.savedNoPop) { context.read().prepareNextOperationInBatch(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Servizio aggiunto! Inserisci il prossimo.'), ), ); // Ripuliamo SOLO i testi liberi (il Cubit gestisce già i suoi reset) _referenceController.clear(); _noteController.clear(); _customSubtypeController.clear(); } else if (state.status == OperationsStatus.failure) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.errorMessage ?? 'Errore'), backgroundColor: theme.colorScheme.error, ), ); } }, builder: (context, state) { // Loader iniziale if (!_isInitialized && (widget.operationId != null || widget.existingOperation != null) && state.status == OperationsStatus.loading) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } return Scaffold( appBar: AppBar( title: Text( state.currentOperation?.id == null ? 'Nuova Pratica' : 'Modifica Pratica', ), ), body: Form( key: _formKey, child: LayoutBuilder( builder: (context, constraints) { final isDesktop = constraints.maxWidth > 900; if (isDesktop) { // --- LAYOUT DESKTOP --- return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 7, child: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: _buildMainFormContent(theme, state), ), ), VerticalDivider(width: 1, color: theme.dividerColor), Expanded( flex: 3, child: Padding( padding: const EdgeInsets.all(16.0), child: _buildNotesSection(isDesktop: true), ), ), ], ); } else { // --- LAYOUT MOBILE --- return SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildMainFormContent(theme, state), const Divider(height: 32), _buildNotesSection(isDesktop: false), const SizedBox(height: 80), ], ), ); } }, ), ), // --- LA CASSA --- bottomNavigationBar: SafeArea( child: Padding( padding: const EdgeInsets.all(16.0), child: Row( children: [ Expanded( flex: 1, child: OutlinedButton( onPressed: state.status == OperationsStatus.saving ? null : () => _saveOperation(keepAdding: true), child: const Text( 'Salva e Aggiungi Altro', textAlign: TextAlign.center, ), ), ), const SizedBox(width: 12), Expanded( flex: 1, child: ElevatedButton( onPressed: state.status == OperationsStatus.saving ? null : () => _saveOperation(keepAdding: false), child: state.status == OperationsStatus.saving ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : const Text('Salva ed Esci'), ), ), ], ), ), ), ); }, ); } // --- COSTRUTTORI UI COMPONENTI --- Widget _buildMainFormContent(ThemeData theme, OperationsState state) { final currentOp = state.currentOperation; final currentType = currentOp?.type ?? 'AL'; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // --- BLOCCO 1: CONTESTO --- _buildSectionTitle('Cliente & Riferimento'), _buildCustomerSelector(currentOp), const SizedBox(height: 16), TextFormField( controller: _referenceController, decoration: const InputDecoration( labelText: 'Riferimento (es. numero di telefono, targa...)', prefixIcon: Icon(Icons.tag), ), ), const Divider(height: 32), // --- BLOCCO 2: TIPO DI OPERAZIONE --- _buildSectionTitle('Cosa stiamo facendo?'), Wrap( spacing: 8.0, runSpacing: 8.0, children: _availableTypes.map((type) { return ChoiceChip( label: Text(type), selected: currentType == type, onSelected: (selected) { if (selected) { // Diciamo al Cubit di cambiare tipo e spianare i campi dipendenti context.read().updateOperationFields( type: type, clearProvider: true, clearSubtype: true, clearExpiration: true, ); } }, ); }).toList(), ), const Divider(height: 32), // --- BLOCCO 3: DETTAGLI REATTIVI --- _buildSectionTitle('Dettagli Servizio'), // PROVIDER (Mostrato quasi sempre) ListTile( title: const Text('Seleziona Gestore'), subtitle: Text( currentOp?.providerId ?? 'Nessun gestore selezionato', ), // Adatta se hai displayName trailing: const Icon(Icons.arrow_drop_down), shape: RoundedRectangleBorder( side: BorderSide(color: theme.dividerColor), borderRadius: BorderRadius.circular(8), ), onTap: () { // TODO: Modale o Dropdown Provider }, ), const SizedBox(height: 16), // SOTTO-TIPO (Reattivo) if (['Energy', 'Fin', 'Entertainment'].contains(currentType)) ...[ DropdownButtonFormField( initialValue: null, // Sostituisci con currentOp?.subtype quando lo aggiungi decoration: const InputDecoration( labelText: 'Dettaglio (es. Luce, Gas...)', ), items: [ 'Luce', 'Gas', 'Dual', ].map((s) => DropdownMenuItem(value: s, child: Text(s))).toList(), onChanged: (val) { // context.read().updateOperationFields(subtype: val); }, ), const SizedBox(height: 16), ], // SOTTO-TIPO CUSTOM (Reattivo) if (currentType == 'Custom') ...[ TextFormField( controller: _customSubtypeController, decoration: const InputDecoration( labelText: 'Specifica il servizio (es. Monopattino)', ), ), const SizedBox(height: 16), ], // SCADENZA (Reattivo) if ([ 'Energy', 'Fin', 'Entertainment', 'Custom', ].contains(currentType)) ...[ ListTile( title: const Text('Data di Scadenza'), subtitle: Text( currentOp?.expirationDate?.toLocal().toString().split(' ')[0] ?? 'Nessuna scadenza', ), trailing: const Icon(Icons.calendar_today), shape: RoundedRectangleBorder( side: BorderSide(color: theme.dividerColor), borderRadius: BorderRadius.circular(8), ), onTap: () async { final operationsCubit = context.read(); final date = await showDatePicker( context: context, initialDate: DateTime.now().add(const Duration(days: 365)), firstDate: DateTime.now(), lastDate: DateTime.now().add(const Duration(days: 3650)), ); if (date != null) { operationsCubit.updateOperationFields(expirationDate: date); } }, ), const SizedBox(height: 16), ], // QUANTITÀ Row( children: [ const Text('Quantità: '), IconButton( icon: const Icon(Icons.remove), onPressed: () { final q = currentOp?.quantity ?? 1; if (q > 1) { context.read().updateOperationFields( quantity: q - 1, ); } }, ), Text( '${currentOp?.quantity ?? 1}', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), IconButton( icon: const Icon(Icons.add), onPressed: () { final q = currentOp?.quantity ?? 1; context.read().updateOperationFields( quantity: q + 1, ); }, ), ], ), const Divider(height: 32), // --- BLOCCO 5: ALLEGATI --- _buildSectionTitle('Documenti & Foto'), const Center( child: Text( "Widget File in arrivo...", style: TextStyle(color: Colors.grey), ), ), ], ); } Widget _buildNotesSection({required bool isDesktop}) { final title = _buildSectionTitle('Note Interne'); final noteField = TextFormField( controller: _noteController, keyboardType: TextInputType.multiline, minLines: isDesktop ? null : 5, maxLines: null, expands: isDesktop, textAlignVertical: TextAlignVertical.top, decoration: const InputDecoration( hintText: 'Incolla qui seriali, ICCID, IBAN, indirizzi...', alignLabelWithHint: true, border: OutlineInputBorder(), ), ); if (isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ title, const SizedBox(height: 8), Expanded(child: noteField), ], ); } else { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [title, const SizedBox(height: 8), noteField], ); } } Widget _buildSectionTitle(String title) { return Padding( padding: const EdgeInsets.only(bottom: 12.0), child: Text( title, style: Theme.of( context, ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), ), ); } Widget _buildCustomerSelector(OperationModel? currentOp) { final hasCustomer = currentOp?.customerId != null && currentOp!.customerId!.isNotEmpty; return InkWell( onTap: _showCustomerModal, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( border: Border.all(color: Theme.of(context).colorScheme.primary), borderRadius: BorderRadius.circular(8), color: Theme.of( context, ).colorScheme.primaryContainer.withValues(alpha: 0.2), ), child: Row( children: [ const Icon(Icons.person), const SizedBox(width: 12), Expanded( child: Text( hasCustomer ? currentOp.customerDisplayName ?? '' : 'Seleziona Cliente *', style: TextStyle( fontWeight: hasCustomer ? FontWeight.bold : FontWeight.normal, color: hasCustomer ? null : Colors.grey, ), ), ), const Icon(Icons.search), ], ), ), ); } }