import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:qpay/screens/accounts/account_provider.dart';
import 'package:qpay/screens/confirm/confirm_controller.dart';
import 'package:qpay/screens/groups/models/batch_item_update_model.dart';
import 'package:qpay/screens/groups/models/group_batch_model.dart';
import 'package:qpay/models/responsive_policy.dart';
import 'package:qpay/screens/groups/controllers/batch_controller.dart';
import 'package:qpay/screens/home/home_controller.dart';
import 'package:qpay/screens/pay/pay_controller.dart';
import 'package:qpay/widgets/app_snack_bar.dart';
import 'package:qpay/widgets/status_chip_widget.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';

class BatchDetailScreen extends StatefulWidget {
  final String batchId;

  const BatchDetailScreen({super.key, required this.batchId});

  @override
  State<BatchDetailScreen> createState() => _BatchDetailScreenState();
}

class _BatchDetailScreenState extends State<BatchDetailScreen>
    with TickerProviderStateMixin {
  late BatchController controller;
  ConfirmController? _confirmController;
  GroupBatchUpdateRequest? _cachedUpdateReq;
  bool _showPollButton = false;
  String _workspaceId = '';
  bool _initialLoading = true;
  String? _loadError;
  PaymentProcessor? _selectedProcessor;

  final TextEditingController _searchController = TextEditingController();
  final TextEditingController _accountController = TextEditingController();
  final ScrollController _scrollController = ScrollController();

  bool _editMode = false;
  final Map<String, TextEditingController> _editedAmounts = {};
  final Map<String, BillProvider> _editedProviders = {};
  final Map<String, BillProduct?> _editedProducts = {};

  late AnimationController _entryController;
  late Animation<double> _entryFade;
  late Animation<Offset> _entrySlide;

  static const _terminalStatuses = {'SUCCESS', 'COMPLETE', 'FAILED'};
  static const _successStatuses = {'SUCCESS', 'COMPLETE'};
  static const _editableStatuses = {'CREATED', 'CONFIRM_FAILED', 'CONFIRMING'};

  GroupBatch? get _batch => controller.model.currentBatch;

  @override
  void initState() {
    super.initState();
    controller = BatchController(context);

    _entryController = AnimationController(
      duration: const Duration(milliseconds: 220),
      vsync: this,
    );
    _entryFade = Tween<double>(
      begin: 0.0,
      end: 1.0,
    ).animate(CurvedAnimation(parent: _entryController, curve: Curves.easeOut));
    _entrySlide = Tween<Offset>(
      begin: const Offset(0, 0.02),
      end: Offset.zero,
    ).animate(CurvedAnimation(parent: _entryController, curve: Curves.easeOut));
    _entryController.forward();

    controller.addListener(() => setState(() {}));
    _searchController.addListener(() => setState(() {}));

    _init();
  }

  Future<void> _init() async {
    final prefs = await SharedPreferences.getInstance();
    _workspaceId = prefs.getString('workspaceId') ?? '';

    final result = await controller.getBatch(widget.batchId, _workspaceId);
    if (!mounted) return;

    if (result.isSuccess) {
      if (_batch?.id != null) {
        await controller.listBatchItems(_batch!.id!);
      }
      setState(() {
        _initialLoading = false;
      });

      controller.loadProcessors(_batch!.currency);
    } else {
      setState(() {
        _initialLoading = false;
        _loadError = result.error ?? 'Failed to load batch';
      });
    }
  }

  @override
  void dispose() {
    _searchController.dispose();
    _accountController.dispose();
    _scrollController.dispose();
    _entryController.dispose();
    _confirmController?.dispose();
    controller.dispose();
    super.dispose();
  }

  Future<void> _onConfirm() async {
    final batch = _batch;
    if (batch == null) return;

    // Step 1: Choose payment processor and update the batch with it before confirming
    final processor = await _showProcessorSelectorBottomSheet();
    if (processor == null) return; // user dismissed
    _selectedProcessor = processor;

    // Persist the selected payment processor on the batch before confirming
    final updateReq = GroupBatchUpdateRequest(
      id: batch.id,
      workspaceId: _workspaceId,
      paymentProcessorLabel: processor.label,
      paymentProcessorName: processor.name,
      paymentProcessorImage: processor.image,
    );
    final updateResult = await controller.updateBatch(updateReq);
    if (!mounted) return;
    if (!updateResult.isSuccess) {
      _showError(updateResult.error ?? 'Failed to update processor.');
      return;
    }

    // Cache the update request so _onRequest can check the processor
    _cachedUpdateReq = updateReq;

    // Step 2: Confirm the batch
    final result = await controller.confirmBatch(
      batch.id!,
      _workspaceId,
      _selectedProcessor!.authType,
    );
    if (!mounted) return;
    if (!result.isSuccess) {
      _showError(result.error ?? 'Confirm failed.');
    } else {
      setState(() => _selectedProcessor = null);
      setState(() => _initialLoading = true);
      await _init();

      if (!mounted) {
        return;
      }

      AppSnackBar.show(
        context,
        'Batch items are now being confirmed. Tap refresh to see latest status.',
      );
    }
  }

  /// Shows a bottom sheet that lets the user pick a payment processor.
  /// Returns the chosen [PaymentProcessor], or `null` if dismissed.
  Future<PaymentProcessor?> _showProcessorSelectorBottomSheet() async {
    final processors = controller.model.processors;
    if (processors.isEmpty) {
      _showError('No payment processors available.');
      return null;
    }

    final theme = Theme.of(context);
    final isDark = theme.brightness == Brightness.dark;
    final currency = _batch?.currency ?? 'USD';
    final value = _batch?.value?.toStringAsFixed(2) ?? '—';

    PaymentProcessor? picked = _selectedProcessor;

    return showModalBottomSheet<PaymentProcessor>(
      context: context,
      isScrollControlled: true,
      backgroundColor: Colors.transparent,
      builder: (sheetContext) {
        return StatefulBuilder(
          builder: (innerContext, setSheetState) {
            return Container(
              decoration: BoxDecoration(
                color: isDark ? const Color(0xFF1A1A2E) : Colors.white,
                borderRadius: const BorderRadius.vertical(
                  top: Radius.circular(24),
                ),
              ),
              padding: EdgeInsets.only(
                left: 20,
                right: 20,
                top: 12,
                bottom: MediaQuery.of(innerContext).viewInsets.bottom + 24,
              ),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Center(
                    child: Container(
                      width: 40,
                      height: 4,
                      margin: const EdgeInsets.only(bottom: 16),
                      decoration: BoxDecoration(
                        color: Colors.grey.shade300,
                        borderRadius: BorderRadius.circular(2),
                      ),
                    ),
                  ),
                  Text(
                    'Confirm Batch',
                    style: TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.w700,
                      color: isDark ? Colors.white : Colors.black87,
                    ),
                  ),
                  const SizedBox(height: 6),
                  Text(
                    'Select a payment processor to confirm this batch.',
                    style: TextStyle(
                      fontSize: 13,
                      fontWeight: FontWeight.w500,
                      color: Colors.grey.shade500,
                    ),
                  ),
                  const SizedBox(height: 18),
                  Container(
                    padding: const EdgeInsets.symmetric(
                      horizontal: 14,
                      vertical: 10,
                    ),
                    decoration: BoxDecoration(
                      color: theme.colorScheme.primary.withValues(alpha: 0.08),
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child: Row(
                      children: [
                        Icon(
                          Icons.receipt_long_rounded,
                          size: 18,
                          color: theme.colorScheme.primary,
                        ),
                        const SizedBox(width: 10),
                        Text(
                          'Batch Total',
                          style: TextStyle(
                            fontSize: 13,
                            fontWeight: FontWeight.w600,
                            color: theme.colorScheme.primary,
                          ),
                        ),
                        const Spacer(),
                        Text(
                          '$currency $value',
                          style: TextStyle(
                            fontSize: 15,
                            fontWeight: FontWeight.w700,
                            color: theme.colorScheme.primary,
                          ),
                        ),
                      ],
                    ),
                  ),
                  const SizedBox(height: 18),
                  Text(
                    'Payment Processor',
                    style: TextStyle(
                      fontSize: 13,
                      fontWeight: FontWeight.w600,
                      color: Colors.grey.shade500,
                    ),
                  ),
                  const SizedBox(height: 10),
                  ConstrainedBox(
                    constraints: BoxConstraints(
                      maxHeight: MediaQuery.of(innerContext).size.height * 0.4,
                    ),
                    child: SingleChildScrollView(
                      child: Column(
                        children: processors.map((processor) {
                          final isSelected = picked == processor;
                          return Padding(
                            padding: const EdgeInsets.only(bottom: 10),
                            child: GestureDetector(
                              onTap: () {
                                setSheetState(() {
                                  picked = processor;
                                });
                              },
                              child: AnimatedContainer(
                                duration: const Duration(milliseconds: 200),
                                curve: Curves.easeOutCubic,
                                padding: const EdgeInsets.symmetric(
                                  horizontal: 14,
                                  vertical: 12,
                                ),
                                decoration: BoxDecoration(
                                  color: isSelected
                                      ? theme.colorScheme.primary.withValues(
                                          alpha: 0.12,
                                        )
                                      : (isDark
                                            ? Colors.white.withValues(
                                                alpha: 0.05,
                                              )
                                            : Colors.grey.shade50),
                                  borderRadius: BorderRadius.circular(14),
                                  border: Border.all(
                                    color: isSelected
                                        ? theme.colorScheme.primary
                                        : (isDark
                                              ? Colors.white.withValues(
                                                  alpha: 0.08,
                                                )
                                              : Colors.grey.shade200),
                                    width: isSelected ? 2 : 1,
                                  ),
                                ),
                                child: Column(
                                  children: [
                                    Row(
                                      children: [
                                        ClipRRect(
                                          borderRadius: BorderRadius.circular(
                                            8,
                                          ),
                                          child: Image.asset(
                                            'assets/${processor.image}',
                                            width: 36,
                                            height: 36,
                                            errorBuilder: (_, __, ___) => Icon(
                                              Icons.payment_rounded,
                                              size: 28,
                                              color: theme.colorScheme.primary,
                                            ),
                                          ),
                                        ),
                                        const SizedBox(width: 12),
                                        Expanded(
                                          child: Column(
                                            crossAxisAlignment:
                                                CrossAxisAlignment.start,
                                            children: [
                                              Text(
                                                processor.name,
                                                style: TextStyle(
                                                  fontSize: 14,
                                                  fontWeight: FontWeight.w600,
                                                  color: isDark
                                                      ? Colors.white
                                                      : Colors.black87,
                                                ),
                                              ),
                                              if (processor.label.isNotEmpty)
                                                Text(
                                                  processor.label,
                                                  style: TextStyle(
                                                    fontSize: 12,
                                                    color: Colors.grey.shade500,
                                                  ),
                                                ),
                                            ],
                                          ),
                                        ),
                                        Icon(
                                          isSelected
                                              ? Icons.radio_button_checked
                                              : Icons.radio_button_unchecked,
                                          color: isSelected
                                              ? theme.colorScheme.primary
                                              : Colors.grey.shade400,
                                        ),
                                      ],
                                    ),
                                    // Row 2.5: City Wallet balance (if available for batch currency)
                                    Builder(
                                      builder: (context) {
                                        final accountProvider = context
                                            .watch<AccountProvider>();
                                        final batchCurrency =
                                            _batch!.currency ?? 'USD';
                                        final cityWallet = accountProvider
                                            .balanceForCurrency(batchCurrency);
                                        final hasCityWallet =
                                            cityWallet != null;
                                        if (!hasCityWallet)
                                          return const SizedBox.shrink();
                                        if (processor.label != 'WALLET')
                                          return const SizedBox.shrink();
                                        return Padding(
                                          padding: const EdgeInsets.only(
                                            top: 8,
                                          ),
                                          child: Row(
                                            children: [
                                              Icon(
                                                Icons
                                                    .account_balance_wallet_outlined,
                                                size: 14,
                                                color: Theme.of(context)
                                                    .colorScheme
                                                    .primary
                                                    .withValues(alpha: 0.7),
                                              ),
                                              const SizedBox(width: 6),
                                              Text(
                                                'City Wallet Balance',
                                                style: TextStyle(
                                                  fontSize: 12,
                                                  fontWeight: FontWeight.w500,
                                                  color: Theme.of(context)
                                                      .colorScheme
                                                      .primary
                                                      .withValues(alpha: 0.7),
                                                ),
                                              ),
                                              const Spacer(),
                                              Text(
                                                '\$${cityWallet!.balance.toStringAsFixed(2)}',
                                                style: TextStyle(
                                                  fontSize: 14,
                                                  fontWeight: FontWeight.w700,
                                                  color: Theme.of(
                                                    context,
                                                  ).colorScheme.primary,
                                                ),
                                              ),
                                            ],
                                          ),
                                        );
                                      },
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          );
                        }).toList(),
                      ),
                    ),
                  ),
                  const SizedBox(height: 8),
                  SizedBox(
                    width: double.infinity,
                    child: ElevatedButton(
                      onPressed: () {
                        final selected = picked ?? processors.first;
                        Navigator.of(innerContext).pop(selected);
                      },
                      style: ElevatedButton.styleFrom(
                        backgroundColor: theme.colorScheme.primary,
                        foregroundColor: Colors.white,
                        padding: const EdgeInsets.symmetric(vertical: 14),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(14),
                        ),
                        elevation: 0,
                      ),
                      child: const Text(
                        'Confirm',
                        style: TextStyle(
                          fontSize: 15,
                          fontWeight: FontWeight.w700,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            );
          },
        );
      },
    );
  }

  Future<void> _onRequest() async {
    final batch = _batch;
    if (batch == null) return;

    final result = await controller.requestBatch(batch.id!, _workspaceId);
    if (!mounted) return;

    if (!result.isSuccess) {
      _showError(result.error ?? 'Request failed.');
      return;
    }

    // Check if the selected payment processor is WALLET
    final isWallet =
        _cachedUpdateReq?.paymentProcessorLabel == 'WALLET' ||
        _selectedProcessor?.label == 'WALLET' ||
        _batch?.paymentProcessorLabel == 'WALLET';

    if (isWallet) {
      // Show OTP bottom sheet for WALLET processor
      final otp = await _showOtpBottomSheet();
      if (!mounted) return;
      if (otp == null) {
        _showError('OTP verification cancelled.');
        return;
      }

      // Initialize confirm controller if needed
      _confirmController ??= ConfirmController(context);

      final txn = result.data!;
      final otpResult = await _confirmController!.updateVelocityTransactionOtp(
        txn.id!,
        {'verificationCode': otp},
      );
      if (!mounted) return;

      if (!otpResult.isSuccess) {
        _showError(otpResult.error ?? 'OTP verification failed.');
        return;
      }
    }

    // Normal flow after request (and optional OTP verification)
    await _init();

    final txn = result.data!;
    final pollingStatus = txn.pollingStatus?.toUpperCase();

    if (pollingStatus != null && _terminalStatuses.contains(pollingStatus)) {
      await _init();
      if (_successStatuses.contains(pollingStatus)) {
        if (mounted) {
          AppSnackBar.showSuccess(context, 'Payment completed successfully.');
        }
      }
    } else {
      final pollResult = await controller.pollBatch(batch.id!, _workspaceId);
      if (!mounted) return;
      if (pollResult.isSuccess) {
        setState(() => _showPollButton = false);
      } else {
        setState(() => _showPollButton = true);
        _showError(
          pollResult.error ?? 'Auto-poll failed. You can retry manually.',
        );
      }

      await _init();
    }
  }

  /// Shows a bottom sheet for OTP input when the payment processor is WALLET.
  /// Returns the entered OTP string, or `null` if dismissed.
  Future<String?> _showOtpBottomSheet() async {
    final theme = Theme.of(context);
    final isDark = theme.brightness == Brightness.dark;
    final otpController = TextEditingController();
    final formKey = GlobalKey<FormState>();

    return showModalBottomSheet<String>(
      context: context,
      isScrollControlled: true,
      backgroundColor: Colors.transparent,
      builder: (sheetContext) {
        return Padding(
          padding: EdgeInsets.only(
            bottom: MediaQuery.of(sheetContext).viewInsets.bottom,
          ),
          child: Container(
            decoration: BoxDecoration(
              color: isDark ? const Color(0xFF1A1A2E) : Colors.white,
              borderRadius: const BorderRadius.vertical(
                top: Radius.circular(24),
              ),
            ),
            padding: const EdgeInsets.fromLTRB(20, 12, 20, 24),
            child: Form(
              key: formKey,
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Center(
                    child: Container(
                      width: 40,
                      height: 4,
                      margin: const EdgeInsets.only(bottom: 16),
                      decoration: BoxDecoration(
                        color: Colors.grey.shade300,
                        borderRadius: BorderRadius.circular(2),
                      ),
                    ),
                  ),
                  Center(
                    child: Container(
                      width: 56,
                      height: 56,
                      decoration: BoxDecoration(
                        color: theme.colorScheme.primary.withValues(alpha: 0.1),
                        shape: BoxShape.circle,
                      ),
                      child: Icon(
                        Icons.verified_user_rounded,
                        size: 28,
                        color: theme.colorScheme.primary,
                      ),
                    ),
                  ),
                  const SizedBox(height: 16),
                  Text(
                    'OTP Verification',
                    style: TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.w700,
                      color: isDark ? Colors.white : Colors.black87,
                    ),
                    textAlign: TextAlign.center,
                  ),
                  const SizedBox(height: 8),
                  Text(
                    'An OTP has been sent to your registered mobile number/email. Please enter it below to proceed.',
                    style: TextStyle(
                      fontSize: 13,
                      fontWeight: FontWeight.w500,
                      color: Colors.grey.shade500,
                    ),
                    textAlign: TextAlign.center,
                  ),
                  const SizedBox(height: 20),
                  TextFormField(
                    controller: otpController,
                    keyboardType: TextInputType.number,
                    textAlign: TextAlign.center,
                    maxLength: 6,
                    style: TextStyle(
                      fontSize: 24,
                      fontWeight: FontWeight.w700,
                      letterSpacing: 8,
                      color: isDark ? Colors.white : Colors.black87,
                    ),
                    decoration: InputDecoration(
                      counterText: '',
                      hintText: '------',
                      hintStyle: TextStyle(
                        fontSize: 24,
                        fontWeight: FontWeight.w700,
                        letterSpacing: 8,
                        color: Colors.grey.shade400,
                      ),
                      filled: true,
                      fillColor: isDark
                          ? Colors.white.withValues(alpha: 0.06)
                          : Colors.grey.shade50,
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(14),
                        borderSide: BorderSide.none,
                      ),
                      focusedBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(14),
                        borderSide: BorderSide(
                          color: theme.colorScheme.primary,
                          width: 2,
                        ),
                      ),
                      errorBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(14),
                        borderSide: BorderSide(color: Colors.red.shade400),
                      ),
                      contentPadding: const EdgeInsets.symmetric(
                        horizontal: 16,
                        vertical: 14,
                      ),
                    ),
                    validator: (value) {
                      if (value == null || value.trim().isEmpty) {
                        return 'Please enter the OTP';
                      }
                      if (value.trim().length < 4) {
                        return 'OTP must be at least 4 digits';
                      }
                      return null;
                    },
                  ),
                  const SizedBox(height: 16),
                  SizedBox(
                    width: double.infinity,
                    child: ElevatedButton(
                      onPressed: () {
                        if (formKey.currentState?.validate() ?? false) {
                          Navigator.of(
                            sheetContext,
                          ).pop(otpController.text.trim());
                        }
                      },
                      style: ElevatedButton.styleFrom(
                        backgroundColor: theme.colorScheme.primary,
                        foregroundColor: Colors.white,
                        padding: const EdgeInsets.symmetric(vertical: 14),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(14),
                        ),
                        elevation: 0,
                      ),
                      child: const Text(
                        'Submit',
                        style: TextStyle(
                          fontSize: 15,
                          fontWeight: FontWeight.w700,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      },
    );
  }

  Future<void> _onPoll() async {
    final batch = _batch;
    if (batch == null) return;
    final result = await controller.pollBatch(batch.id!, _workspaceId);
    if (!mounted) return;
    if (result.isSuccess) {
      setState(() => _showPollButton = false);
      await _init(); // refresh batch and items after poll
    } else {
      _showError(result.error ?? 'Poll failed.');
    }
  }

  Future<void> _onDownloadReport() async {
    final batch = _batch;
    if (batch == null) return;
    final result = await controller.downloadBatchReport(
      batch.id!,
      _workspaceId,
    );
    if (!mounted) return;
    if (result.isSuccess) {
      final fileUrl = result.data?['fileUrl'] as String?;
      if (fileUrl != null && fileUrl.isNotEmpty) {
        final uri = Uri.tryParse(fileUrl);
        if (uri != null && await canLaunchUrl(uri)) {
          await launchUrl(uri, mode: LaunchMode.externalApplication);
        } else {
          _showError('Could not open report URL.');
        }
      } else {
        _showError('Report URL not available.');
      }
    } else {
      _showError(result.error ?? 'Failed to download report.');
    }
  }

  void _toggleEditMode() async {
    if (_editMode) {
      // Exiting edit mode — discard pending edits
      _clearEditState();
      return;
    }

    // Entering edit mode — ensure providers are loaded
    if (controller.model.providers.isEmpty) {
      await controller.loadProviders(_batch!.currency!);
    }
    if (!mounted) return;

    // Initialize edit controllers with current item values
    _clearEditState();
    for (final item in controller.model.batchItems) {
      if (item.id != null) {
        _editedAmounts[item.id!] = TextEditingController(
          text: item.amount?.toStringAsFixed(2) ?? '',
        );
        // Match provider by label from the item's providerLabel field,
        // falling back to the first provider if no match is found
        if (controller.model.providers.isNotEmpty) {
          final matchedProvider = item.providerLabel != null
              ? controller.model.providers.cast<BillProvider?>().firstWhere(
                  (p) => p?.label == item.providerLabel,
                  orElse: () => null,
                )
              : null;
          _editedProviders[item.id!] =
              matchedProvider ?? controller.model.providers.first;
        }

        // Match product by billProductName from the item
        if (_editedProviders[item.id!] != null) {
          final providerId = _editedProviders[item.id!]!.id;
          _ensureProductsLoaded(providerId, item.id!);
        }
      }
    }

    setState(() => _editMode = true);
  }

  /// Ensures products are loaded for the given provider, then tries to match
  /// the item's [billProductName] to a product. Called during edit mode init.
  Future<void> _ensureProductsLoaded(String providerId, String itemId) async {
    final cached = controller.model.providerProducts[providerId];
    if (cached != null) {
      // Already cached — match now
      _matchProductForItem(itemId, cached);
      return;
    }

    // Load products for this provider
    final result = await controller.loadProviderProducts(providerId);
    if (!mounted || result.data == null) return;

    final item = controller.model.batchItems
        .where((i) => i.id == itemId)
        .firstOrNull;
    if (item == null) return;

    _matchProductForItem(itemId, result.data!);
    setState(() {});
  }

  void _matchProductForItem(String itemId, List<BillProduct> products) {
    final item = controller.model.batchItems
        .where((i) => i.id == itemId)
        .firstOrNull;
    if (item == null) return;

    if (products.isEmpty) {
      _editedProducts[itemId] = null;
      return;
    }

    final matchedProduct =
        item.billProductName != null && item.billProductName!.isNotEmpty
        ? products.cast<BillProduct?>().firstWhere(
            (p) =>
                p?.name == item.billProductName ||
                p?.displayName == item.billProductName,
            orElse: () => null,
          )
        : null;
    final selected = matchedProduct ?? products.first;
    _editedProducts[itemId] = selected;

    // Update amount with the product's default amount
    if (selected.defaultAmount > 0) {
      final ctrl = _editedAmounts[itemId];
      if (ctrl != null) {
        ctrl.text = selected.defaultAmount.toStringAsFixed(2);
      }
    }
  }

  void _clearEditState() {
    for (final controller in _editedAmounts.values) {
      controller.dispose();
    }
    _editedAmounts.clear();
    _editedProviders.clear();
    _editedProducts.clear();
    setState(() => _editMode = false);
  }

  /// Shows a bottom sheet with provider & product dropdowns (2 columns) to apply
  /// to all batch items. Returns both the chosen [BillProvider] and optional [BillProduct],
  /// or `null` if dismissed.
  Future<({BillProvider provider, BillProduct? product})?>
  _showBatchProviderSelectorBottomSheet() async {
    if (controller.model.providers.isEmpty) {
      await controller.loadProviders(_batch!.currency!);
      if (controller.model.providers.isEmpty) {
        _showError('No providers available.');
        return null;
      }
    }

    final theme = Theme.of(context);
    final isDark = theme.brightness == Brightness.dark;
    final allProviders = controller.model.providers;

    // Selected provider & product state inside the sheet
    BillProvider? selectedProvider;
    BillProduct? selectedProduct;
    bool isLoadingProducts = false;

    return showModalBottomSheet<
      ({BillProvider provider, BillProduct? product})
    >(
      context: context,
      isScrollControlled: true,
      backgroundColor: Colors.transparent,
      builder: (sheetContext) {
        return StatefulBuilder(
          builder: (innerContext, setSheetState) {
            // Resolve products based on selected provider
            final providerProducts = selectedProvider != null
                ? (controller.model.providerProducts[selectedProvider!.id] ??
                      <BillProduct>[])
                : <BillProduct>[];

            return Container(
              decoration: BoxDecoration(
                color: isDark ? const Color(0xFF1A1A2E) : Colors.white,
                borderRadius: const BorderRadius.vertical(
                  top: Radius.circular(24),
                ),
              ),
              padding: EdgeInsets.only(
                left: 20,
                right: 20,
                top: 12,
                bottom: MediaQuery.of(innerContext).viewInsets.bottom + 24,
              ),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Center(
                    child: Container(
                      width: 40,
                      height: 4,
                      margin: const EdgeInsets.only(bottom: 16),
                      decoration: BoxDecoration(
                        color: Colors.grey.shade300,
                        borderRadius: BorderRadius.circular(2),
                      ),
                    ),
                  ),
                  Text(
                    'Apply to All Items',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.w700,
                      color: isDark ? Colors.white : Colors.black87,
                    ),
                  ),
                  const SizedBox(height: 6),
                  Text(
                    'Choose provider & product for every batch item.',
                    style: TextStyle(
                      fontSize: 13,
                      fontWeight: FontWeight.w500,
                      color: Colors.grey.shade500,
                    ),
                  ),
                  const SizedBox(height: 18),
                  // Two-column: Provider + Product dropdowns
                  Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      // Provider dropdown
                      Expanded(
                        child: _buildBatchProviderDropdown(
                          providers: allProviders,
                          selectedProvider: selectedProvider,
                          isDark: isDark,
                          theme: theme,
                          onChanged: (provider) {
                            setSheetState(() {
                              selectedProvider = provider;
                              selectedProduct = null;
                              isLoadingProducts = true;
                            });
                            // Load products for the selected provider
                            if (!controller.model.providerProducts.containsKey(
                              provider.id,
                            )) {
                              controller.loadProviderProducts(provider.id).then(
                                (_) {
                                  if (mounted) {
                                    setSheetState(() {
                                      isLoadingProducts = false;
                                    });
                                  }
                                },
                              );
                            } else {
                              setSheetState(() {
                                isLoadingProducts = false;
                              });
                            }
                          },
                        ),
                      ),
                      const SizedBox(width: 8),
                      // Product dropdown
                      Expanded(
                        child: _buildBatchProductDropdown(
                          products: providerProducts,
                          selectedProduct: selectedProduct,
                          isLoading: isLoadingProducts,
                          isDark: isDark,
                          theme: theme,
                          onChanged: (product) {
                            setSheetState(() {
                              selectedProduct = product;
                            });
                          },
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 18),
                  SizedBox(
                    width: double.infinity,
                    child: ElevatedButton(
                      onPressed: () {
                        final provider = selectedProvider ?? allProviders.first;
                        Navigator.of(
                          innerContext,
                        ).pop((provider: provider, product: selectedProduct));
                      },
                      style: ElevatedButton.styleFrom(
                        backgroundColor: theme.colorScheme.primary,
                        foregroundColor: Colors.white,
                        padding: const EdgeInsets.symmetric(vertical: 14),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(14),
                        ),
                        elevation: 0,
                      ),
                      child: const Text(
                        'Apply',
                        style: TextStyle(
                          fontSize: 15,
                          fontWeight: FontWeight.w700,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            );
          },
        );
      },
    );
  }

  /// Builds the provider dropdown used in the batch-level bottom sheet.
  Widget _buildBatchProviderDropdown({
    required List<BillProvider> providers,
    required BillProvider? selectedProvider,
    required bool isDark,
    required ThemeData theme,
    required ValueChanged<BillProvider> onChanged,
  }) {
    return SizedBox(
      height: 48,
      child: DropdownButtonFormField<BillProvider>(
        value: selectedProvider,
        isExpanded: true,
        hint: const Text('Select Provider'),
        decoration: InputDecoration(
          labelText: 'Provider',
          labelStyle: TextStyle(fontSize: 12, color: Colors.grey.shade500),
          filled: true,
          fillColor: isDark
              ? Colors.white.withValues(alpha: 0.06)
              : Colors.grey.shade50,
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8),
            borderSide: BorderSide.none,
          ),
          contentPadding: const EdgeInsets.symmetric(
            horizontal: 10,
            vertical: 6,
          ),
          isDense: true,
        ),
        items: providers.map((provider) {
          return DropdownMenuItem<BillProvider>(
            value: provider,
            child: Text(
              provider.label,
              style: TextStyle(
                fontSize: 13,
                color: isDark ? Colors.white : Colors.black87,
              ),
              overflow: TextOverflow.ellipsis,
            ),
          );
        }).toList(),
        onChanged: (provider) {
          if (provider != null) {
            onChanged(provider);
          }
        },
      ),
    );
  }

  /// Builds the product dropdown used in the batch-level bottom sheet.
  Widget _buildBatchProductDropdown({
    required List<BillProduct> products,
    required BillProduct? selectedProduct,
    required bool isLoading,
    required bool isDark,
    required ThemeData theme,
    required ValueChanged<BillProduct> onChanged,
  }) {
    if (isLoading) {
      return SizedBox(
        height: 48,
        child: Row(
          children: [
            const SizedBox(
              width: 20,
              height: 20,
              child: CircularProgressIndicator(strokeWidth: 2),
            ),
            const SizedBox(width: 8),
            Text(
              'Loading...',
              style: TextStyle(fontSize: 12, color: Colors.grey.shade500),
            ),
          ],
        ),
      );
    }

    if (products.isEmpty) {
      return SizedBox(
        height: 48,
        child: InputDecorator(
          decoration: InputDecoration(
            labelText: 'Product',
            labelStyle: TextStyle(fontSize: 12, color: Colors.grey.shade500),
            filled: true,
            fillColor: isDark
                ? Colors.white.withValues(alpha: 0.06)
                : Colors.grey.shade50,
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
              borderSide: BorderSide.none,
            ),
            contentPadding: const EdgeInsets.symmetric(
              horizontal: 10,
              vertical: 6,
            ),
            isDense: true,
          ),
          child: Text(
            'No products',
            style: TextStyle(fontSize: 13, color: Colors.grey.shade500),
          ),
        ),
      );
    }

    return SizedBox(
      height: 48,
      child: DropdownButtonFormField<BillProduct>(
        value: selectedProduct ?? products.first,
        isExpanded: true,
        decoration: InputDecoration(
          labelText: 'Product',
          labelStyle: TextStyle(fontSize: 12, color: Colors.grey.shade500),
          filled: true,
          fillColor: isDark
              ? Colors.white.withValues(alpha: 0.06)
              : Colors.grey.shade50,
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8),
            borderSide: BorderSide.none,
          ),
          contentPadding: const EdgeInsets.symmetric(
            horizontal: 10,
            vertical: 6,
          ),
          isDense: true,
        ),
        items: products.map((product) {
          return DropdownMenuItem<BillProduct>(
            value: product,
            child: Text(
              product.displayName.isNotEmpty
                  ? product.displayName
                  : product.name,
              style: TextStyle(
                fontSize: 13,
                color: isDark ? Colors.white : Colors.black87,
              ),
              overflow: TextOverflow.ellipsis,
            ),
          );
        }).toList(),
        onChanged: (product) {
          if (product != null) {
            onChanged(product);
          }
        },
      ),
    );
  }

  /// Shows a bottom sheet to pick a product and apply it to all batch items.
  /// Loads products for the provider if needed.
  /// Returns the chosen [BillProduct], or `null` if dismissed.
  Future<BillProduct?> _showBatchProductSelectorBottomSheet(
    BillProvider provider,
  ) async {
    // Load products for this provider if not already cached
    if (!controller.model.providerProducts.containsKey(provider.id)) {
      final result = await controller.loadProviderProducts(provider.id);
      if (!mounted) return null;
      if (!result.isSuccess) {
        _showError(result.error ?? 'Failed to load products.');
        return null;
      }
    }

    final products = controller.model.providerProducts[provider.id] ?? [];
    if (products.isEmpty) {
      _showError('No products available for this provider.');
      return null;
    }

    final theme = Theme.of(context);
    final isDark = theme.brightness == Brightness.dark;
    BillProduct? picked;

    return showModalBottomSheet<BillProduct>(
      context: context,
      isScrollControlled: true,
      backgroundColor: Colors.transparent,
      builder: (sheetContext) {
        return StatefulBuilder(
          builder: (innerContext, setSheetState) {
            return Container(
              decoration: BoxDecoration(
                color: isDark ? const Color(0xFF1A1A2E) : Colors.white,
                borderRadius: const BorderRadius.vertical(
                  top: Radius.circular(24),
                ),
              ),
              padding: EdgeInsets.only(
                left: 20,
                right: 20,
                top: 12,
                bottom: MediaQuery.of(innerContext).viewInsets.bottom + 24,
              ),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Center(
                    child: Container(
                      width: 40,
                      height: 4,
                      margin: const EdgeInsets.only(bottom: 16),
                      decoration: BoxDecoration(
                        color: Colors.grey.shade300,
                        borderRadius: BorderRadius.circular(2),
                      ),
                    ),
                  ),
                  Text(
                    'Apply Product to All Items',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.w700,
                      color: isDark ? Colors.white : Colors.black87,
                    ),
                  ),
                  const SizedBox(height: 6),
                  Text(
                    'Choose a product to set for every batch item.',
                    style: TextStyle(
                      fontSize: 13,
                      fontWeight: FontWeight.w500,
                      color: Colors.grey.shade500,
                    ),
                  ),
                  const SizedBox(height: 18),
                  ConstrainedBox(
                    constraints: BoxConstraints(
                      maxHeight: MediaQuery.of(innerContext).size.height * 0.4,
                    ),
                    child: SingleChildScrollView(
                      child: Column(
                        children: products.map((product) {
                          final isSelected = picked == product;
                          return Padding(
                            padding: const EdgeInsets.only(bottom: 10),
                            child: GestureDetector(
                              onTap: () {
                                setSheetState(() => picked = product);
                              },
                              child: AnimatedContainer(
                                duration: const Duration(milliseconds: 200),
                                curve: Curves.easeOutCubic,
                                padding: const EdgeInsets.symmetric(
                                  horizontal: 14,
                                  vertical: 12,
                                ),
                                decoration: BoxDecoration(
                                  color: isSelected
                                      ? theme.colorScheme.primary.withValues(
                                          alpha: 0.12,
                                        )
                                      : (isDark
                                            ? Colors.white.withValues(
                                                alpha: 0.05,
                                              )
                                            : Colors.grey.shade50),
                                  borderRadius: BorderRadius.circular(14),
                                  border: Border.all(
                                    color: isSelected
                                        ? theme.colorScheme.primary
                                        : (isDark
                                              ? Colors.white.withValues(
                                                  alpha: 0.08,
                                                )
                                              : Colors.grey.shade200),
                                    width: isSelected ? 2 : 1,
                                  ),
                                ),
                                child: Row(
                                  children: [
                                    Container(
                                      width: 36,
                                      height: 36,
                                      decoration: BoxDecoration(
                                        color: theme.colorScheme.primary
                                            .withValues(alpha: 0.1),
                                        borderRadius: BorderRadius.circular(8),
                                      ),
                                      child: Icon(
                                        Icons.inventory_2_rounded,
                                        size: 20,
                                        color: theme.colorScheme.primary,
                                      ),
                                    ),
                                    const SizedBox(width: 12),
                                    Expanded(
                                      child: Column(
                                        crossAxisAlignment:
                                            CrossAxisAlignment.start,
                                        children: [
                                          Text(
                                            product.displayName.isNotEmpty
                                                ? product.displayName
                                                : product.name,
                                            style: TextStyle(
                                              fontSize: 14,
                                              fontWeight: FontWeight.w600,
                                              color: isDark
                                                  ? Colors.white
                                                  : Colors.black87,
                                            ),
                                          ),
                                          if (product.defaultAmount > 0)
                                            Text(
                                              '\$${product.defaultAmount.toStringAsFixed(2)}',
                                              style: TextStyle(
                                                fontSize: 12,
                                                color: Colors.grey.shade500,
                                              ),
                                            ),
                                        ],
                                      ),
                                    ),
                                    Icon(
                                      isSelected
                                          ? Icons.radio_button_checked
                                          : Icons.radio_button_unchecked,
                                      color: isSelected
                                          ? theme.colorScheme.primary
                                          : Colors.grey.shade400,
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          );
                        }).toList(),
                      ),
                    ),
                  ),
                  const SizedBox(height: 8),
                  SizedBox(
                    width: double.infinity,
                    child: ElevatedButton(
                      onPressed: () {
                        Navigator.of(
                          innerContext,
                        ).pop(picked ?? products.first);
                      },
                      style: ElevatedButton.styleFrom(
                        backgroundColor: theme.colorScheme.primary,
                        foregroundColor: Colors.white,
                        padding: const EdgeInsets.symmetric(vertical: 14),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(14),
                        ),
                        elevation: 0,
                      ),
                      child: const Text(
                        'Apply',
                        style: TextStyle(
                          fontSize: 15,
                          fontWeight: FontWeight.w700,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            );
          },
        );
      },
    );
  }

  Future<bool> _saveEdits() async {
    if (controller.model.isActing) return false;
    final batch = _batch;
    if (batch == null || batch.id == null) return false;

    // Check if any edited items are missing a provider label
    final anyMissingProvider = controller.model.batchItems.any((item) {
      if (item.id == null) return false;
      final editedProvider = _editedProviders[item.id];
      return editedProvider == null || editedProvider.label.isEmpty;
    });

    // If any items lack a provider, prompt user to choose one (and optionally a product) for all items
    if (anyMissingProvider) {
      final result = await _showBatchProviderSelectorBottomSheet();
      if (!mounted) return false;
      if (result == null) return false;
      if (!mounted) return false;

      final chosenProvider = result.provider;
      final chosenProduct = result.product;

      // Apply chosen provider to all items
      for (final item in controller.model.batchItems) {
        if (item.id != null) {
          _editedProviders[item.id!] = chosenProvider;
          if (chosenProduct != null) {
            _editedProducts[item.id!] = chosenProduct;
          }
        }
      }

      // Also ensure products are loaded for the chosen provider
      if (!controller.model.providerProducts.containsKey(chosenProvider.id)) {
        await controller.loadProviderProducts(chosenProvider.id);
        if (!mounted) return false;
      }
    }

    final items = <GroupBatchItemUpdateRequest>[];
    for (final item in controller.model.batchItems) {
      if (item.id == null) continue;

      final amountStr = _editedAmounts[item.id]?.text ?? '';
      final parsedAmount = double.tryParse(amountStr);
      final provider = _editedProviders[item.id];
      final product = _editedProducts[item.id];

      items.add(
        GroupBatchItemUpdateRequest(
          id: item.id,
          amount: parsedAmount ?? item.amount,
          recipientName: item.recipientName,
          recipientPhone: item.recipientPhone,
          providerImage: provider?.image,
          providerLabel: provider?.label,
          billClientId: provider?.clientId,
          billName: provider?.name,
          billProductName:
              product?.name ??
              product?.displayName ??
              item.billProductName ??
              '',
        ),
      );
    }

    if (items.isEmpty) return false;

    final updateList = GroupBatchItemUpdateList(
      items: items,
      workspaceId: _workspaceId,
    );

    final result = await controller.updateBatchItems(batch.id!, updateList);
    if (!mounted) return false;

    _clearEditState();

    if (result.isSuccess) {
      // Refresh batch items to reflect updated data
      await controller.listBatchItems(batch.id!);
    } else {
      AppSnackBar.showError(context, result.error ?? 'Failed to update items.');
    }
    return true;
  }

  Future<void> _onRefresh() async {
    setState(() {
      _initialLoading = true;
      _loadError = null;
    });
    await _init();
  }

  void _showError(String message) {
    AppSnackBar.showError(context, message);
  }

  void _showFilterSheet() {
    showModalBottomSheet(
      context: context,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
      ),
      builder: (ctx) {
        return StatefulBuilder(
          builder: (context, setSheetState) {
            return Padding(
              padding: const EdgeInsets.all(20),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      const Text(
                        'Filter Items',
                        style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.w600,
                        ),
                      ),
                      const Spacer(),
                      TextButton(
                        onPressed: () {
                          setSheetState(() {
                            controller.model.batchItemsStatusFilter = null;
                            controller.model.batchItemsRecipientAccount = null;
                            _accountController.clear();
                          });
                        },
                        child: const Text('Clear All'),
                      ),
                    ],
                  ),
                  const SizedBox(height: 16),
                  DropdownButtonFormField<String?>(
                    initialValue: controller.model.batchItemsStatusFilter,
                    decoration: InputDecoration(
                      labelText: 'Status',
                      filled: true,
                      fillColor: Colors.grey.shade100,
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(12),
                        borderSide: BorderSide.none,
                      ),
                      contentPadding: const EdgeInsets.symmetric(
                        horizontal: 16,
                        vertical: 14,
                      ),
                    ),
                    items: const [
                      DropdownMenuItem(value: null, child: Text('All')),
                      DropdownMenuItem(
                        value: 'CONFIRMED',
                        child: Text('CONFIRMED'),
                      ),
                      DropdownMenuItem(
                        value: 'INTEGRATED',
                        child: Text('INTEGRATED'),
                      ),
                      DropdownMenuItem(value: 'FAILED', child: Text('FAILED')),
                      DropdownMenuItem(
                        value: 'SKIPPED',
                        child: Text('SKIPPED'),
                      ),
                    ],
                    onChanged: (v) {
                      setSheetState(
                        () => controller.model.batchItemsStatusFilter = v,
                      );
                    },
                  ),
                  const SizedBox(height: 16),
                  TextField(
                    controller: _accountController,
                    decoration: InputDecoration(
                      labelText: 'Account Number',
                      hintText: 'Filter by account number',
                      filled: true,
                      fillColor: Colors.grey.shade100,
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(12),
                        borderSide: BorderSide.none,
                      ),
                      contentPadding: const EdgeInsets.symmetric(
                        horizontal: 16,
                        vertical: 14,
                      ),
                    ),
                  ),
                  const SizedBox(height: 24),
                  SizedBox(
                    width: double.infinity,
                    child: ElevatedButton(
                      onPressed: () {
                        Navigator.of(ctx).pop();
                        final batchId = _batch?.id ?? '';
                        if (batchId.isNotEmpty) {
                          controller.searchBatchItems(
                            batchId,
                            search: _searchController.text.isNotEmpty
                                ? _searchController.text
                                : null,
                            status: controller.model.batchItemsStatusFilter,
                            account: _accountController.text.isNotEmpty
                                ? _accountController.text
                                : null,
                          );
                        }
                      },
                      child: const Text('Apply Filters'),
                    ),
                  ),
                  const SizedBox(height: 8),
                ],
              ),
            );
          },
        );
      },
    );
  }

  // ──────────────────────────────────────────────────────────────
  // Build
  // ──────────────────────────────────────────────────────────────
  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final isDark = theme.brightness == Brightness.dark;

    return Scaffold(
      backgroundColor: isDark
          ? const Color(0xFF0D0D0D)
          : const Color(0xFFF8F9FA),
      body: ListenableBuilder(
        listenable: controller,
        builder: (context, _) {
          if (_initialLoading) {
            return const Center(child: CircularProgressIndicator());
          }

          if (_loadError != null) {
            return Center(
              child: Padding(
                padding: const EdgeInsets.all(32),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Icon(
                      Icons.error_outline,
                      size: 48,
                      color: Colors.grey.shade400,
                    ),
                    const SizedBox(height: 16),
                    Text(
                      _loadError!,
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        fontSize: 15,
                        color: Colors.grey.shade600,
                      ),
                    ),
                    const SizedBox(height: 16),
                    ElevatedButton(
                      onPressed: () {
                        setState(() {
                          _initialLoading = true;
                          _loadError = null;
                        });
                        _init();
                      },
                      child: const Text('Retry'),
                    ),
                  ],
                ),
              ),
            );
          }

          final batch = _batch;
          if (batch == null) {
            return const Center(child: Text('Batch not found'));
          }

          return _buildContent(batch, theme, isDark);
        },
      ),
    );
  }

  Widget _buildContent(GroupBatch batch, ThemeData theme, bool isDark) {
    return CustomScrollView(
      controller: _scrollController,
      physics: const BouncingScrollPhysics(),
      slivers: [
        SliverAppBar(
          pinned: false,
          floating: true,
          stretch: true,
          stretchTriggerOffset: 80.0,
          expandedHeight: 60.0,
          collapsedHeight: 60.0,
          surfaceTintColor: Colors.transparent,
          backgroundColor: isDark
              ? const Color(0xFF0D0D0D)
              : const Color(0xFFF8F9FA),
          leading: context.canPop()
              ? IconButton(
                  onPressed: () => context.pop(),
                  icon: const Icon(Icons.arrow_back_rounded),
                  color: theme.colorScheme.primary,
                )
              : const SizedBox.shrink(),
          title: Text(
            batch.description ?? 'Batch',
            style: TextStyle(
              fontWeight: FontWeight.w700,
              fontSize: 20,
              color: isDark ? Colors.white : Colors.black87,
            ),
          ),
          centerTitle: true,
        ),
        SliverToBoxAdapter(
          child: Padding(
            padding: const EdgeInsets.fromLTRB(16, 4, 16, 32),
            child: LayoutBuilder(
              builder: (context, constraints) {
                return Center(
                  child: SizedBox(
                    width: constraints.maxWidth > ResponsivePolicy.md
                        ? ResponsivePolicy.md.toDouble()
                        : constraints.maxWidth,
                    child: FadeTransition(
                      opacity: _entryFade,
                      child: SlideTransition(
                        position: _entrySlide,
                        child: Column(
                          children: [
                            _buildCompactHeader(batch, theme, isDark),
                            const SizedBox(height: 20),
                            _buildActionBar(theme, isDark),
                            const SizedBox(height: 20),
                            _buildItemsList(batch, theme, isDark),
                          ],
                        ),
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
        ),
      ],
    );
  }

  // ──────────────────────────────────────────────────────────────
  // Compact Header — concise row-based summary
  // ──────────────────────────────────────────────────────────────
  Widget _buildCompactHeader(GroupBatch batch, ThemeData theme, bool isDark) {
    final currency = batch.currency ?? 'USD';
    final value = batch.value;
    final status = batch.status ?? 'UNKNOWN';
    final createdAt = batch.createdAt;
    final parsedDate = createdAt != null ? DateTime.tryParse(createdAt) : null;
    final totalCount = batch.count ?? 0;
    final successCount = batch.successfulCount ?? 0;
    final failedCount = batch.failedCount ?? 0;

    return Container(
      width: double.infinity,
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: isDark
              ? [const Color(0xFF1E1E3A), const Color(0xFF1A1A2E)]
              : [Colors.white, const Color(0xFFF8F9FC)],
        ),
        borderRadius: BorderRadius.circular(20),
        border: Border.all(
          color: isDark
              ? Colors.white.withValues(alpha: 0.08)
              : Colors.grey.shade200,
        ),
        boxShadow: [
          BoxShadow(
            color: isDark
                ? Colors.black.withValues(alpha: 0.35)
                : Colors.black.withValues(alpha: 0.05),
            blurRadius: 20,
            offset: const Offset(0, 6),
          ),
        ],
      ),
      padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 20),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // ── Section 1: Status badge + Refresh ──
          Row(
            children: [
              StatusChip(status: status),
              const Spacer(),
              _buildRefreshButton(theme, isDark),
            ],
          ),
          const SizedBox(height: 20),

          // ── Section 2: Hero Amount ⫶ the expressive centre-piece ──
          Row(
            crossAxisAlignment: CrossAxisAlignment.baseline,
            textBaseline: TextBaseline.alphabetic,
            children: [
              Container(
                padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                decoration: BoxDecoration(
                  color: theme.colorScheme.primary.withValues(alpha: 0.1),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Text(
                  currency,
                  style: TextStyle(
                    fontSize: 15,
                    fontWeight: FontWeight.w700,
                    color: theme.colorScheme.primary,
                    letterSpacing: 0.5,
                  ),
                ),
              ),
              const SizedBox(width: 12),
              Text(
                value?.toStringAsFixed(2) ?? '—',
                style: TextStyle(
                  fontSize: 36,
                  fontWeight: FontWeight.w800,
                  color: isDark ? Colors.white : Colors.black87,
                  height: 1.0,
                  letterSpacing: -1.0,
                ),
              ),
            ],
          ),
          const SizedBox(height: 14),

          // ── Section 3: Date display ──
          if (parsedDate != null)
            Row(
              children: [
                Container(
                  width: 40,
                  height: 40,
                  decoration: BoxDecoration(
                    color: isDark
                        ? Colors.white.withValues(alpha: 0.05)
                        : Colors.grey.shade100,
                    borderRadius: BorderRadius.circular(10),
                  ),
                  child: Icon(
                    Icons.calendar_today_rounded,
                    size: 18,
                    color: isDark ? Colors.white54 : Colors.grey.shade600,
                  ),
                ),
                const SizedBox(width: 12),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      DateFormat.yMMMMd().format(parsedDate),
                      style: TextStyle(
                        fontSize: 14,
                        fontWeight: FontWeight.w600,
                        color: isDark ? Colors.white70 : Colors.black87,
                      ),
                    ),
                    Text(
                      DateFormat.jm().format(parsedDate),
                      style: TextStyle(
                        fontSize: 12,
                        fontWeight: FontWeight.w400,
                        color: isDark ? Colors.white38 : Colors.grey.shade500,
                      ),
                    ),
                  ],
                ),
              ],
            ),
          if (parsedDate != null) const SizedBox(height: 20),

          // ── Section 4: Stat cards ──
          if (totalCount > 0) ...[
            Row(
              children: [
                Expanded(
                  child: _statCard(
                    icon: Icons.people_rounded,
                    label: 'Total',
                    value: totalCount,
                    color: const Color(0xFF3B82F6),
                    isDark: isDark,
                  ),
                ),
                const SizedBox(width: 10),
                Expanded(
                  child: _statCard(
                    icon: Icons.check_circle_rounded,
                    label: 'Successful',
                    value: successCount,
                    color: const Color(0xFF10B981),
                    isDark: isDark,
                  ),
                ),
                const SizedBox(width: 10),
                Expanded(
                  child: _statCard(
                    icon: Icons.cancel_rounded,
                    label: 'Failed',
                    value: failedCount,
                    color: const Color(0xFFEF4444),
                    isDark: isDark,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 20),
          ],

          // ── Section 5: Divider ──
          Container(
            height: 1,
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: isDark
                    ? [
                        Colors.white.withValues(alpha: 0.0),
                        Colors.white.withValues(alpha: 0.1),
                        Colors.white.withValues(alpha: 0.0),
                      ]
                    : [
                        Colors.grey.shade200,
                        Colors.grey.shade300,
                        Colors.grey.shade200,
                      ],
              ),
            ),
          ),
          const SizedBox(height: 16),

          // ── Section 6: Action buttons ──
          _buildHeaderActionButtons(batch, theme),
        ],
      ),
    );
  }

  Widget _statCard({
    required IconData icon,
    required String label,
    required int value,
    required Color color,
    required bool isDark,
  }) {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 10),
      decoration: BoxDecoration(
        color: isDark
            ? color.withValues(alpha: 0.08)
            : color.withValues(alpha: 0.06),
        borderRadius: BorderRadius.circular(14),
        border: Border.all(
          color: isDark
              ? color.withValues(alpha: 0.18)
              : color.withValues(alpha: 0.12),
        ),
      ),
      child: Column(
        children: [
          Icon(icon, size: 20, color: color),
          const SizedBox(height: 6),
          Text(
            '$value',
            style: TextStyle(
              fontSize: 20,
              fontWeight: FontWeight.w800,
              color: isDark ? Colors.white : Colors.black87,
              height: 1.0,
            ),
          ),
          const SizedBox(height: 2),
          Text(
            label,
            style: TextStyle(
              fontSize: 11,
              fontWeight: FontWeight.w500,
              color: isDark ? Colors.white54 : Colors.grey.shade500,
            ),
          ),
        ],
      ),
    );
  }

  Widget _miniChip({
    required String label,
    required int value,
    required Color color,
    required bool isDark,
  }) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
      decoration: BoxDecoration(
        color: color.withValues(alpha: 0.1),
        borderRadius: BorderRadius.circular(6),
        border: Border.all(color: color.withValues(alpha: 0.25)),
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text(
            '$value',
            style: TextStyle(
              fontSize: 11,
              fontWeight: FontWeight.w700,
              color: color,
            ),
          ),
          const SizedBox(width: 3),
          Text(
            label,
            style: TextStyle(
              fontSize: 10,
              fontWeight: FontWeight.w500,
              color: color.withValues(alpha: 0.8),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildRefreshButton(ThemeData theme, bool isDark) {
    return Material(
      color: Colors.transparent,
      shape: const CircleBorder(),
      child: InkWell(
        customBorder: const CircleBorder(),
        onTap: _onRefresh,
        child: Tooltip(
          message: 'Refresh',
          child: Container(
            width: 30,
            height: 30,
            decoration: BoxDecoration(
              shape: BoxShape.circle,
              color: isDark
                  ? Colors.white.withValues(alpha: 0.08)
                  : Colors.grey.shade100,
              border: Border.all(
                color: isDark
                    ? Colors.white.withValues(alpha: 0.10)
                    : Colors.grey.shade300,
              ),
            ),
            child: Icon(
              Icons.refresh_rounded,
              size: 15,
              color: isDark ? Colors.white70 : Colors.grey.shade700,
            ),
          ),
        ),
      ),
    );
  }

  // ──────────────────────────────────────────────────────────────
  // Header Action Buttons
  // ──────────────────────────────────────────────────────────────
  Widget _buildHeaderActionButtons(GroupBatch batch, ThemeData theme) {
    final status = batch.status?.toUpperCase() ?? '';
    final isActing = controller.model.isActing;
    final canConfirm = ['CREATED', 'CONFIRM_FAILED'].contains(status);
    final canRequest = ['CONFIRMED_ALL', 'REQUEST_FAILED'].contains(status);
    final canPoll =
        ['REQUESTED', 'POLL_FAILED'].contains(status) || _showPollButton;
    final canDownloadReport = status == 'COMPLETED';

    final List<Widget> buttons = [];

    if (canConfirm) {
      buttons.add(
        _prominentActionButton(
          icon: Icons.check_circle_rounded,
          label: 'Confirm Batch',
          subtitle: 'Verify items and prepare for payment',
          color: theme.colorScheme.primary,
          isLoading: isActing,
          onPressed: _onConfirm,
        ),
      );
    } else if (canRequest) {
      buttons.add(
        _prominentPaymentButton(
          image: _batch!.paymentProcessorImage ?? '',
          label: 'Push Payment',
          subtitle: 'Send payments to all recipients',
          color: const Color(0xFF10B981),
          isLoading: isActing,
          onPressed: _onRequest,
        ),
      );
    } else if (canPoll) {
      buttons.add(
        _prominentActionButton(
          icon: Icons.refresh_rounded,
          label: 'Check Status',
          subtitle: 'Poll for the latest payment status',
          color: const Color(0xFFF59E0B),
          isLoading: isActing,
          onPressed: _onPoll,
        ),
      );
    }

    if (canDownloadReport) {
      buttons.add(
        _prominentActionButton(
          icon: Icons.download_rounded,
          label: 'Download Report',
          subtitle: 'Export batch results as CSV',
          color: const Color(0xFF6366F1),
          isLoading: isActing,
          onPressed: _onDownloadReport,
        ),
      );
    }

    if (buttons.isEmpty) return const SizedBox.shrink();

    return Column(
      children: buttons
          .map(
            (btn) =>
                Padding(padding: const EdgeInsets.only(bottom: 10), child: btn),
          )
          .toList(),
    );
  }

  Widget _prominentActionButton({
    required IconData icon,
    required String label,
    required String subtitle,
    required Color color,
    required bool isLoading,
    required VoidCallback onPressed,
  }) {
    return Material(
      color: Colors.transparent,
      child: InkWell(
        borderRadius: BorderRadius.circular(14),
        onTap: isLoading ? null : onPressed,
        child: Container(
          width: double.infinity,
          padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16),
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [
                color.withValues(alpha: 0.12),
                color.withValues(alpha: 0.06),
              ],
            ),
            borderRadius: BorderRadius.circular(14),
            border: Border.all(color: color.withValues(alpha: 0.25)),
          ),
          child: Row(
            children: [
              Container(
                width: 44,
                height: 44,
                decoration: BoxDecoration(
                  color: color.withValues(alpha: 0.15),
                  borderRadius: BorderRadius.circular(12),
                ),
                child: isLoading
                    ? Padding(
                        padding: const EdgeInsets.all(10),
                        child: CircularProgressIndicator(
                          strokeWidth: 2.5,
                          valueColor: AlwaysStoppedAnimation<Color>(color),
                        ),
                      )
                    : Icon(icon, size: 22, color: color),
              ),
              const SizedBox(width: 14),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      label,
                      style: TextStyle(
                        fontSize: 15,
                        fontWeight: FontWeight.w700,
                        color: color,
                      ),
                    ),
                    const SizedBox(height: 2),
                    Text(
                      subtitle,
                      style: TextStyle(
                        fontSize: 12,
                        fontWeight: FontWeight.w400,
                        color: color.withValues(alpha: 0.7),
                      ),
                    ),
                  ],
                ),
              ),
              Icon(
                Icons.arrow_forward_ios_rounded,
                size: 16,
                color: color.withValues(alpha: 0.5),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _prominentPaymentButton({
    required String image,
    required String label,
    required String subtitle,
    required Color color,
    required bool isLoading,
    required VoidCallback onPressed,
  }) {
    return Material(
      color: Colors.transparent,
      child: InkWell(
        borderRadius: BorderRadius.circular(14),
        onTap: isLoading ? null : onPressed,
        child: Container(
          width: double.infinity,
          padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16),
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [
                color.withValues(alpha: 0.12),
                color.withValues(alpha: 0.06),
              ],
            ),
            borderRadius: BorderRadius.circular(14),
            border: Border.all(color: color.withValues(alpha: 0.25)),
          ),
          child: Row(
            children: [
              Container(
                width: 44,
                height: 44,
                decoration: BoxDecoration(
                  color: Colors.white.withValues(alpha: 0.2),
                  borderRadius: BorderRadius.circular(12),
                ),
                padding: const EdgeInsets.all(8),
                child: isLoading
                    ? CircularProgressIndicator(
                        strokeWidth: 2.5,
                        valueColor: AlwaysStoppedAnimation<Color>(color),
                      )
                    : ClipRRect(
                        borderRadius: BorderRadius.circular(6),
                        child: Image.asset(
                          'assets/$image',
                          width: 28,
                          height: 28,
                          errorBuilder: (_, __, ___) => Icon(
                            Icons.payment_rounded,
                            size: 22,
                            color: color,
                          ),
                        ),
                      ),
              ),
              const SizedBox(width: 14),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      label,
                      style: TextStyle(
                        fontSize: 15,
                        fontWeight: FontWeight.w700,
                        color: color,
                      ),
                    ),
                    const SizedBox(height: 2),
                    Text(
                      subtitle,
                      style: TextStyle(
                        fontSize: 12,
                        fontWeight: FontWeight.w400,
                        color: color.withValues(alpha: 0.7),
                      ),
                    ),
                  ],
                ),
              ),
              Icon(
                Icons.arrow_forward_ios_rounded,
                size: 16,
                color: color.withValues(alpha: 0.5),
              ),
            ],
          ),
        ),
      ),
    );
  }

  // ──────────────────────────────────────────────────────────────
  // Action Bar — search, filter, edit, refresh
  // ──────────────────────────────────────────────────────────────
  Widget _buildActionBar(ThemeData theme, bool isDark) {
    final hasFilters =
        controller.model.batchItemsStatusFilter != null ||
        controller.model.batchItemsRecipientAccount != null;
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Expanded(
              child: Padding(
                padding: const EdgeInsets.only(right: 8.0),
                child: TextFormField(
                  controller: _searchController,
                  decoration: InputDecoration(
                    hintText: 'Search by recipient name',
                    prefixIcon: const Icon(Icons.search, size: 20),
                    suffixIcon: _searchController.text.isNotEmpty
                        ? IconButton(
                            icon: const Icon(Icons.clear, size: 18),
                            onPressed: () {
                              _searchController.clear();
                              final batchId = _batch?.id ?? '';
                              if (batchId.isNotEmpty) {
                                controller.searchBatchItems(
                                  batchId,
                                  status:
                                      controller.model.batchItemsStatusFilter,
                                  account: controller
                                      .model
                                      .batchItemsRecipientAccount,
                                );
                              }
                            },
                          )
                        : null,
                    filled: true,
                    fillColor: Colors.grey.shade100,
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(12),
                      borderSide: BorderSide.none,
                    ),
                    focusedBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(12),
                      borderSide: BorderSide(color: theme.colorScheme.primary),
                    ),
                    contentPadding: const EdgeInsets.symmetric(
                      horizontal: 12,
                      vertical: 10,
                    ),
                  ),
                  style: const TextStyle(fontSize: 14),
                  textInputAction: TextInputAction.search,
                  onFieldSubmitted: (value) {
                    final batchId = _batch?.id ?? '';
                    if (batchId.isNotEmpty) {
                      controller.searchBatchItems(
                        batchId,
                        search: value.isNotEmpty ? value : null,
                        status: controller.model.batchItemsStatusFilter,
                        account: controller.model.batchItemsRecipientAccount,
                      );
                    }
                  },
                ),
              ),
            ),
            if (_editMode)
              Padding(
                padding: const EdgeInsets.only(right: 8.0),
                child: _saveButton(theme),
              ),
            if (!_editMode &&
                _editableStatuses.contains(_batch?.status?.toUpperCase() ?? ''))
              Padding(
                padding: const EdgeInsets.only(right: 8.0),
                child: _actionIconButton(
                  icon: Icons.edit_rounded,
                  tooltip: 'Edit items',
                  isActive: false,
                  color: Colors.grey.shade700,
                  bgColor: Colors.grey.shade100,
                  onPressed: _toggleEditMode,
                ),
              ),
            if (_editMode)
              Padding(
                padding: const EdgeInsets.only(right: 8.0),
                child: _actionIconButton(
                  icon: Icons.edit_off_rounded,
                  tooltip: 'Cancel item edits',
                  isActive: false,
                  color: Colors.grey.shade700,
                  bgColor: Colors.grey.shade100,
                  onPressed: _toggleEditMode,
                ),
              ),
            Padding(
              padding: const EdgeInsets.only(right: 8.0),
              child: _actionIconButton(
                icon: Icons.filter_list,
                tooltip: 'Filter items',
                isActive: hasFilters,
                color: hasFilters
                    ? theme.colorScheme.primary
                    : Colors.grey.shade700,
                bgColor: hasFilters
                    ? theme.colorScheme.primary
                    : Colors.grey.shade100,
                onPressed: _showFilterSheet,
              ),
            ),
          ],
        ),
        if (hasFilters)
          Padding(
            padding: const EdgeInsets.only(top: 8),
            child: Wrap(
              spacing: 6,
              runSpacing: 6,
              children: [
                if (controller.model.batchItemsStatusFilter != null)
                  _activeFilterChip(
                    'Status: ${controller.model.batchItemsStatusFilter}',
                    () {
                      controller.model.batchItemsStatusFilter = null;
                      final batchId = _batch?.id ?? '';
                      if (batchId.isNotEmpty) {
                        controller.searchBatchItems(
                          batchId,
                          search: _searchController.text.isNotEmpty
                              ? _searchController.text
                              : null,
                          account: controller.model.batchItemsRecipientAccount,
                        );
                      }
                    },
                    theme,
                  ),
                if (controller.model.batchItemsRecipientAccount != null)
                  _activeFilterChip(
                    'Account: ${controller.model.batchItemsRecipientAccount}',
                    () {
                      controller.model.batchItemsRecipientAccount = null;
                      _accountController.clear();
                      final batchId = _batch?.id ?? '';
                      if (batchId.isNotEmpty) {
                        controller.searchBatchItems(
                          batchId,
                          search: _searchController.text.isNotEmpty
                              ? _searchController.text
                              : null,
                          status: controller.model.batchItemsStatusFilter,
                        );
                      }
                    },
                    theme,
                  ),
              ],
            ),
          ),
      ],
    );
  }

  Widget _saveButton(ThemeData theme) {
    final isSaving = controller.model.isActing;
    return Container(
      decoration: BoxDecoration(
        color: isSaving
            ? theme.colorScheme.primary.withValues(alpha: 0.1)
            : Colors.grey.shade100,
        borderRadius: BorderRadius.circular(12),
      ),
      child: IconButton(
        icon: isSaving
            ? SizedBox(
                width: 20,
                height: 20,
                child: CircularProgressIndicator(
                  strokeWidth: 2.5,
                  valueColor: AlwaysStoppedAnimation<Color>(
                    theme.colorScheme.primary,
                  ),
                ),
              )
            : Icon(Icons.save_rounded, color: Colors.grey.shade700, size: 20),
        tooltip: isSaving ? 'Saving...' : 'Save',
        onPressed: isSaving
            ? null
            : () async {
                final result = await _saveEdits();
                if (!result) {
                  // Handle save failure
                }
              },
        constraints: const BoxConstraints(minWidth: 42, minHeight: 42),
        padding: EdgeInsets.zero,
      ),
    );
  }

  Widget _actionIconButton({
    required IconData icon,
    required String tooltip,
    required bool isActive,
    required Color color,
    required Color bgColor,
    required VoidCallback onPressed,
  }) {
    return Container(
      decoration: BoxDecoration(
        color: bgColor,
        borderRadius: BorderRadius.circular(12),
      ),
      child: IconButton(
        icon: Icon(icon, color: color, size: 20),
        tooltip: tooltip,
        onPressed: onPressed,
        constraints: const BoxConstraints(minWidth: 42, minHeight: 42),
        padding: EdgeInsets.zero,
      ),
    );
  }

  Widget _activeFilterChip(
    String label,
    VoidCallback onRemove,
    ThemeData theme,
  ) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
      decoration: BoxDecoration(
        color: theme.colorScheme.primary.withAlpha(20),
        borderRadius: BorderRadius.circular(20),
        border: Border.all(color: theme.colorScheme.primary.withAlpha(60)),
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text(
            label,
            style: TextStyle(
              fontSize: 12,
              color: theme.colorScheme.primary,
              fontWeight: FontWeight.w500,
            ),
          ),
          const SizedBox(width: 4),
          InkWell(
            onTap: onRemove,
            borderRadius: BorderRadius.circular(10),
            child: Padding(
              padding: const EdgeInsets.all(2),
              child: Icon(
                Icons.close,
                size: 14,
                color: theme.colorScheme.primary,
              ),
            ),
          ),
        ],
      ),
    );
  }

  // ──────────────────────────────────────────────────────────────
  // Items List
  // ──────────────────────────────────────────────────────────────
  Widget _buildItemsList(GroupBatch batch, ThemeData theme, bool isDark) {
    if (controller.model.isLoading && controller.model.batchItems.isEmpty) {
      return const Center(
        child: Padding(
          padding: EdgeInsets.all(32),
          child: CircularProgressIndicator(),
        ),
      );
    }

    if (controller.model.batchItems.isEmpty) {
      return Padding(
        padding: const EdgeInsets.symmetric(vertical: 40),
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Icon(Icons.inbox_outlined, size: 48, color: Colors.grey.shade400),
              const SizedBox(height: 12),
              Text(
                'No items found.',
                style: TextStyle(
                  fontSize: 15,
                  color: Colors.grey.shade500,
                  fontWeight: FontWeight.w500,
                ),
              ),
            ],
          ),
        ),
      );
    }

    final totalPages = controller.model.batchItemsTotalPages;
    final currentPage = controller.model.batchItemsCurrentPage;
    final totalElements = controller.model.batchItemsTotalElements;
    final isLoadingMore = controller.model.batchItemsIsLoadingMore;
    final hasMultiplePages = totalPages > 1;

    // Calculate the range of items being shown (1-based)
    final pageSize = controller.model.batchItemsPageSize;
    final startItem = totalElements > 0 ? (currentPage * pageSize) + 1 : 0;
    final endItem = (startItem + controller.model.batchItems.length - 1).clamp(
      0,
      totalElements,
    );

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        if (totalElements > 0)
          Padding(
            padding: const EdgeInsets.only(bottom: 8),
            child: Text(
              'Showing $startItem–$endItem of $totalElements items',
              style: TextStyle(
                fontSize: 12,
                fontWeight: FontWeight.w500,
                color: Colors.grey.shade500,
              ),
            ),
          ),
        ...controller.model.batchItems.map(
          (item) => Padding(
            padding: const EdgeInsets.only(bottom: 8),
            child: _buildItemCard(item, batch, theme, isDark),
          ),
        ),
        if (isLoadingMore)
          const Padding(
            padding: EdgeInsets.symmetric(vertical: 16),
            child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
          ),
        if (hasMultiplePages) _buildPaginationControls(batch, theme, isDark),
      ],
    );
  }

  /// Builds prev/next pagination controls for batch items.
  Widget _buildPaginationControls(
    GroupBatch batch,
    ThemeData theme,
    bool isDark,
  ) {
    final totalPages = controller.model.batchItemsTotalPages;
    final currentPage = controller.model.batchItemsCurrentPage;
    final isLoadingMore = controller.model.batchItemsIsLoadingMore;
    final batchId = batch.id ?? '';

    return Column(
      children: [
        const SizedBox(height: 4),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Row(
              children: [
                _paginationButton(
                  icon: Icons.chevron_left_rounded,
                  tooltip: 'Previous page',
                  enabled: currentPage > 0 && !isLoadingMore,
                  onTap: () {
                    if (batchId.isNotEmpty) {
                      controller.goToBatchItemsPage(batchId, currentPage - 1);
                    }
                  },
                  theme: theme,
                  isDark: isDark,
                ),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 16),
                  child: Text(
                    'Page ${currentPage + 1} of $totalPages',
                    style: TextStyle(
                      fontSize: 13,
                      fontWeight: FontWeight.w600,
                      color: isDark ? Colors.white70 : Colors.grey.shade700,
                    ),
                  ),
                ),
                _paginationButton(
                  icon: Icons.chevron_right_rounded,
                  tooltip: 'Next page',
                  enabled: currentPage < totalPages - 1 && !isLoadingMore,
                  onTap: () {
                    if (batchId.isNotEmpty) {
                      controller.goToBatchItemsPage(batchId, currentPage + 1);
                    }
                  },
                  theme: theme,
                  isDark: isDark,
                ),
              ],
            ),
            _buildPageSizeSelector(batch, theme, isDark),
          ],
        ),
      ],
    );
  }

  /// Builds a dropdown to select the number of items per page.
  Widget _buildPageSizeSelector(
    GroupBatch batch,
    ThemeData theme,
    bool isDark,
  ) {
    final pageSize = controller.model.batchItemsPageSize;
    final isLoadingMore = controller.model.batchItemsIsLoadingMore;
    final batchId = batch.id ?? '';

    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          'Items per page: ',
          style: TextStyle(
            fontSize: 12,
            fontWeight: FontWeight.w500,
            color: isDark ? Colors.white54 : Colors.grey.shade500,
          ),
        ),
        Container(
          height: 32,
          padding: const EdgeInsets.symmetric(horizontal: 8),
          decoration: BoxDecoration(
            color: isDark
                ? Colors.white.withValues(alpha: 0.06)
                : Colors.grey.shade50,
            borderRadius: BorderRadius.circular(8),
            border: Border.all(
              color: isDark
                  ? Colors.white.withValues(alpha: 0.10)
                  : Colors.grey.shade300,
            ),
          ),
          child: DropdownButtonHideUnderline(
            child: DropdownButton<int>(
              value: pageSize,
              isDense: true,
              style: TextStyle(
                fontSize: 13,
                fontWeight: FontWeight.w600,
                color: isDark ? Colors.white70 : Colors.grey.shade700,
              ),
              dropdownColor: isDark ? const Color(0xFF1A1A2E) : Colors.white,
              items: const [
                DropdownMenuItem(value: 10, child: Text('10')),
                DropdownMenuItem(value: 20, child: Text('20')),
                DropdownMenuItem(value: 50, child: Text('50')),
              ],
              onChanged: isLoadingMore
                  ? null
                  : (value) {
                      if (value != null && batchId.isNotEmpty) {
                        controller.setBatchItemsPageSize(value);
                        // Re-load from page 0 with the new page size
                        controller.searchBatchItems(
                          batchId,
                          search:
                              controller.model.batchItemsSearchQuery.isNotEmpty
                              ? controller.model.batchItemsSearchQuery
                              : null,
                          status: controller.model.batchItemsStatusFilter,
                          account: controller.model.batchItemsRecipientAccount,
                        );
                      }
                    },
            ),
          ),
        ),
      ],
    );
  }

  Widget _paginationButton({
    required IconData icon,
    required String tooltip,
    required bool enabled,
    required VoidCallback onTap,
    required ThemeData theme,
    required bool isDark,
  }) {
    return Material(
      color: Colors.transparent,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
      child: InkWell(
        borderRadius: BorderRadius.circular(10),
        onTap: enabled ? onTap : null,
        child: Container(
          width: 40,
          height: 40,
          decoration: BoxDecoration(
            color: enabled
                ? (isDark
                      ? Colors.white.withValues(alpha: 0.08)
                      : Colors.grey.shade100)
                : (isDark
                      ? Colors.white.withValues(alpha: 0.03)
                      : Colors.grey.shade50),
            borderRadius: BorderRadius.circular(10),
            border: Border.all(
              color: enabled
                  ? (isDark
                        ? Colors.white.withValues(alpha: 0.12)
                        : Colors.grey.shade300)
                  : (isDark
                        ? Colors.white.withValues(alpha: 0.04)
                        : Colors.grey.shade200),
            ),
          ),
          child: Icon(
            icon,
            size: 20,
            color: enabled
                ? (isDark ? Colors.white70 : Colors.grey.shade700)
                : (isDark ? Colors.white24 : Colors.grey.shade300),
          ),
        ),
      ),
    );
  }

  Widget _buildItemCard(
    GroupBatchItem item,
    GroupBatch batch,
    ThemeData theme,
    bool isDark,
  ) {
    if (_editMode) {
      return _buildEditableItemCard(item, batch, theme, isDark);
    }

    return Container(
      decoration: BoxDecoration(
        color: isDark ? const Color(0xFF1A1A2E) : Colors.white,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(
          color: isDark
              ? Colors.white.withValues(alpha: 0.06)
              : Colors.grey.shade200,
        ),
        boxShadow: [
          BoxShadow(
            color: isDark
                ? Colors.black.withValues(alpha: 0.2)
                : Colors.black.withValues(alpha: 0.03),
            blurRadius: 8,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14),
      child: Row(
        children: [
          Container(
            width: 44,
            height: 44,
            padding: const EdgeInsets.all(10),
            decoration: BoxDecoration(
              color: theme.colorScheme.primary.withValues(alpha: 0.1),
              borderRadius: BorderRadius.circular(12),
            ),
            child: Center(
              child: Text(
                item.recipientName?.isNotEmpty == true
                    ? item.recipientName![0].toUpperCase()
                    : '?',
                style: TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.w600,
                  color: theme.colorScheme.primary,
                ),
              ),
            ),
          ),
          const SizedBox(width: 14),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  item.recipientName ?? 'Unknown',
                  style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.w600,
                    color: isDark ? Colors.white : Colors.black87,
                  ),
                ),
                const SizedBox(height: 4),
                Text(item.billName ?? ''),
                Row(
                  children: [
                    if (item.billProductName != null &&
                        item.billProductName!.isNotEmpty) ...[
                      Text(
                        item.billProductName!,
                        style: TextStyle(
                          fontSize: 12,
                          fontWeight: FontWeight.w400,
                          color: isDark ? Colors.white54 : Colors.grey.shade500,
                        ),
                      ),
                      Text(
                        ' • ',
                        style: TextStyle(
                          fontSize: 11,
                          color: isDark ? Colors.white24 : Colors.grey.shade400,
                        ),
                      ),
                    ],
                    Text(
                      item.recipientAccount ?? '',
                      style: TextStyle(
                        fontSize: 12,
                        fontWeight: FontWeight.w400,
                        color: isDark ? Colors.white54 : Colors.grey.shade500,
                      ),
                    ),
                  ],
                ),
                Text(item.creditAddress ?? ''),
              ],
            ),
          ),
          const SizedBox(width: 8),
          Column(
            crossAxisAlignment: CrossAxisAlignment.end,
            children: [
              Text(
                '${batch.currency ?? 'USD'} ${item.amount?.toStringAsFixed(2) ?? '—'}',
                style: TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.w700,
                  color: isDark ? Colors.white : Colors.black87,
                ),
              ),
              const SizedBox(height: 2),
              StatusChip(status: item.status ?? 'PENDING'),
            ],
          ),
        ],
      ),
    );
  }

  Widget _buildBillInfoRow(GroupBatchItem item, bool isDark) {
    final parts = <InlineSpan>[];
    if (item.billName != null && item.billName!.isNotEmpty) {
      parts.addAll([
        TextSpan(
          text: item.billName,
          style: TextStyle(
            fontSize: 11,
            fontWeight: FontWeight.w500,
            color: isDark ? Colors.white54 : Colors.grey.shade600,
          ),
        ),
      ]);
    }
    if (item.billProductName != null && item.billProductName!.isNotEmpty) {
      if (parts.isNotEmpty) {
        parts.add(
          TextSpan(
            text: ' • ',
            style: TextStyle(fontSize: 11, color: Colors.grey.shade400),
          ),
        );
      }
      parts.add(
        TextSpan(
          text: item.billProductName,
          style: TextStyle(
            fontSize: 11,
            fontWeight: FontWeight.w400,
            color: isDark ? Colors.white38 : Colors.grey.shade500,
          ),
        ),
      );
    }
    return RichText(text: TextSpan(children: parts));
  }

  Widget _buildProviderBadge(GroupBatchItem item, bool isDark) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Icon(Icons.store_rounded, size: 11, color: Colors.grey.shade400),
        const SizedBox(width: 3),
        Text(
          item.providerLabel ?? '',
          style: TextStyle(
            fontSize: 10,
            fontWeight: FontWeight.w500,
            color: isDark ? Colors.white38 : Colors.grey.shade500,
          ),
        ),
      ],
    );
  }

  /// Edit-mode card: provider dropdown in the middle + editable amount + product dropdown
  Widget _buildEditableItemCard(
    GroupBatchItem item,
    GroupBatch batch,
    ThemeData theme,
    bool isDark,
  ) {
    final providers = controller.model.providers;
    final itemId = item.id ?? '';
    final selectedProvider = _editedProviders[itemId];
    final amountController = _editedAmounts[itemId];

    // Get cached products for the selected provider
    final providerProducts = selectedProvider != null
        ? (controller.model.providerProducts[selectedProvider.id] ??
              <BillProduct>[])
        : <BillProduct>[];
    final selectedProduct = _editedProducts[itemId];

    return Container(
      decoration: BoxDecoration(
        color: isDark ? const Color(0xFF1A1A2E) : Colors.white,
        borderRadius: BorderRadius.circular(14),
        border: Border.all(
          color: _editMode
              ? theme.colorScheme.primary.withValues(alpha: 0.3)
              : (isDark
                    ? Colors.white.withValues(alpha: 0.06)
                    : Colors.grey.shade200),
          width: _editMode ? 1.5 : 1,
        ),
        boxShadow: [
          BoxShadow(
            color: isDark
                ? Colors.black.withValues(alpha: 0.2)
                : Colors.black.withValues(alpha: 0.03),
            blurRadius: 8,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      padding: const EdgeInsets.all(10),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // Top row: Avatar + Name + Amount input
          Row(
            children: [
              CircleAvatar(
                radius: 16,
                backgroundColor: theme.colorScheme.primary.withAlpha(25),
                child: Text(
                  item.recipientName?.isNotEmpty == true
                      ? item.recipientName![0].toUpperCase()
                      : '?',
                  style: TextStyle(
                    fontSize: 12,
                    fontWeight: FontWeight.w600,
                    color: theme.colorScheme.primary,
                  ),
                ),
              ),
              const SizedBox(width: 8),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      item.recipientName ?? 'Unknown',
                      style: TextStyle(
                        fontSize: 13,
                        fontWeight: FontWeight.w600,
                        color: isDark ? Colors.white : Colors.black87,
                      ),
                    ),
                    Text(
                      item.recipientPhone ?? '',
                      style: TextStyle(
                        fontSize: 11,
                        color: Colors.grey.shade500,
                      ),
                    ),
                  ],
                ),
              ),
              // Editable amount field (readOnly when products exist — amount comes from product)
              SizedBox(
                width: 110,
                height: 38,
                child: TextFormField(
                  controller: amountController,
                  readOnly: providerProducts.isNotEmpty,
                  keyboardType: const TextInputType.numberWithOptions(
                    decimal: true,
                  ),
                  style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.w700,
                    color: isDark ? Colors.white : Colors.black87,
                  ),
                  decoration: InputDecoration(
                    prefixText: '${batch.currency ?? 'USD'} ',
                    prefixStyle: TextStyle(
                      fontSize: 11,
                      fontWeight: FontWeight.w500,
                      color: Colors.grey.shade500,
                    ),
                    filled: true,
                    fillColor: isDark
                        ? Colors.white.withValues(alpha: 0.06)
                        : Colors.grey.shade50,
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                      borderSide: BorderSide(color: Colors.grey.shade300),
                    ),
                    focusedBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                      borderSide: BorderSide(color: theme.colorScheme.primary),
                    ),
                    contentPadding: const EdgeInsets.symmetric(
                      horizontal: 8,
                      vertical: 8,
                    ),
                    isDense: true,
                  ),
                ),
              ),
            ],
          ),
          const SizedBox(height: 8),
          // Provider + Product dropdown row (2 columns side by side)
          if (providers.isNotEmpty)
            Row(
              children: [
                Expanded(
                  child: SizedBox(
                    height: 40,
                    child: DropdownButtonFormField<BillProvider>(
                      value: selectedProvider ?? providers.first,
                      isExpanded: true,
                      decoration: InputDecoration(
                        labelText: 'Provider',
                        labelStyle: TextStyle(
                          fontSize: 12,
                          color: Colors.grey.shade500,
                        ),
                        filled: true,
                        fillColor: isDark
                            ? Colors.white.withValues(alpha: 0.06)
                            : Colors.grey.shade50,
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(8),
                          borderSide: BorderSide.none,
                        ),
                        contentPadding: const EdgeInsets.symmetric(
                          horizontal: 10,
                          vertical: 6,
                        ),
                        isDense: true,
                      ),
                      items: providers.map((provider) {
                        return DropdownMenuItem<BillProvider>(
                          value: provider,
                          child: Text(
                            provider.label,
                            style: TextStyle(
                              fontSize: 13,
                              color: isDark ? Colors.white : Colors.black87,
                            ),
                            overflow: TextOverflow.ellipsis,
                          ),
                        );
                      }).toList(),
                      onChanged: (provider) {
                        if (provider != null && itemId.isNotEmpty) {
                          setState(() {
                            _editedProviders[itemId] = provider;
                            // Clear the product when provider changes
                            _editedProducts.remove(itemId);
                          });
                          // Load products for the new provider
                          _onProviderChangedInEdit(itemId, provider);
                        }
                      },
                    ),
                  ),
                ),
                const SizedBox(width: 8),
                Expanded(
                  child: _buildEditProductDropdown(
                    itemId: itemId,
                    provider: selectedProvider ?? providers.first,
                    isDark: isDark,
                    theme: theme,
                  ),
                ),
              ],
            ),
          // Status chip on the bottom-right
          if (item.status != null)
            Padding(
              padding: const EdgeInsets.only(top: 6),
              child: Align(
                alignment: Alignment.centerRight,
                child: StatusChip(status: item.status!),
              ),
            ),
        ],
      ),
    );
  }

  /// Builds the product dropdown for an editable item card.
  /// Shows a loading indicator if products are being fetched.
  Widget _buildEditProductDropdown({
    required String itemId,
    required BillProvider provider,
    required bool isDark,
    required ThemeData theme,
  }) {
    final isLoading = controller.model.loadingProviderProducts.contains(
      provider.id,
    );
    final products =
        (controller.model.providerProducts[provider.id] ?? <BillProduct>[]);
    final selectedProduct = _editedProducts[itemId];

    if (isLoading) {
      return SizedBox(
        height: 40,
        child: Row(
          children: [
            const SizedBox(
              width: 20,
              height: 20,
              child: CircularProgressIndicator(strokeWidth: 2),
            ),
            const SizedBox(width: 8),
            Text(
              'Loading products...',
              style: TextStyle(fontSize: 12, color: Colors.grey.shade500),
            ),
          ],
        ),
      );
    }

    if (products.isEmpty) {
      return SizedBox(
        height: 40,
        child: InputDecorator(
          decoration: InputDecoration(
            labelText: 'Product',
            labelStyle: TextStyle(fontSize: 12, color: Colors.grey.shade500),
            filled: true,
            fillColor: isDark
                ? Colors.white.withValues(alpha: 0.06)
                : Colors.grey.shade50,
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
              borderSide: BorderSide.none,
            ),
            contentPadding: const EdgeInsets.symmetric(
              horizontal: 10,
              vertical: 6,
            ),
            isDense: true,
          ),
          child: Text(
            'No products',
            style: TextStyle(fontSize: 13, color: Colors.grey.shade500),
          ),
        ),
      );
    }

    return SizedBox(
      height: 40,
      child: DropdownButtonFormField<BillProduct>(
        value: selectedProduct ?? products.first,
        isExpanded: true,
        decoration: InputDecoration(
          labelText: 'Product',
          labelStyle: TextStyle(fontSize: 12, color: Colors.grey.shade500),
          filled: true,
          fillColor: isDark
              ? Colors.white.withValues(alpha: 0.06)
              : Colors.grey.shade50,
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8),
            borderSide: BorderSide.none,
          ),
          contentPadding: const EdgeInsets.symmetric(
            horizontal: 10,
            vertical: 6,
          ),
          isDense: true,
        ),
        items: products.map((product) {
          return DropdownMenuItem<BillProduct>(
            value: product,
            child: Text(
              product.displayName.isNotEmpty
                  ? product.displayName
                  : product.name,
              style: TextStyle(
                fontSize: 13,
                color: isDark ? Colors.white : Colors.black87,
              ),
              overflow: TextOverflow.ellipsis,
            ),
          );
        }).toList(),
        onChanged: (product) {
          if (product != null && itemId.isNotEmpty) {
            setState(() {
              _editedProducts[itemId] = product;
              // Update amount field with product's default amount
              if (product.defaultAmount > 0) {
                final ctrl = _editedAmounts[itemId];
                if (ctrl != null) {
                  ctrl.text = product.defaultAmount.toStringAsFixed(2);
                }
              }
            });
          }
        },
      ),
    );
  }

  /// Called when the provider changes for an item in edit mode.
  /// Triggers product loading for the new provider and auto-updates amount.
  void _onProviderChangedInEdit(String itemId, BillProvider provider) {
    // If products are already cached, update the amount now
    final cached = controller.model.providerProducts[provider.id];
    if (cached != null && cached.isNotEmpty) {
      final ctrl = _editedAmounts[itemId];
      if (ctrl != null && cached.first.defaultAmount > 0) {
        ctrl.text = cached.first.defaultAmount.toStringAsFixed(2);
      }
      return;
    }

    // Load products and update amount when they arrive
    controller.loadProviderProducts(provider.id).then((result) {
      if (!mounted) return;
      final products = result.data;
      if (products != null && products.isNotEmpty) {
        final ctrl = _editedAmounts[itemId];
        if (ctrl != null && products.first.defaultAmount > 0) {
          ctrl.text = products.first.defaultAmount.toStringAsFixed(2);
        }
        setState(() {});
      }
    });
  }
}
